/* * Copyright 2013 Netflix, Inc. * * 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. */ package com.netflix.suro.sink; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.netflix.governator.guice.LifecycleInjector; import com.netflix.governator.lifecycle.LifecycleManager; import com.netflix.suro.SuroPlugin; import com.netflix.suro.jackson.DefaultObjectMapper; import com.netflix.suro.message.MessageContainer; import org.junit.Test; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.*; public class TestSinkManager { private Map<String, Sink> getSinkMap(ObjectMapper mapper, String desc) throws Exception { return mapper.<Map<String, Sink>>readValue( desc, new TypeReference<Map<String, Sink>>() {}); } public static class TestSink implements Sink { public static final String TYPE = "TestSink"; private final String message; private String status; private static AtomicInteger numOfSink = new AtomicInteger(); public static int getNumOfSinks() { return numOfSink.get(); } @JsonCreator public TestSink(@JsonProperty("message") String message) { this.message = message; } @Override public void writeTo(MessageContainer message) {} @Override public void open() { status = "open"; numOfSink.incrementAndGet(); } @Override public void close() { status = "closed"; numOfSink.decrementAndGet(); } @Override public String recvNotice() { return null; } @Override public String getStat() { return message + " " + status; } @Override public long getNumOfPendingMessages() { return 0; } @Override public long checkPause() { return 0; } } @Test public void test() throws Exception { Injector injector = LifecycleInjector.builder().withModules( new SuroPlugin() { @Override protected void configure() { this.addSinkType("TestSink", TestSink.class); } }, new AbstractModule() { @Override protected void configure() { bind(ObjectMapper.class).to(DefaultObjectMapper.class); } } ).build().createInjector(); LifecycleManager lifecycleManager = injector.getInstance(LifecycleManager.class); lifecycleManager.start(); String desc = "{\n" + " \"default\": {\n" + " \"type\": \"TestSink\",\n" + " \"message\": \"defaultTestSink\"\n" + " },\n" + " \"topic1\": {\n" + " \"type\": \"TestSink\",\n" + " \"message\": \"topic1TestSink\"\n" + " }\n" + "}"; SinkManager sinkManager = injector.getInstance(SinkManager.class); ObjectMapper mapper = injector.getInstance(ObjectMapper.class); sinkManager.set(getSinkMap(mapper, desc)); assertEquals(sinkManager.getSink("topic1").getStat(), "topic1TestSink open"); assertEquals(sinkManager.getSink("default").getStat(), "defaultTestSink open"); assertEquals(sinkManager.getSink("topic7").getStat(), "defaultTestSink open"); assertTrue( sinkManager.reportSinkStat().equals("default:defaultTestSink open\n\ntopic1:topic1TestSink open\n\n") || sinkManager.reportSinkStat().equals("topic1:topic1TestSink open\n\ndefault:defaultTestSink open\n\n")); assertEquals(TestSink.getNumOfSinks(), 2); // change desc - test removal desc = "{\n" + " \"default\": {\n" + " \"type\": \"TestSink\",\n" + " \"message\": \"defaultTestSink\"\n" + " }\n" + "}"; sinkManager.set(getSinkMap(mapper, desc)); assertEquals(sinkManager.getSink("topic1").getStat(), "defaultTestSink open"); assertEquals(sinkManager.getSink("default").getStat(), "defaultTestSink open"); assertEquals(sinkManager.reportSinkStat(), String.format("default:defaultTestSink open\n\n")); assertEquals(TestSink.getNumOfSinks(), 1); // change desc - test addition desc = "{\n" + " \"default\": {\n" + " \"type\": \"TestSink\",\n" + " \"message\": \"defaultTestSink\"\n" + " },\n" + " \"topic2\": {\n" + " \"type\": \"TestSink\",\n" + " \"message\": \"topic2TestSink\"\n" + " }\n" + "}"; sinkManager.set(getSinkMap(mapper, desc)); assertEquals(sinkManager.getSink("topic1").getStat(), "defaultTestSink open"); assertEquals(sinkManager.getSink("default").getStat(), "defaultTestSink open"); assertEquals(sinkManager.getSink("topic2").getStat(), "topic2TestSink open"); assertTrue( sinkManager.reportSinkStat().equals("default:defaultTestSink open\n\ntopic2:topic2TestSink open\n\n") || sinkManager.reportSinkStat().equals("topic2:topic2TestSink open\n\ndefault:defaultTestSink open\n\n") ); assertEquals(TestSink.getNumOfSinks(), 2); // test exception - nothing changed desc = "{\n" + " \"default\": {\n" + " \"type\": \"TestSink\",\n" + " \"message\": \"defaultTestSink\"\n" + " },\n" + " \"topic2\": {\n" + " \"type\": \"TestSink\",\n" + " \"message\": \"topic2TestSink\"\n" + " }\n" + "},"; sinkManager.set(getSinkMap(mapper, desc)); assertEquals(sinkManager.getSink("topic1").getStat(), "defaultTestSink open"); assertEquals(sinkManager.getSink("default").getStat(), "defaultTestSink open"); assertEquals(sinkManager.getSink("topic2").getStat(), "topic2TestSink open"); assertTrue( sinkManager.reportSinkStat().equals("default:defaultTestSink open\n\ntopic2:topic2TestSink open\n\n") || sinkManager.reportSinkStat().equals("topic2:topic2TestSink open\n\ndefault:defaultTestSink open\n\n") ); assertEquals(TestSink.getNumOfSinks(), 2); // test destroy lifecycleManager.close(); assertEquals(TestSink.getNumOfSinks(), 0); assertNull(sinkManager.getSink("any")); } }