package com.linkedin.databus.core; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.apache.commons.codec.binary.Hex; import org.apache.log4j.Logger; import com.linkedin.databus.core.util.ByteBufferCRC32; import com.linkedin.databus.core.util.StringUtils; import com.linkedin.databus.core.util.Utils; public class DbusEventV2 extends DbusEventSerializable { /** Serialization Format is : * <pre> * HEADER FIXED PART (43 bytes): * Version (1 byte) // 0 for DbusEventV1 (historical), 2 for DbusEventV2 * Magic (4 bytes) // Must be 0xCAFEDEED for version 2 * Header Length (4 bytes) // Length of fixed and variable parts of the header * HeaderCrc (4 bytes) // CRC of the header portion (fixed + variable), from offset 17 through end of header * BodyCRC (4 bytes) // CRC of the rest of the event * Total Length (4 bytes) // Total length, including header and body * Attributes (2 bytes) // (MSB)12-bit flags, 2 bits opcode, 2-bits key-type (LSB) * NanoTimestamp (8 bytes) // Time (in nanoseconds) at which the event was generated * SourceId (4 bytes) // SourceId for the event * PartitionId (2 bytes) // Partition ID for the event * Sequence (8 bytes) // Sequence number for the event window in which this event was generated * * HEADER VARIABLE PART: * if key type is long * Long Key (8 bytes) * else if key type is string * String key length (4 bytes) * String key (as per length) * else // key type must be schema * DbusEventPart * * METADATA PART: * DbusEventPart * * PAYLOAD PART: * DbusEventPart * </pre> * * A DbusEventPart is serialized as: * <pre> * Length (4 bytes) * Schema Attributes (2 bytes having the schema version and schema digest type) * Schema Digest (4 or 16 bytes depending on schema digest type) * Bytes (as per length) * </pre> * TODO Point to a wiki page */ public static final String MODULE = DbusEventV2.class.getName(); public static final Logger LOG = Logger.getLogger(MODULE); private static final int VersionOffset = 0; private static final int MagicOffset = 1; private static final int HeaderLenOffset = 5; private static final int HeaderCrcOffset = 9; private static final int BodyCrcOffset = 13; private static final int TotalLenOffset = 17; private static final int AttributesOffset = 21; private static final int TimestampOffset = 23; private static final int SourceIdOffset = 31; private static final int PartitionIdOffset = 35; private static final int SequenceOffset = 37; private static final int FixedHeaderLen = 45; private static final int StringKeyLengthOffset = 45; // If the key is of type string, the length of the string is here private static final int StringKeyLengthSize = 4; // size of storage to store the length of the key private static final int LongKeyOffset = 45; private static final int LongKeyLength = 8; // sizedof(long) private static final int SchemaKeyOffset = 45; // If the key is of type schema, it starts at this offset. // Bits in the Attributes (short) field // | 12 bits flags | 2 bits key type | 2 bits op code | // MSB LSB private static final short KEY_TYPE_MASK = 0x000C; // Shifted left by 2 bits private static final short KEY_TYPE_SHIFT = 2; private static final short LONG_KEY_TYPE = 0x01; private static final short STRING_KEY_TYPE = 0x02; private static final short SCHEMA_KEY_TYPE = 0x03; private static final short OPCODE_MASK = 0x0003; private static final int DELETE_OP_CODE = 2; private static final int UPSERT_OP_CODE = 1; private static final int CONTROL_EVENT_OP_CODE = 0; // Shifted positions for the attribute bits private static final short FLAG_IS_REPLICATED = 0x10; private static final short FLAG_TRACE_ON = 0x20; private static final short FLAG_HAS_PAYLOAD_METADATA_PART = 0x40; private static final short FLAG_HAS_PAYLOAD_PART = 0x80; private static final int MAGIC = 0xCAFEDEED; // TODO Find a way to not duplicate variables and methods. // near-empty constructor that doesn't create a useful event public DbusEventV2() { _inited = false; } public DbusEventV2(ByteBuffer buf, int position) { resetInternal(buf, position); } @Override public void setSequence(long sequence) { _buf.putLong(_position + SequenceOffset, sequence); } @Override public void applyCrc() { long headerCrc = ByteBufferCRC32.getChecksum(_buf, _position + BodyCrcOffset, numBytesForHeaderCrc()); Utils.putUnsignedInt(_buf, _position + HeaderCrcOffset, headerCrc); } @Override public void setSize(int sz) { _buf.putInt(_position + TotalLenOffset, sz); } @Override public void setHeaderCrc(long crc) { Utils.putUnsignedInt(_buf, _position + HeaderCrcOffset, crc); } @Override public void setValue(byte[] bytes) { // TODO Remove this after implementing DbusEventPart method to set value? throw new UnsupportedOperationException("Not implemented"); } @Override public void setValueCrc(long crc) { // For V2 events, this translates to setting the CRC for the non-header portion. Utils.putUnsignedInt(_buf, _position + BodyCrcOffset, crc); } @Override public DbusEvent clone(DbusEvent e) { DbusEventV2 reuse = (DbusEventV2)e; if (null == reuse) { reuse = new DbusEventV2(_buf, _position); } else { if (!(e instanceof DbusEventV2)) { throw new UnsupportedClassVersionError("Unsupported class:" + e.getClass().getSimpleName()); } reuse.resetInternal(_buf, _position); } return reuse; } @Override public void setSrcId(int srcId) { _buf.putInt(_position + SourceIdOffset, srcId); } /** * Replace the schema ID for the payload. The caller knows what they are doing, and * will call methods to re-compute CRCs. * @param payloadSchemaId the new schema digest */ @Override public void setSchemaId(byte[] payloadSchemaId) { int payloadPartPosition = payloadPartPosition(); if (payloadPartPosition == -1) { throw new DatabusRuntimeException("No payload in which to set schema ID"); } DbusEventPart.replaceSchemaDigest(_buf, payloadPartPosition, payloadSchemaId); } @Override public void recomputeCrcsAfterEspressoRewrite() { long valueCrc = getCalculatedValueCrc(); setValueCrc(valueCrc); applyCrc(); // Recompute the Body as well as header CRC since both have changed } @Override public DbusEventInternalReadable reset(ByteBuffer buf, int position) { if (buf.get(position) != DbusEventFactory.DBUS_EVENT_V2) { verifyByteOrderConsistency(buf, "DbusEventV2.reset()"); return DbusEventV1Factory.createReadOnlyDbusEventFromBufferUnchecked(buf, position); } resetInternal(buf, position); return this; } @Override public long headerCrc() { return Utils.getUnsignedInt(_buf, _position+HeaderCrcOffset); } // DbusEventV1 returns PARTIAL if the header or event is partial, but it logs an error. // We return PARTIAL but do not log an error. // TODO: should this also check _inited? if _inited == false, presumably we should return ERR @Override public EventScanStatus scanEvent(boolean logErrors) { HeaderScanStatus h = scanHeader(logErrors); if (h != HeaderScanStatus.OK) { // scanHeader should have logged errors. return (h == HeaderScanStatus.ERR? EventScanStatus.ERR : EventScanStatus.PARTIAL); } int bytesInBuffer = _buf.limit() - _position; int eventLengthFromHeader = _buf.getInt(_position + TotalLenOffset); if (bytesInBuffer < eventLengthFromHeader) { return EventScanStatus.PARTIAL; } // We know that we have exactly the number of bytes we expect to have. long calculatedBodyCrc = getCalculatedValueCrc(); long bodyCrc = bodyCrc(); if (calculatedBodyCrc != bodyCrc) { if (logErrors) { LOG.error("bodyCrcInEvent = "+ bodyCrc); LOG.error(",calculatedBodyCrc = "+ calculatedBodyCrc); LOG.error(",crc-ed block size = "+ valueLength()); LOG.error(",event sequence = " + sequence()); LOG.error(",timestamp = " + timestampInNanos()); } return EventScanStatus.ERR; } return EventScanStatus.OK; } @Override public int payloadLength() { return valueLength(); } @Override public long bodyCrc() { return Utils.getUnsignedInt(_buf, _position + BodyCrcOffset); } private int bodyLength() { return _buf.getInt(_position+TotalLenOffset) - headerLength(); } @Override public long getCalculatedValueCrc() { return ByteBufferCRC32.getChecksum(_buf, _position + headerLength(), bodyLength()); } @Override public DbusEventInternalWritable createCopy() { throw new UnsupportedOperationException("Not imeplemented"); } // We really mean the payload schema version here. // TODO We should optimize by creating the DbusEventPart objects during scan event. @Override public short schemaVersion() { DbusEventPart payloadPart = getPayloadPart(); if (payloadPart == null) { return 0; } return payloadPart.getSchemaVersion(); } private int headerLength() { return _buf.getInt(_position + HeaderLenOffset); } @Override protected HeaderScanStatus scanHeader(boolean logErrors) { if (getVersion() != DbusEventFactory.DBUS_EVENT_V2) { if (logErrors) { LOG.error("Incorrect version byte in header:" + getVersion()); } } int bytesInBuffer = _buf.limit() - _position; if (bytesInBuffer < HeaderCrcOffset) { // We can't even get to the header length return HeaderScanStatus.PARTIAL; } if (headerLength() < FixedHeaderLen) { if (logErrors) { LOG.error("Header length too small:" + headerLength()); } } if (bytesInBuffer < headerLength()) { return HeaderScanStatus.PARTIAL; } // We have the complete header. Verify the CRC and return the status. long calculatedHeaderCrc = getCalculatedHeaderCrc(); if (calculatedHeaderCrc != headerCrc()) { if (logErrors) { LOG.error("Header CRC mismatch: Calculated:" + calculatedHeaderCrc + ",found:" + headerCrc()); } return HeaderScanStatus.ERR; } return HeaderScanStatus.OK; } @Override protected boolean isPartial() { throw new UnsupportedOperationException("Not implemented"); } @Override public byte getVersion() { return (_buf.get(_position + VersionOffset)); } @Override public int getMagic() { return (_buf.getInt(_position + MagicOffset)); } @Override public boolean isExtReplicatedEvent() { return isAttributeSet(FLAG_IS_REPLICATED); } private short getKeyTypeAttribute() { return _buf.getShort(_position + AttributesOffset); } @Override public boolean isKeyNumber() { return (getKeyType(getKeyTypeAttribute()) == DbusEventKey.KeyType.LONG); } @Override public boolean isKeyString() { return (getKeyType(getKeyTypeAttribute()) == DbusEventKey.KeyType.STRING); } @Override public boolean isKeySchema() { return (getKeyType(getKeyTypeAttribute()) == DbusEventKey.KeyType.SCHEMA); } @Override public boolean isTraceEnabled() { return isAttributeSet(FLAG_TRACE_ON); } private boolean hasPayloadPart() { return isAttributeSet(FLAG_HAS_PAYLOAD_PART); } private boolean hasMetadata() { return isAttributeSet(FLAG_HAS_PAYLOAD_METADATA_PART); } @Override public DbusOpcode getOpcode() { return getOpCode(_buf.getShort(_position + AttributesOffset)); } @Override public long timestampInNanos() { return _buf.getLong(_position + TimestampOffset); } @Override public int size() { return _buf.getInt(_position + TotalLenOffset); } @Override public long sequence() { return _buf.getLong(_position + SequenceOffset); } @Override public long key() { assert isKeyNumber(); return _buf.getLong(_position + LongKeyOffset); } /** * key length includes 4 bytes of the key length size * @see com.linkedin.databus.core.DbusEvent#keyLength() */ @Override public int keyLength() { throw new UnsupportedOperationException("DbusEvent.keyLength() is deprecated"); } /** * @see com.linkedin.databus.core.DbusEventInternalReadable#keyBytesLength() */ @Override public int keyBytesLength() { assert isKeyString() : "Key type not string = " + getKeyType(getKeyTypeAttribute()); return _buf.getInt(_position + StringKeyLengthOffset); } @Override public byte[] keyBytes() { assert isKeyString() : "Key type = " + getKeyType(getKeyTypeAttribute()); int strKeyLen = keyBytesLength(); byte dst[] = new byte[strKeyLen]; int offsetToCopyFrom = StringKeyLengthOffset + StringKeyLengthSize; // TODO Use slice() and then bulk-get. for (int i = 0; i < strKeyLen; i++) //not using 'bulk' get, because it would adjust _position { dst[i] = _buf.get(_position + offsetToCopyFrom + i); } return dst; } @Override public short srcId() { int srcId = getSourceId(); assert srcId <= Short.MAX_VALUE : "Source ID = " + srcId + " not handled by caller"; return (short)srcId; } @Override public int getSourceId() { return _buf.getInt(_position + SourceIdOffset); } @Override public short physicalPartitionId() { return (_buf.getShort(_position + PartitionIdOffset)); } @Override public short logicalPartitionId() { return (_buf.getShort(_position + PartitionIdOffset)); } @Override public short getPartitionId() { return physicalPartitionId(); } @Override public byte[] schemaId() { DbusEventPart payloadPart = getPayloadPart(); if (payloadPart == null) { return null; } return payloadPart.getSchemaDigest(); // clones the byte array already } @Override public void schemaId(byte[] md5) { DbusEventPart payloadPart = getPayloadPart(); if (payloadPart == null) { return; } // TODO assert that the digest is MD5 type? Optimize to copy once? byte[] srcBytes = payloadPart.getSchemaDigest(); for (int i = 0; i < 16; i++) { md5[i] = srcBytes[i]; } } // Return the position within the buffer where the payload part starts. private int payloadPartPosition() { if (!hasPayloadPart()) { return -1; } // We know that payload has MD5 hash as signature. int payloadPartPosition = _position + headerLength(); if (hasMetadata()) { // Skip metadata to get to the payload payloadPartPosition += DbusEventPart.partLength(_buf, payloadPartPosition); } return payloadPartPosition; } @Override public int valueLength() { // TODO Maybe use DbusEventPart? if (!hasPayloadPart()) { return 0; } return _buf.getInt(payloadPartPosition()); } @Override public ByteBuffer value() { DbusEventPart payloadPart = getPayloadPart(); if (payloadPart == null) { return null; } // this will return a new ByteBuffer object pointing to the // original data, with position pointing to beginning of the // data and limit set to the end. return payloadPart.getData(); } @Override public DbusEventPart getPayloadPart() { int payloadPartPosition = payloadPartPosition(); if (payloadPartPosition == -1) { return null; } return getDbusEventPart(payloadPartPosition); } @Override public DbusEventPart getPayloadMetadataPart() { // TODO Cache the parts as we de-serialize them. Perhaps best done in scanEvent(). if (!hasMetadata()) { return null; } return getDbusEventPart(_position + headerLength()); } @Override public DbusEventPart getKeyPart() { assert isKeySchema() : "Key type = " + getKeyType(getKeyTypeAttribute()); return getDbusEventPart(_position + SchemaKeyOffset); } private DbusEventPart getDbusEventPart(int dbusEventPartStartPos) { ByteBuffer dbusEventPartBB; dbusEventPartBB = _buf.asReadOnlyBuffer().order(_buf.order()); dbusEventPartBB.position(dbusEventPartStartPos); DbusEventPart dbusEventPart = DbusEventPart.decode(dbusEventPartBB); return dbusEventPart; } private DbusEventKey.KeyType getKeyType(short attributes) { switch((attributes & KEY_TYPE_MASK) >> KEY_TYPE_SHIFT) { case LONG_KEY_TYPE: return DbusEventKey.KeyType.LONG; case STRING_KEY_TYPE: return DbusEventKey.KeyType.STRING; case SCHEMA_KEY_TYPE: return DbusEventKey.KeyType.SCHEMA; } throw new DatabusRuntimeException("Unexpected attribute value:" + attributes); } private DbusOpcode getOpCode(short attributes) { switch(attributes & OPCODE_MASK) { case UPSERT_OP_CODE: return DbusOpcode.UPSERT; case DELETE_OP_CODE: return DbusOpcode.DELETE; case CONTROL_EVENT_OP_CODE: return null; } throw new DatabusRuntimeException("Unexpected op code " + (attributes & OPCODE_MASK)); } private int numBytesForHeaderCrc() { // We run the header CRC from the BodyCrcOffset up until the end of the header. return (_buf.getInt(_position + HeaderLenOffset) - BodyCrcOffset); } private boolean isAttributeSet(short attribute) { return ((_buf.getShort(_position + AttributesOffset) & attribute) == attribute); } private long getCalculatedHeaderCrc() { return ByteBufferCRC32.getChecksum(_buf, _position + BodyCrcOffset, numBytesForHeaderCrc()); } private static short setOpCode(DbusOpcode opCode, short attributes, int srcId) { // DbusEventInfo does not support an opcode reserved for control events. if (DbusEventUtils.isControlSrcId(srcId)) { // We know that CONTROL_EVENT_OP_CODE is 0, and findbugs does not like us ORing a 0, // so we return here. // attributes |= CONTROL_EVENT_OP_CODE; return attributes; } if (opCode == null) { // Keeping compatiblity with V1. See DDSDBUS-2282. opCode = DbusOpcode.UPSERT; } switch (opCode) { case UPSERT: attributes |= UPSERT_OP_CODE; break; case DELETE: attributes |= DELETE_OP_CODE; break; default: throw new UnsupportedOperationException("Unimplemented opCode:" + opCode); } return attributes; } private static short setKeyType(DbusEventKey key, short attributes) { switch (key.getKeyType()) { case STRING: attributes |= (STRING_KEY_TYPE << KEY_TYPE_SHIFT); break; case LONG: attributes |= (LONG_KEY_TYPE << KEY_TYPE_SHIFT); break; case SCHEMA: attributes |= (SCHEMA_KEY_TYPE << KEY_TYPE_SHIFT); break; default: throw new UnsupportedOperationException("Unimplemented key type:" + key.getKeyType()); } return attributes; } // Since this is in test code path only, we keep it sub-optimal by copying // string key multiple times. Better way is to get a reference to the key // bytes (sorry, findbugs) or move the encoding logic into DbusEventKey to // avoid a short-term key copy plus memory allocation. public static void setKey(ByteBuffer buf, DbusEventKey key) { switch (key.getKeyType()) { case STRING: byte[] keyBytes = key.getStringKeyInBytes(); buf.putInt(keyBytes.length).put(keyBytes); break; case LONG: buf.putLong(key.getLongKey()); break; case SCHEMA: key.getSchemaKey().encode(buf); break; default: throw new UnsupportedOperationException("Unimplemented key type:" + key.getKeyType()); } } // Having a value length of 0 is a valid case when a row is deleted, or has a schema that produces a 0 length payload. // Also, we use DbusEvent internally to create events to carry Checkpoint, DbusErrorEvent or SCNRegressMessage. // These events have a payload, but no schema version. The schemaId is ignored in these events, but the payload // part must be present. private static boolean shouldEncodePayloadPart(DbusEventInfo eventInfo) { if (eventInfo.getPayloadSchemaVersion() > 0 || eventInfo.getValueLength() > 0) { return true; } return false; } public static int serializeEvent(DbusEventKey key, ByteBuffer buf, DbusEventInfo dbusEventInfo) { // Serialize a DbusEventV2 that has exact same contents as a DbusEventV1. final int start = buf.position(); buf.put(DbusEventFactory.DBUS_EVENT_V2); buf.putInt(MAGIC); buf.putInt(0); // Header len placeholder buf.putInt(0); // Header crc placeholder buf.putInt(0); // Body CRC placeholder buf.putInt(0); // total length placeholder short attributes = 0; attributes = setOpCode(dbusEventInfo.getOpCode(), attributes, dbusEventInfo.getSrcId()); attributes = setKeyType(key, attributes); if (dbusEventInfo.isEnableTracing()) { attributes |= FLAG_TRACE_ON; } if (dbusEventInfo.isReplicated()) { attributes |= FLAG_IS_REPLICATED; } DbusEventPart metadata = dbusEventInfo.getMetadata(); if (shouldEncodePayloadPart(dbusEventInfo)) { attributes |= FLAG_HAS_PAYLOAD_PART; } if (metadata != null) { attributes |= FLAG_HAS_PAYLOAD_METADATA_PART; } buf.putShort(attributes); buf.putLong(dbusEventInfo.getTimeStampInNanos()); buf.putInt(dbusEventInfo.getSrcId()); buf.putShort(dbusEventInfo.getpPartitionId()); buf.putLong(dbusEventInfo.getSequenceId()); // Fixed part of header is done. Now for the variable header part setKey(buf, key); final int hdrEndPos = buf.position(); if (metadata != null) { metadata.encode(buf); } if ((attributes & FLAG_HAS_PAYLOAD_PART) != 0) { ByteBuffer bb = dbusEventInfo.getValueByteBuffer(); if (bb == null) { // Special case to encode when there is no data. bb = ByteBuffer.allocate(1).order(buf.order()); bb.limit(0); } DbusEventPart valuePart = new DbusEventPart(SchemaDigestType.MD5, dbusEventInfo.getSchemaId(), dbusEventInfo.getPayloadSchemaVersion(), bb); valuePart.encode(buf); } final int end = buf.position(); buf.putInt(start+HeaderLenOffset, hdrEndPos-start); buf.putInt(start+TotalLenOffset, end-start); long bodyCrc = ByteBufferCRC32.getChecksum(buf, hdrEndPos, end-hdrEndPos); Utils.putUnsignedInt(buf, start+BodyCrcOffset, bodyCrc); // Header CRC if (dbusEventInfo.isAutocommit()) { // Do the body CRC first, since that is included in the header CRC long hdrCrc = ByteBufferCRC32.getChecksum(buf, start+BodyCrcOffset, hdrEndPos-start-BodyCrcOffset); Utils.putUnsignedInt(buf, start+HeaderCrcOffset, hdrCrc); } return buf.position() - start; } public static int serializeEvent(DbusEventKey key, short pPartitionId, short lPartitionId, long timeStampInNanos, short srcId, byte[] schemaId, byte[] value, boolean enableTracing, ByteBuffer serializationBuffer) throws KeyTypeNotImplementedException { throw new UnsupportedOperationException("Unimplemented as yet"); } /** * Compute how long an event would be, if it were to be serialized. */ public static int computeEventLength(DbusEventKey key, DbusEventInfo eventInfo) throws KeyTypeNotImplementedException { int evtLen = LongKeyOffset; switch (key.getKeyType()) { case LONG: evtLen += LongKeyLength; break; case STRING: evtLen += key.getStringKeyInBytes().length + StringKeyLengthSize; break; case SCHEMA: evtLen += key.getSchemaKey().computePartLength(); break; default: String msg = "Unknown key type:" + key.getKeyType(); LOG.error(msg); throw new KeyTypeNotImplementedException(msg); } DbusEventPart metadata = eventInfo.getMetadata(); if (metadata != null) { evtLen += metadata.computePartLength(); } // In case of Checksum event, the payload is non-null, but there is no schema. // We need to encode a payload part if either the payload has some data, or // if schema version is non-zero. // All payload schema digests are MD5 until we choose to support CRC digest in // payload. if (shouldEncodePayloadPart(eventInfo)) { evtLen += DbusEventPart.computePartLength(SchemaDigestType.MD5, eventInfo.getValueLength()); } return evtLen; } public DbusEventInternalWritable convertToV1() throws KeyTypeNotImplementedException { DbusEventKey key; DbusEventFactory eventV1Factory = new DbusEventV1Factory(); // to create new event we need to get data from ByteBuffer of the current event ByteBuffer curValue = value(); // create the key if(isKeyNumber()) { key = new DbusEventKey(key()); } else if(isKeyString()) { key = new DbusEventKey(keyBytes()); } else { String msg = "Conversion not supported for this key type:" + getKeyType(getKeyTypeAttribute()); LOG.error(msg); throw new KeyTypeNotImplementedException(msg); } // validate schmeaId - for v1 it should be array of bytes with 0s byte [] schemaId = schemaId(); if(schemaId == null) { schemaId = DbusEventInternalWritable.emptyMd5; } boolean autocommit = true; // will generate CRC for the event - should always be true DbusEventInfo dbusEventInfo = new DbusEventInfo(getOpcode(), sequence(), getPartitionId(), getPartitionId(), timestampInNanos(), (short)getSourceId(), schemaId, null, isTraceEnabled(), autocommit); if(curValue != null) { dbusEventInfo.setValueByteBuffer(curValue); // to make it more efficient we should copy directly from the buffer } // allocate the buffer int newEventSize = eventV1Factory.computeEventLength(key, dbusEventInfo); ByteBuffer serializationBuffer = ByteBuffer.allocate(newEventSize); serializationBuffer.order(_buf.order()); int size = DbusEventV1.serializeEvent(key, serializationBuffer, dbusEventInfo); if(size != newEventSize) throw new DatabusRuntimeException("event size doesn't match after conversion from V2 to V1"); serializationBuffer.limit(size); // set the limit to the end of the event // construct the event from the buffer at the position return new DbusEventV1(serializationBuffer, 0); } public static ByteBuffer serializeEndOfPeriodMarker(long seq, short partition, ByteOrder byteOrder) { DbusEventKey key = DbusEventInternalWritable.EOPMarkerKey; DbusEventInfo eventInfo = new DbusEventInfo(null, seq, partition, partition, System.nanoTime(), EOPMarkerSrcId, DbusEventInternalWritable.emptyMd5, new byte[0], false, //enable tracing true, // autocommit DbusEventFactory.DBUS_EVENT_V2, (short)0, // payload schema version null // Metadata ); try { int evtLen = computeEventLength(key, eventInfo); ByteBuffer buf = ByteBuffer.allocate(evtLen).order(byteOrder); serializeEvent(key, buf, eventInfo); return buf; } catch (KeyTypeNotImplementedException e) { throw new DatabusRuntimeException("Unexpected exception on key :" + key, e); } } @Override public String toString() { if (_buf == null) { return "buf=null"; } boolean valid = true; try { valid = isValid(true); } catch (Exception ex) { LOG.error("DbusEventV2.toString() : Got Exception while trying to validate the event ", ex); valid = false; } if (!valid ) { StringBuilder sb = new StringBuilder("Position: "); sb.append(_position); sb.append(", buf: "); sb.append(_buf != null ? _buf.toString(): "null"); sb.append(", validity: false; hexDump:"); if (_buf != null && _position >= 0) { sb.append(StringUtils.hexdumpByteBufferContents(_buf, _position, 100)); } return sb.toString(); } StringBuilder sb = new StringBuilder(200); sb.append("Version=") .append(getVersion()) .append(";Position=") .append(_position) .append(";isEndOfPeriodMarker=") .append(isEndOfPeriodMarker()) .append(";isExtReplicated=") .append(isExtReplicatedEvent()) .append(";HeaderCrc=") .append("0x") .append(Integer.toHexString((int)headerCrc())) .append(";Length=") .append(size()) .append(";KeyType=") .append(getKeyType(getKeyTypeAttribute())) .append(";Key="); if (isKeyString()) { sb.append("0x") .append(Hex.encodeHexString(keyBytes())); } else if (isKeyNumber()) { sb.append(key()); } else if (isKeySchema()) { sb.append(getKeyPart().toString()); } sb.append(";Sequence=") .append(sequence()) .append(";PartitionId=") .append(getPartitionId()) .append(";Timestamp=") .append(timestampInNanos()) .append(";SrcId=") .append(srcId()) .append(";SchemaId=") .append(schemaId() == null ? "null" : "0x" + Hex.encodeHexString(schemaId())) .append(";ValueCrc=") .append("0x") .append(Integer.toHexString((int)bodyCrc())) .append(";HasMetadata=") .append(hasMetadata()) .append(";hasPayloadPart=") .append(hasPayloadPart()) .append(";PayloadLen=") .append(payloadLength()); if (hasMetadata()) { sb.append(";Metadata={") .append(getPayloadMetadataPart().toString()) .append("}"); } // Do not print data here, it could be big. return sb.toString(); } }