Package com.linkedin.databus.container.netty

Source Code of com.linkedin.databus.container.netty.TestRelayCommandsLocal

package com.linkedin.databus.container.netty;
/*
*
* 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 static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.apache.avro.Schema;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.local.DefaultLocalServerChannelFactory;
import org.jboss.netty.channel.local.LocalAddress;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import com.linkedin.databus.container.request.GenerateDataEventsRequestProcessor;
import com.linkedin.databus.core.Checkpoint;
import com.linkedin.databus.core.DbusEventBuffer;
import com.linkedin.databus.core.DbusEventBufferMult;
import com.linkedin.databus.core.test.netty.SimpleHttpResponseHandler;
import com.linkedin.databus.core.test.netty.SimpleTestHttpClient;
import com.linkedin.databus.core.test.netty.SimpleTestHttpClient.TimeoutPolicy;
import com.linkedin.databus.core.util.ConfigLoader;
import com.linkedin.databus.core.util.DatabusEventProducer;
import com.linkedin.databus.core.util.DatabusEventRandomProducer;
import com.linkedin.databus.core.util.IdNamePair;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus2.core.DatabusException;
import com.linkedin.databus2.core.container.DatabusHttpHeaders;
import com.linkedin.databus2.core.container.netty.HttpServerPipelineFactory;
import com.linkedin.databus2.core.container.request.EchoRequestProcessor;
import com.linkedin.databus2.core.container.request.InvalidRequestParamValueException;
import com.linkedin.databus2.core.container.request.RegisterResponseEntry;
import com.linkedin.databus2.core.container.request.RequestProcessorRegistry;
import com.linkedin.databus2.schemas.SchemaRegistryStaticConfig.RegistryType;
import com.linkedin.databus2.schemas.VersionedSchema;
import com.linkedin.databus2.schemas.VersionedSchemaSetBackedRegistryService;

@Test(singleThreaded=true)
public class TestRelayCommandsLocal
{

  static
  {
    BasicConfigurator.configure();
    Logger.getRootLogger().setLevel(Level.OFF);
    //Logger.getRootLogger().setLevel(Level.ERROR);
    //Logger.getRootLogger().setLevel(Level.DEBUG);
  }

  public static final String MODULE = TestRelayCommandsLocal.class.getName();
  public static final Logger LOG = Logger.getLogger(MODULE);

  private HttpRelay _relay;
  private final HttpRelay.Config _staticConfigBuilder;
  private final HttpRelay.StaticConfig _staticConfig;
  private final VersionedSchemaSetBackedRegistryService _schemaRegistry;
  private DbusEventBufferMult _eventBuffer;
  private LocalAddress _serverAddress;
  private Channel _serverChannel;
  private ServerBootstrap _bootstrap;
  private final ExecutorService _executor = Executors.newCachedThreadPool();

  public TestRelayCommandsLocal() throws IOException, InvalidConfigException, DatabusException
  {
    _staticConfigBuilder = new HttpRelay.Config();

    //ConfigLoader<HttpRelay.StaticConfig> configLoader =
        new ConfigLoader<HttpRelay.StaticConfig>("databus.relay.", _staticConfigBuilder);

    //configLoader.loadConfig(System.getProperties());
    _staticConfigBuilder.getContainer().setExistingMbeanServer(null);

    _staticConfigBuilder.getSchemaRegistry().getFileSystem().setRefreshPeriodMs(0);

    DbusEventBuffer.Config eventBufferConfig = _staticConfigBuilder.getEventBuffer();
    eventBufferConfig.setAllocationPolicy("HEAP_MEMORY");
    eventBufferConfig.setMaxSize(15000);
    eventBufferConfig.setAverageEventSize(10000);
    eventBufferConfig.setScnIndexSize(10000);

    _staticConfigBuilder.setSourceName("100", "commonsource1");
    _staticConfigBuilder.setSourceName("101", "commonsource1");
    _staticConfigBuilder.setSourceName("1001", "src11");
    _staticConfigBuilder.setSourceName("2001", "src21");
    _staticConfigBuilder.setSourceName("2002", "src22");
    _staticConfigBuilder.setSourceName("3001", "test3.source1");
    _staticConfigBuilder.setSourceName("3002", "test3.source2");
    _staticConfigBuilder.setSourceName("4001", "test4.source1");
    _staticConfigBuilder.setSourceName("4002", "test4.source2");
    _staticConfigBuilder.setSourceName("5001", "test5.source1");
    _staticConfigBuilder.setSourceName("5002", "test5.source2");

    _schemaRegistry = new VersionedSchemaSetBackedRegistryService();

    String schema31Str = "{\"name\":\"source1_v1\",\"namespace\":\"test3\",\"type\":\"record\",\"fields\":[{\"type\":\"int\",\"name\":\"intField\"}]}";
    VersionedSchema vschema31 = new VersionedSchema("test3.source1", (short)1, Schema.parse(schema31Str), null);
    String schema32Str = "{\"name\":\"source2_v1\",\"namespace\":\"test3\",\"type\":\"record\",\"fields\":[{\"type\":\"string\",\"name\":\"strField\"}]}";
    VersionedSchema vschema32 = new VersionedSchema("test3.source2", (short)1, Schema.parse(schema32Str), null);
    _schemaRegistry.registerSchema(vschema31);
    _schemaRegistry.registerSchema(vschema32);

    String schema41Str = "{\"name\":\"source1_v1\",\"namespace\":\"test4\",\"type\":\"record\",\"fields\":[{\"type\":\"int\",\"name\":\"intField\"}]}";
    VersionedSchema vschema41 = new VersionedSchema("test4.source1", (short)1, Schema.parse(schema41Str), null);
    String schema42Str = "{\"name\":\"source2_v1\",\"namespace\":\"test4\",\"type\":\"record\",\"fields\":[{\"type\":\"string\",\"name\":\"strField\"}]}";
    VersionedSchema vschema42 = new VersionedSchema("test4.source2", (short)1, Schema.parse(schema42Str), null);
    _schemaRegistry.registerSchema(vschema41);
    _schemaRegistry.registerSchema(vschema42);

    String schema51Str = "{\"name\":\"source1_v1\",\"namespace\":\"test5\",\"type\":\"record\",\"fields\":[{\"type\":\"int\",\"name\":\"intField\"}]}";
    VersionedSchema vschema51 = new VersionedSchema("test5.source1", (short)1, Schema.parse(schema51Str), null);
    //String schema52Str = "{\"name\":\"source2_v1\",\"namespace\":\"test5\",\"type\":\"record\",\"fields\":[{\"type\":\"string\",\"name\":\"strField\"}]}";
    VersionedSchema vschema52 = new VersionedSchema("test5.source2", (short)1, Schema.parse(schema51Str), null);
    _schemaRegistry.registerSchema(vschema51);
    _schemaRegistry.registerSchema(vschema52);

    // See https://iwww.corp.linkedin.com/wiki/cf/display/ENGS/Espresso+Metadata+Schema for
    // the real definition.
    String metadataSchemaStr = "{\"name\":\"metadata\",\"namespace\":\"test_namespace\",\"type\":\"record\",\"fields\":[{\"name\":\"randomStrField\",\"type\":\"string\"}]}";
    // TODO (DDSDBUS-2093/2096):  update this according to real schema-registry metadata API.
    // (Right now the "metadata" string here is used as a key for lookups within the relay's
    // schema registry, but that could conflict with an actual source schema named "metadata".)
    VersionedSchema metaSchema = new VersionedSchema("metadata", (short)1, Schema.parse(metadataSchemaStr), null);
    _schemaRegistry.registerSchema(metaSchema);

    _staticConfigBuilder.getSchemaRegistry().setType(RegistryType.EXISTING.toString());
    _staticConfigBuilder.getSchemaRegistry().useExistingService(_schemaRegistry);
    _staticConfig = _staticConfigBuilder.build();
  }

  @BeforeMethod
  public void setUp() throws Exception
  {
    _relay = new HttpRelay(_staticConfig, null)// creates an event factory for us

    _eventBuffer = _relay.getEventBuffer();

    DatabusEventProducer randomEventProducer = new DatabusEventRandomProducer(
        _eventBuffer, 10, 1, 10, _staticConfig.getSourceIds(),null,null);

    RequestProcessorRegistry processorRegistry = _relay.getProcessorRegistry();
    processorRegistry.register(EchoRequestProcessor.COMMAND_NAME, new EchoRequestProcessor(null));
    processorRegistry.register(GenerateDataEventsRequestProcessor.COMMAND_NAME,
                               new GenerateDataEventsRequestProcessor(null, _relay,
                                                                      randomEventProducer));

    // Configure the server.
    _bootstrap = new ServerBootstrap(new DefaultLocalServerChannelFactory());
    // Set up the event pipeline factory.
    _bootstrap.setPipelineFactory(new HttpServerPipelineFactory(_relay));
    _serverAddress = new LocalAddress(10);
    _serverChannel = _bootstrap.bind(_serverAddress);
  }

  @AfterMethod
  public void tearDown()
  {
    _relay.doShutdown();

    if (null != _staticConfig)
    {
      _relay.getDefaultExecutorService().shutdownNow();
    }
    if (null != _bootstrap)
    {
      _serverChannel.close();
      _bootstrap.releaseExternalResources();
    }
  }

  @Test
  public void testSources1Command() throws Exception
  {
    LOG.debug("\n\nstarting testSources1Command()\n");


    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                                                     HttpMethod.GET, "/sources");

    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    ByteArrayInputStream in = new ByteArrayInputStream(respHandler.getReceivedBytes());
    ObjectMapper objMapper = new ObjectMapper();
    List<IdNamePair> res = objMapper.readValue(in, new TypeReference<List<IdNamePair>>(){});
    assertNotNull("no result", res);
    if (LOG.isDebugEnabled())
    {
      LOG.debug("/sources response:" + new String(respHandler.getReceivedBytes()));
    }

    HashSet<IdNamePair> origSet = new HashSet<IdNamePair>(_staticConfig.getSourceIds());
    HashSet<IdNamePair> resSet = new HashSet<IdNamePair>(res);
    Assert.assertEquals(origSet, resSet);
  }

  @Test
  public void testSourcesTrackingCommand() throws Exception
  {
    LOG.debug("\n\nstarting testSourcesTrackingCommand()\n");


    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                                                     HttpMethod.GET, "/sources");
    httpRequest.setHeader(DatabusHttpHeaders.DBUS_CLIENT_HOST_HDR, "localhost");
    httpRequest.setHeader(DatabusHttpHeaders.DBUS_CLIENT_SERVICE_HDR, "unittestclient");
    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response",
               respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    ByteArrayInputStream in = new ByteArrayInputStream(respHandler.getReceivedBytes());
    ObjectMapper objMapper = new ObjectMapper();
    List<IdNamePair> res = objMapper.readValue(in, new TypeReference<List<IdNamePair>>(){});
    assertNotNull("no result", res);
    if (LOG.isDebugEnabled())
    {
      LOG.debug("/sources response:" + new String(respHandler.getReceivedBytes()));
    }

    HashSet<IdNamePair> origSet = new HashSet<IdNamePair>(_staticConfig.getSourceIds());
    HashSet<IdNamePair> resSet = new HashSet<IdNamePair>(res);
    Assert.assertEquals(origSet, resSet);
  }

  private void prepareTestSources2Command()
  {
  }

  private void doTestSources2Command() throws Exception
  {
    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                                                     HttpMethod.GET, "/sources");

    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    ByteArrayInputStream in = new ByteArrayInputStream(respHandler.getReceivedBytes());
    ObjectMapper objMapper = new ObjectMapper();
    List<IdNamePair> res = objMapper.readValue(in, new TypeReference<List<IdNamePair>>(){});
    assertNotNull("no result", res);
    if (LOG.isDebugEnabled())
    {
      LOG.debug("/sources response:" + new String(respHandler.getReceivedBytes()));
    }

    HashSet<IdNamePair> origSet = new HashSet<IdNamePair>(_staticConfig.getSourceIds());
    HashSet<IdNamePair> resSet = new HashSet<IdNamePair>(res);
    Assert.assertEquals(origSet, resSet);
  }

  @Test
  public void testSources2Command() throws Exception
  {
    LOG.debug("\n\nstarting testSources2Command()\n");

    prepareTestSources2Command();
    doTestSources2Command();
  }

  @Test
  public void testSources1CommandThreaded() throws Exception
  {
    LOG.debug("\n\nstarting testSources1CommandThreaded()\n");

    Future<?> future = _executor.submit(new Runnable()
    {
      @Override
      public void run()
      {
        boolean ok = true;
        try
        {
          testSources1Command();
        }
        catch (Exception e)
        {
          ok = false;
        }
        assertTrue("unexpected /sources exception", ok);
      }
    });

    try
    {
    future.get(2, TimeUnit.SECONDS);
    }
    catch (InterruptedException ie) {}
    assertTrue("/sources failed to complete", future.isDone());
  }

  @Test
  public void testSources2CommandThreaded() throws Exception
  {
    LOG.debug("\n\nstarting testSources2CommandThreaded()\n");

    prepareTestSources2Command();

    Runnable runTestSources2Command = new Runnable()
        {
          @Override
          public void run()
          {
            LOG.info("Start testSource2Command thread");
            boolean ok = true;
            try
            {
              doTestSources2Command();
            }
            catch (Exception e)
            {
              ok = false;
            }
            assertTrue("unexpected /sources exception", ok);
            LOG.info("Done testSource2Command thread");
          }
        };

    Future<?> future1 = _executor.submit(runTestSources2Command);
    Future<?> future2 = _executor.submit(runTestSources2Command);
    Future<?> future3 = _executor.submit(runTestSources2Command);

    try
    {
      future1.get(2, TimeUnit.SECONDS);
      future2.get(2, TimeUnit.SECONDS);
      future3.get(2, TimeUnit.SECONDS);
    }
    catch (InterruptedException ie) {}
    assertTrue("expected /sources call 1 to complete", future1.isDone());
    assertTrue("expected /sources call 2 to complete", future1.isDone());
    assertTrue("expected /sources call 3 to complete", future1.isDone());
  }

  @Test
  public void testRegisterCommandOneSource() throws Exception
  {
    LOG.debug("\n\nstarting testRegisterCommandOneSource()\n");

    // /register?sources=2
    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                                                     HttpMethod.GET, "/register?sources=3002");

    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    byte[] respBytes = respHandler.getReceivedBytes();
    if (LOG.isDebugEnabled())
    {
      LOG.debug("/register response:" + new String(respBytes));
    }
    ByteArrayInputStream in = new ByteArrayInputStream(respBytes);
    ObjectMapper objMapper = new ObjectMapper();
    List<RegisterResponseEntry> res =
        objMapper.readValue(in, new TypeReference<List<RegisterResponseEntry>>(){});
    assertNotNull("no result", res);
    assertEquals("expected one source", 1, res.size());
    assertEquals("expected correct source id", 3002, res.get(0).getId());
    Schema resSchema = Schema.parse(res.get(0).getSchema());
    assertEquals("expected correct source schema", "test3.source2_v1", resSchema.getFullName());
  }

  private void prepareTestRegisterCommandTwoSources() throws Exception
  {
  }

  private void doTestRegisterCommandTwoSources() throws Exception
  {
    ObjectMapper objMapper = new ObjectMapper();

    // /register?sources=1,2
    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                                         HttpMethod.GET, "/register?sources=4001,4002");
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    ByteArrayInputStream in = new ByteArrayInputStream(respHandler.getReceivedBytes());
    List<RegisterResponseEntry> res =
        objMapper.readValue(in, new TypeReference<List<RegisterResponseEntry>>(){});
    assertNotNull("no result", res);
    if (LOG.isDebugEnabled())
    {
      LOG.debug("/register response:" + new String(respHandler.getReceivedBytes()));
    }
    assertEquals("expected two sources", 2, res.size());
    assertEquals("expected correct source id", 4001, res.get(0).getId());
    Schema resSchema = Schema.parse(res.get(0).getSchema());
    assertEquals("expected correct source schema", "test4.source1_v1", resSchema.getFullName());
    assertEquals("expected correct source id", 4002, res.get(1).getId());
    resSchema = Schema.parse(res.get(1).getSchema());
    assertEquals("expected correct source schema", "test4.source2_v1", resSchema.getFullName());
  }


  @Test
  public void testRegisterCommandTwoSources() throws Exception
  {
    LOG.debug("\n\nstarting testRegisterCommandTwoSources()\n");

    prepareTestRegisterCommandTwoSources();
    doTestRegisterCommandTwoSources();
  }

  @Test
  public void testRegisterCommandThreeSources() throws Exception
  {
    LOG.debug("\n\nstarting testRegisterCommandThreeSources()\n");

    // /register?sources=1,2,3
    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                                         HttpMethod.GET, "/register?sources=5001,5002,5003");
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);
    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));

    if (LOG.isDebugEnabled())
    {
      LOG.debug("/register response:" + new String(respHandler.getReceivedBytes()));
    }

    HttpResponse respObj = respHandler.getResponse();
    // Note that v3 client code doesn't currently (May 2013) support "sources" param for "/register".
    assertNotNull("exception class header not present",
                  respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    assertEquals("exception class name mismatch",
                 InvalidRequestParamValueException.class.getName(),
                 respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
  }

  @Test
  public void testRegisterCommandOneSourceThreaded() throws Exception
  {
    LOG.debug("\n\nstarting testRegisterCommandOneSourceThreaded()\n");

    Future<?> future = _executor.submit(new Runnable()
    {
      @Override
      public void run()
      {
        boolean ok = true;
        try
        {
          testRegisterCommandOneSource();
        }
        catch (Exception e)
        {
          ok = false;
        }
        assertTrue("unexpected /register exception", ok);
      }
    });

    try
    {
    future.get(2, TimeUnit.SECONDS);
    }
    catch (InterruptedException ie) {}
    assertTrue("/register failed to complete", future.isDone());
  }

  @Test
  public void testRegisterCommandTwoSourcesThreaded() throws Exception
  {
    LOG.debug("\n\nstarting testRegisterCommandTwoSourcesThreaded()\n");

    Runnable runRegisterCommandTwoSources = new Runnable()
        {
          @Override
          public void run()
          {
            boolean ok = true;
            try
            {
              doTestRegisterCommandTwoSources();
            }
            catch (Exception e)
            {
              ok = false;
            }
            assertTrue("unexpected /register exception", ok);
          }
        };

    Future<?>[] future = new Future<?>[4];

    for (int i = 0; i < 4; ++i) future[i] = _executor.submit(runRegisterCommandTwoSources);

    try
    {
      for (int i = 0; i < 4; ++i) future[i].get(2, TimeUnit.SECONDS);
    }
    catch (InterruptedException ie)
    {}
    for (int i = 0; i < 4; ++i)
    {
      assertTrue("/register thread " + (i+1) + " failed to complete", future[i].isDone());
    }
  }

  @Test
  public void testRegisterV4CommandHappyPath() throws Exception
  {
    LOG.debug("\n\nstarting testRegisterV4CommandHappyPath()\n");

    // /register?protocolVersion=4
    HttpRequest httpRequest =
        new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                               HttpMethod.GET,
                               "/register?sources=4002&" + DatabusHttpHeaders.PROTOCOL_VERSION_PARAM + "=4");

    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    HttpResponse respObj = respHandler.getResponse();
    assertNull("/register v4 returned unexpected error: " +
               respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER),
               respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    String registerResponseProtocolVersionStr =
        respObj.getHeader(DatabusHttpHeaders.DBUS_CLIENT_RELAY_PROTOCOL_VERSION_HDR);
    assertNotNull("/register protocol-version response header not present", registerResponseProtocolVersionStr);
    assertEquals("client-relay protocol response version mismatch", "4", registerResponseProtocolVersionStr);

    byte[] respBytes = respHandler.getReceivedBytes();
    if (LOG.isDebugEnabled())
    {
      LOG.debug("/register response: " + new String(respBytes));
    }
    ByteArrayInputStream in = new ByteArrayInputStream(respBytes);
    ObjectMapper objMapper = new ObjectMapper();
    Map<String, List<Object>> resMap =
        objMapper.readValue(in, new TypeReference<Map<String, List<Object>>>(){});
    assertNotNull("no result", resMap);

    List<Object> sourceSchemasObjectsList = resMap.get(RegisterResponseEntry.SOURCE_SCHEMAS_KEY);
    assertNotNull("missing required sourceSchemas key in response", sourceSchemasObjectsList);
    assertEquals("expected one source schema", 1, sourceSchemasObjectsList.size());

    // ObjectMapper encodes plain Object as LinkedHashMap with (apparently) String keys and
    // either String or Integer values (currently).  We must construct RegisterResponseEntry
    // manually.
    assertTrue("sourceSchemas deserialization error: 'Object' type = " +
               sourceSchemasObjectsList.get(0).getClass().getName() + ", not LinkedHashMap",
               sourceSchemasObjectsList.get(0) instanceof LinkedHashMap);
    @SuppressWarnings("unchecked") // just obj
    LinkedHashMap<String, Object> obj = (LinkedHashMap<String, Object>)sourceSchemasObjectsList.get(0);
    assertTrue("sourceSchemas deserialization error: missing \"id\" key", obj.containsKey("id"));
    assertTrue("sourceSchemas deserialization error: missing \"version\" key", obj.containsKey("version"));
    assertTrue("sourceSchemas deserialization error: missing \"schema\" key", obj.containsKey("schema"));

    assertTrue("obj.get(\"id\") type = " + obj.get("id").getClass().getName() + ", not Integer",
               obj.get("id") instanceof Integer);
    assertTrue("obj.get(\"version\") type = " + obj.get("version").getClass().getName() + ", not Integer",
               obj.get("version") instanceof Integer);
    RegisterResponseEntry rre = new RegisterResponseEntry((Integer)obj.get("id"),
                                                          ((Integer)obj.get("version")).shortValue(),
                                                          (String)obj.get("schema"));
    assertEquals("unexpected source id", 4002, rre.getId());
    Schema resSchema = Schema.parse(rre.getSchema());
    assertEquals("unexpected source-schema name for source id 4002", "test4.source2_v1", resSchema.getFullName());

    // There's no guarantee of a metadataSchemas entry in general, but we pre-stuffed our
    // VersionedSchemaSetBackedRegistryService with one in the test's constructor, so we
    // expect the relay to hand it back to us.  Or else.
/* disabled for now since simplistic relay implementation has been disabled; reenable/update/modify as part of DDSDBUS-2093/2096 (TODO)
    List<Object> metadataSchemasObjectsList = resMap.get(RegisterResponseMetadataEntry.METADATA_SCHEMAS_KEY);
    assertNotNull("missing expected metadataSchemas key in response", metadataSchemasObjectsList);
    assertEquals("expected one metadata schema", 1, metadataSchemasObjectsList.size());

    // As above, we must construct RegisterResponseMetadataEntry manually.
    assertTrue("metadataSchemas deserialization error: 'Object' type = " +
               metadataSchemasObjectsList.get(0).getClass().getName() + ", not LinkedHashMap",
               metadataSchemasObjectsList.get(0) instanceof LinkedHashMap);
    @SuppressWarnings("unchecked") // just obj2
    LinkedHashMap<String, Object> obj2 = (LinkedHashMap<String, Object>)metadataSchemasObjectsList.get(0);
    assertTrue("metadataSchemas deserialization error: missing \"version\" key", obj2.containsKey("version"));
    assertTrue("metadataSchemas deserialization error: missing \"schema\" key", obj2.containsKey("schema"));

    assertTrue("obj2.get(\"version\") type = " + obj2.get("version").getClass().getName() + ", not Integer",
               obj2.get("version") instanceof Integer);
    RegisterResponseMetadataEntry rrme = new RegisterResponseMetadataEntry(((Integer)obj2.get("version")).shortValue(),
                                                                          (String)obj2.get("schema"));
    assertEquals("unexpected metadata version", 1, rrme.getVersion());
    resSchema = Schema.parse(rrme.getSchema());
    assertEquals("unexpected metadata schema name", "test_namespace.metadata", resSchema.getFullName());
*/

    LOG.debug("\n\ndone with testRegisterV4CommandHappyPath()\n");
  }

  @Test
  public void testRegisterV4CommandLessThanHappyPaths() throws Exception
  {
    LOG.debug("\n\nstarting testRegisterV4CommandLessThanHappyPaths()\n");

    // protocolVersion < 2
    HttpRequest httpRequest =
        new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                               HttpMethod.GET,
                               "/register?sources=4002&" + DatabusHttpHeaders.PROTOCOL_VERSION_PARAM + "=1");
    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);
    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    HttpResponse respObj = respHandler.getResponse();
    assertNotNull("/register failed to return expected error",
                  respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    LOG.debug("DATABUS_ERROR_CLASS_HEADER = " + respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));

    // protocolVersion > 4
    httpRequest =
        new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                               HttpMethod.GET,
                               "/register?sources=4002&" + DatabusHttpHeaders.PROTOCOL_VERSION_PARAM + "=5");
    respHandler = httpClient.sendRequest(_serverAddress, httpRequest);
    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    respObj = respHandler.getResponse();
    assertNotNull("/register failed to return expected error",
                  respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    LOG.debug("DATABUS_ERROR_CLASS_HEADER = " + respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));

    // protocolVersion == 2:  this is a happy path, but explicitly specifying the version is
    // unusual in this case (default = version 2 or 3, which are identical for /register), so
    // check for expected response
    httpRequest =
        new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                               HttpMethod.GET,
                               "/register?sources=4002&" + DatabusHttpHeaders.PROTOCOL_VERSION_PARAM + "=2");
    respHandler = httpClient.sendRequest(_serverAddress, httpRequest);
    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    respObj = respHandler.getResponse();
    assertNull("/register v2 returned unexpected error: " +
               respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER),
               respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    LOG.debug("DATABUS_ERROR_CLASS_HEADER = " + respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    String registerResponseProtocolVersionStr =
        respObj.getHeader(DatabusHttpHeaders.DBUS_CLIENT_RELAY_PROTOCOL_VERSION_HDR);
    assertNotNull("/register protocol-version response header not present", registerResponseProtocolVersionStr);
    assertEquals("client-relay protocol response version mismatch", "2", registerResponseProtocolVersionStr);
    byte[] respBytes = respHandler.getReceivedBytes();
    if (LOG.isDebugEnabled())
    {
      LOG.debug("/register response: " + new String(respBytes));
    }
    ByteArrayInputStream in = new ByteArrayInputStream(respBytes);
    ObjectMapper objMapper = new ObjectMapper();
    List<RegisterResponseEntry> sourceSchemasList = null;
    try
    {
      sourceSchemasList = objMapper.readValue(in, new TypeReference<List<RegisterResponseEntry>>(){});
    }
    catch (JsonMappingException jmex)
    {
      Assert.fail("ObjectMapper failed unexpectedly");
    }
    assertNotNull("missing source schemas in response", sourceSchemasList);
    assertEquals("expected one source schema", 1, sourceSchemasList.size());
    RegisterResponseEntry rre = sourceSchemasList.get(0);
    assertEquals("unexpected source id", 4002, rre.getId());
    Schema resSchema = Schema.parse(rre.getSchema());
    assertEquals("unexpected source-schema name for source id 4002", "test4.source2_v1", resSchema.getFullName());

    // protocolVersion == 3:  as with v2 above; just do a quick sanity check
    httpRequest =
        new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                               HttpMethod.GET,
                               "/register?sources=4002&" + DatabusHttpHeaders.PROTOCOL_VERSION_PARAM + "=3");
    respHandler = httpClient.sendRequest(_serverAddress, httpRequest);
    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    respObj = respHandler.getResponse();
    assertNull("/register v3 returned unexpected error: " +
               respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER),
               respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    LOG.debug("DATABUS_ERROR_CLASS_HEADER = " + respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    registerResponseProtocolVersionStr = respObj.getHeader(DatabusHttpHeaders.DBUS_CLIENT_RELAY_PROTOCOL_VERSION_HDR);
    assertNotNull("/register protocol-version response header not present", registerResponseProtocolVersionStr);
    assertEquals("client-relay protocol response version mismatch", "3", registerResponseProtocolVersionStr);
  }

  @Test
  public void testNoDataStreamCommand() throws Exception
  {
    LOG.debug("\n\nstarting testNoDataStreamCommand()\n");

    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                                                     HttpMethod.GET, "/stream?sources=100&size=1000&output=json&checkPoint={\"windowScn\":-1,\"windowOffset\":-1}");

    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));
    assertEquals("expected to get empty response", 0, respHandler.getReceivedBytes().length);

    HttpResponse respObj = respHandler.getResponse();
    assertNull("/stream returned unexpected error", respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    assertNull("/stream returned unexpected error with cause",
               respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CAUSE_CLASS_HEADER));
  }

  @Test
  public void testNoDataStreamCommandThreaded() throws Exception
  {
    LOG.debug("\n\nstarting testNoDataStreamCommandThreaded()\n");

    int numThreads = 10;
    Runnable runRegisterCommandTwoSources = new Runnable()
        {
          @Override
          public void run()
          {
            LOG.info("Starting thread for /stream");
            boolean ok = true;
            try
            {
              doTestRegisterCommandTwoSources();
            }
            catch (Exception e)
            {
              ok = false;
            }
            assertTrue("unexpected /register exception", ok);
            LOG.info("Stopping thread for /stream");
          }
        };

    Future<?>[] future = new Future<?>[numThreads];

    for (int i = 0; i < numThreads; ++i) future[i] = _executor.submit(runRegisterCommandTwoSources);

    try
    {
      for (int i = 0; i < numThreads; ++i) future[i].get(2, TimeUnit.SECONDS);
    }
    catch (InterruptedException ie) {}
    for (int i = 0; i < numThreads; ++i)
    {
      assertTrue("/register thread " + (i+1) + " failed to complete", future[i].isDone());
    }
  }

  private void prepareTestOneDataStreamCommand() throws Exception
  {
    //generate an event
    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                                                     HttpMethod.GET, "/genDataEvents/start?src_ids=100&fromScn=10&eventsPerSec=1&duration=10");
    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));

    String respString = new String(respHandler.getReceivedBytes());
    LOG.debug("Response string:" + respString);

    ByteArrayInputStream in = new ByteArrayInputStream(respHandler.getReceivedBytes());
    ObjectMapper objMapper = new ObjectMapper();
    Map<String,String> genRes = objMapper.readValue(in, new TypeReference<Map<String,String>>(){});
    HttpResponse respObj = respHandler.getResponse();
    assertNull("/genDataEvents returned unexpected error", respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    assertEquals("event-generation failed to start", "true", genRes.get("genDataEventsStarted"));

    try {Thread.sleep(2000);} catch (InterruptedException ie) {}
  }

  private void doTestOneDataStreamCommand() throws Exception
  {
    //try to read it
    Checkpoint cp = Checkpoint.createFlexibleCheckpoint();

    String streamRequest = "/stream?sources=100&size=100000&output=json&checkPoint=" + cp.toString();

    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, streamRequest);

    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));

    HttpResponse respObj = respHandler.getResponse();
    assertNull("/stream returned unexpected error", respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));

    if (LOG.isDebugEnabled())
    {
      LOG.debug("/stream response:" + new String(respHandler.getReceivedBytes()));
    }

    ObjectMapper objMapper = new ObjectMapper();
    ByteArrayInputStream in = new ByteArrayInputStream(respHandler.getReceivedBytes());
    objMapper.readValue(in, new TypeReference<Map<String,String>>(){});
  }

  /** Validates the version checks in the stream calls. */
  private void doTestOneDataClientVerStreamCommand(int ver, boolean expectFail) throws Exception
  {
    //try to read it
    Checkpoint cp = Checkpoint.createFlexibleCheckpoint();

    String maxev = "&" + DatabusHttpHeaders.MAX_EVENT_VERSION + "=" + ver;
    // protocol version 2 (versions >= 3 use "subs=")
    String streamRequest = "/stream?sources=100&size=100000&output=json&checkPoint=" + cp.toString() + maxev;

    HttpRequest httpRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, streamRequest);

    SimpleTestHttpClient httpClient = SimpleTestHttpClient.createLocal(TimeoutPolicy.ALL_TIMEOUTS);
    SimpleHttpResponseHandler respHandler = httpClient.sendRequest(_serverAddress, httpRequest);

    assertTrue("failed to get a response", respHandler.awaitResponseUninterruptedly(1, TimeUnit.SECONDS));

    HttpResponse respObj = respHandler.getResponse();
    if (expectFail)
    {
      assertNotNull("/stream failed to return expected error",
                    respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    }
    else
    {
      assertNull("/stream returned unexpected error",
                 respObj.getHeader(DatabusHttpHeaders.DATABUS_ERROR_CLASS_HEADER));
    }

    if (LOG.isDebugEnabled())
    {
      LOG.debug("/stream response:" + new String(respHandler.getReceivedBytes()));
    }

    ObjectMapper objMapper = new ObjectMapper();
    ByteArrayInputStream in = new ByteArrayInputStream(respHandler.getReceivedBytes());
    objMapper.readValue(in, new TypeReference<Map<String,String>>(){});
  }

  @Test
  public void testOneDataStreamCommand() throws Exception
  {
    LOG.debug("\n\nstarting testOneDataStreamCommand()\n");

    prepareTestOneDataStreamCommand();
    doTestOneDataStreamCommand();
  }

  @Test
  // test relay handling of max event version
  public void testOneDataClientVerStreamCommand() throws Exception
  {
    LOG.debug("\n\nstarting testOneDataClientVerStreamCommand()\n");

    prepareTestOneDataStreamCommand();
    doTestOneDataClientVerStreamCommand(0, false);

    // version "1" doesn't exist (V1 == 0), and the code strictly enforces it now:
    doTestOneDataClientVerStreamCommand(1, true);

    doTestOneDataClientVerStreamCommand(2, false);

    // version of the client is smaller than the version of the events
    doTestOneDataClientVerStreamCommand(-1, true);
  }

  @Test
  public void testOneDataStreamCommandThreaded() throws Exception
  {
    LOG.debug("\n\nstarting testOneDataStreamCommandThreaded()\n");

    prepareTestOneDataStreamCommand();

    int numThreads = 10;
    Runnable runOneDataStreamCommand = new Runnable()
        {
          @Override
          public void run()
          {
            LOG.info("Starting thread for /stream");
            boolean ok = true;
            try
            {
              doTestOneDataStreamCommand();
            }
            catch (Exception e)
            {
              ok = false;
            }
            assertTrue("unexpected /stream exception", ok);
            LOG.info("Stopping thread for /stream");
          }
        };

    Future<?>[] future = new Future<?>[numThreads];

    for (int i = 0; i < numThreads; ++i) future[i] = _executor.submit(runOneDataStreamCommand);

    try
    {
      for (int i = 0; i < numThreads; ++i) future[i].get(2, TimeUnit.SECONDS);
    }
    catch (InterruptedException ie) {}
    for (int i = 0; i < numThreads; ++i)
    {
      assertTrue("/register thread " + (i+1) + " failed to complete", future[i].isDone());
    }
  }

}
TOP

Related Classes of com.linkedin.databus.container.netty.TestRelayCommandsLocal

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.