Package com.linkedin.databus.client.consumer

Source Code of com.linkedin.databus.client.consumer.TestInternalMetadata

package com.linkedin.databus.client.consumer;

/*
* 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.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.avro.generic.GenericRecord;
import org.apache.avro.util.Utf8;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.Assert;

import com.linkedin.databus.client.DbusEventAvroDecoder;
import com.linkedin.databus.core.DbusEvent;
import com.linkedin.databus.core.DbusEventFactory;
import com.linkedin.databus.core.DbusEventV2Factory;
import com.linkedin.databus.core.DbusEventInfo;
import com.linkedin.databus.core.DbusEventKey;
import com.linkedin.databus.core.DbusEventPart;
import com.linkedin.databus.core.DbusOpcode;
import com.linkedin.databus2.schemas.SchemaId;
import com.linkedin.databus2.schemas.SchemaRegistryService;
import com.linkedin.databus2.schemas.VersionedSchemaSet;
import com.linkedin.databus2.test.TestUtil;

public class TestInternalMetadata
{
  public static final Logger LOG = Logger.getLogger(TestInternalMetadata.class.getName());

  // what /register response would include:
  private static final String CORRECT_METADATA_SCHEMA = "{\"type\":\"record\",\"name\":\"metadata\",\"namespace\":\"com.linkedin.events.espresso\",\"version\":1,\"fields\":[{\"name\":\"etag\",\"type\":\"string\"},{\"name\":\"flags\",\"type\":[\"null\",\"int\"]},{\"name\":\"expires\",\"type\":[\"null\",\"long\"]}]}";
  private static final String INCORRECT_METADATA_SCHEMA = "{\"type\":\"record\",\"name\":\"metadata\",\"namespace\":\"com.linkedin.events.espresso\",\"version\":1,\"fields\":[{\"name\":\"etag\",\"type\":\"long\"},{\"name\":\"flags\",\"type\":[\"null\",\"string\"]},{\"name\":\"expires\",\"type\":[\"null\",\"int\"]}]}";

  // what onDataEvent() would include implicitly (DbusEvent bits):
  private static final long SCN = 5984881823629L;
  private static final long LONG_KEY = 456L;
  private static final long TIMESTAMP_IN_NANOS = 1370483609111000000L;
  private static final short PARTITION_ID = 23;
  private static final short SOURCE_ID = 345;
  private static final short METADATA_SCHEMA_VERSION = 1// matches "version" value within CORRECT_METADATA_SCHEMA
  private static final byte[] METADATA_SCHEMA_CHECKSUM = new byte[] {45,46,47,48}// incorrect (but not checked)
  private static final short PAYLOAD_SCHEMA_VERSION = 9;
  private static final byte[] PAYLOAD_SCHEMA_CHECKSUM = new byte[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
  private static final byte[] PAYLOAD = "Some payload".getBytes(Charset.forName("UTF-8"));

  @BeforeClass
  public void setUpClass()
  {
    Date now = new Date();
    SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd-HH:mm:ss");
    TestUtil.setupLogging(true, "TestInternalMetadata-" + dateFormatter.format(now) + ".log", Level.ERROR);
  }

  // construction of METADATA_BYTES:
  //   cat metadata-schema.avsc | tr '\012' ' ' | sed -e 's/  *//g' -e 's/"/\\"/g' -e 's/$/\n/'
  //   contents of metadata-schema.avsc = println(CORRECT_METADATA_SCHEMA) = subset of wiki:  Espresso+Metadata+Schema
  //   java -jar /path/to/avro-tools-1.7.3.jar jsontofrag "`cat metadata-schema.avsc`" metadata-record.json | hexdump -C
  //   contents of metadata-record.json = {"etag":"dunno what an etag is","flags":null,"expires":{"long":1366150681}}
  private static final byte[] METADATA_BYTES = new byte[]
  {
    0x2a, 0x64, 0x75, 0x6e, 0x6e, 0x6f, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x65,
    0x74, 0x61, 0x67, 0x20, 0x69, 0x73, 0x00, 0x02, (byte)0xb2, (byte)0xb8, (byte)0xee, (byte)0x96, 0x0a
  };

  private static final int maxEventLen = 1000;

  private DbusEventPart createMetadataPart()
  {
    return new DbusEventPart(DbusEvent.SchemaDigestType.CRC32,
                             METADATA_SCHEMA_CHECKSUM,
                             METADATA_SCHEMA_VERSION,
                             ByteBuffer.wrap(METADATA_BYTES));
  }

  private DbusEvent createEvent(DbusEventPart metadataPart)
  throws Exception
  {
    DbusEventInfo eventInfo = new DbusEventInfo(DbusOpcode.UPSERT,
                                                SCN,
                                                PARTITION_ID,
                                                PARTITION_ID,
                                                TIMESTAMP_IN_NANOS,
                                                SOURCE_ID,
                                                PAYLOAD_SCHEMA_CHECKSUM,
                                                PAYLOAD,
                                                false,  // enable tracing
                                                true,  // auto-commit
                                                DbusEventFactory.DBUS_EVENT_V2,
                                                PAYLOAD_SCHEMA_VERSION,
                                                metadataPart);
    DbusEventFactory eventFactory = new DbusEventV2Factory();
    DbusEventKey eventKey = new DbusEventKey(LONG_KEY);
    ByteBuffer serialBuf = ByteBuffer.allocate(maxEventLen).order(eventFactory.getByteOrder());
    DbusEventFactory.serializeEvent(eventKey, serialBuf, eventInfo);

    return eventFactory.createReadOnlyDbusEventFromBuffer(serialBuf, 0);
  }

  private DbusEventAvroDecoder createDecoder(VersionedSchemaSet metadataSchemaSet)
  {
    VersionedSchemaSet sourceSchemaSet = new VersionedSchemaSet()// empty is OK for our purposes
    return new DbusEventAvroDecoder(sourceSchemaSet, metadataSchemaSet);
  }

  /**
   * Verifies that getMetadata() returns the expected GenericRecord for the event's
   * metadata and that it has the expected fields and values in it.
   */
  @Test
  public void testGetMetadata_HappyPath()
  throws Exception
  {
    LOG.info("starting testGetMetadata_HappyPath()");

    // build the event's metadata and then the event
    DbusEventPart metadataPart = createMetadataPart();
    DbusEvent event = createEvent(metadataPart);

    // create a metadata schema set that correctly corresponds to the metadata
    VersionedSchemaSet metadataSchemaSet = new VersionedSchemaSet();
    metadataSchemaSet.add(SchemaRegistryService.DEFAULT_METADATA_SCHEMA_SOURCE,
                          metadataPart.getSchemaVersion(),              // METADATA_SCHEMA_VERSION
                          new SchemaId(metadataPart.getSchemaDigest()), // METADATA_SCHEMA_CHECKSUM
                          CORRECT_METADATA_SCHEMA,
                          true)// preserve original string

    // now create the decoder and use it to extract and decode the event's metadata
    DbusEventAvroDecoder eventDecoder = createDecoder(metadataSchemaSet);
    try
    {
      GenericRecord reuse = null;
      GenericRecord decodedMetadata = eventDecoder.getMetadata(event, reuse);
      Assert.assertNotNull(decodedMetadata, "getMetadata() returned null GenericRecord;");

      Utf8 etag = (Utf8)decodedMetadata.get("etag");
      Assert.assertEquals(etag.toString(), "dunno what an etag is");

      Integer flags = (Integer)decodedMetadata.get("flags");
      Assert.assertEquals(flags, null, "expected flags to be null");

      Long expires = (Long)decodedMetadata.get("expires");
      Assert.assertNotNull(expires, "expected expires to have a value;");
      Assert.assertEquals(expires.longValue(), 1366150681);

      Utf8 nonexistentField = (Utf8)decodedMetadata.get("nonexistentField");
      Assert.assertNull(nonexistentField, "unexpected value for 'nonexistentField';");
    }
    catch (Exception ex)
    {
      Assert.fail("unexpected error decoding metadata: " + ex);
    }

    LOG.info("leaving testGetMetadata_HappyPath()");
  }

  /**
   * Verifies that getMetadata() throws an exception if the metadata schema specified
   * in the event header is unavailable.
   */
  @Test
  public void testGetMetadata_UnhappyPath_MissingSchema()
  throws Exception
  {
    LOG.info("starting testGetMetadata_UnhappyPath_MissingSchema()");

    // build the event's metadata and then the event
    DbusEventPart metadataPart = createMetadataPart();
    DbusEvent event = createEvent(metadataPart);

    // create an empty metadata schema set
    VersionedSchemaSet metadataSchemaSet = new VersionedSchemaSet();

    // now create the decoder and attempt to use it to extract and decode the event's metadata
    DbusEventAvroDecoder eventDecoder = createDecoder(metadataSchemaSet);
    try
    {
      GenericRecord reuse = null;
      GenericRecord decodedMetadata = eventDecoder.getMetadata(event, reuse);
      Assert.fail("getMetadata() should have thrown exception");
    }
    catch (Exception ex)
    {
      // expected case:  event had metadata, but schema to decode it was missing
    }

    LOG.info("leaving testGetMetadata_UnhappyPath_MissingSchema()");
  }

  /**
   * Verifies that getMetadata() returns null if there's a mismatch between the event's metadata
   * and the metadata schema whose signature/checksum is specified in the event header.
   */
  @Test
  public void testGetMetadata_UnhappyPath_BadSchema()
  throws Exception
  {
    LOG.info("starting testGetMetadata_UnhappyPath_BadSchema()");

    // build the event's metadata and then the event
    DbusEventPart metadataPart = createMetadataPart();
    DbusEvent event = createEvent(metadataPart);

    // create a metadata schema set with a schema that claims to match the event's
    // metadata but doesn't actually
    VersionedSchemaSet metadataSchemaSet = new VersionedSchemaSet();
    metadataSchemaSet.add(SchemaRegistryService.DEFAULT_METADATA_SCHEMA_SOURCE,
                          metadataPart.getSchemaVersion(),              // METADATA_SCHEMA_VERSION
                          new SchemaId(metadataPart.getSchemaDigest()), // METADATA_SCHEMA_CHECKSUM
                          INCORRECT_METADATA_SCHEMA,
                          true)// preserve original string

    // now create the decoder and attempt to use it to extract and decode the event's metadata
    DbusEventAvroDecoder eventDecoder = createDecoder(metadataSchemaSet);
    try
    {
      GenericRecord reuse = null;
      GenericRecord decodedMetadata = eventDecoder.getMetadata(event, reuse);
      Assert.assertNull(decodedMetadata, "getMetadata() should have returned null;");
    }
    catch (Exception ex)
    {
      Assert.fail("getMetadata() should not have thrown exception: " + ex);
    }

    LOG.info("leaving testGetMetadata_UnhappyPath_BadSchema()");
  }

  /**
   * Verifies that getMetadata() returns null if event has no metadata.
   */
  @Test
  public void testGetMetadata_UnhappyPath_EventHasNoMetadata()
  throws Exception
  {
    LOG.info("starting testGetMetadata_UnhappyPath_EventHasNoMetadata()");

    // build the event without any metadata
    DbusEvent event = createEvent(null);

    // create a metadata schema set, just because we like to
    VersionedSchemaSet metadataSchemaSet = new VersionedSchemaSet();
    metadataSchemaSet.add(SchemaRegistryService.DEFAULT_METADATA_SCHEMA_SOURCE,
                          METADATA_SCHEMA_VERSION,
                          new SchemaId(METADATA_SCHEMA_CHECKSUM),
                          CORRECT_METADATA_SCHEMA,
                          true)// preserve original string

    // now create the decoder and attempt to use it to extract and decode the event's metadata
    DbusEventAvroDecoder eventDecoder = createDecoder(metadataSchemaSet);
    try
    {
      GenericRecord reuse = null;
      GenericRecord decodedMetadata = eventDecoder.getMetadata(event, reuse);
      Assert.assertNull(decodedMetadata, "getMetadata() should have returned null;");
    }
    catch (Exception ex)
    {
      Assert.fail("getMetadata() should not have thrown exception: " + ex);
    }

    LOG.info("leaving testGetMetadata_UnhappyPath_EventHasNoMetadata()");
  }

}
TOP

Related Classes of com.linkedin.databus.client.consumer.TestInternalMetadata

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.