/* * Copyright © 2015 Cask Data, 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 co.cask.cdap.runtime.app; import co.cask.cdap.api.annotation.Output; import co.cask.cdap.api.annotation.ProcessInput; import co.cask.cdap.api.annotation.Tick; import co.cask.cdap.api.app.AbstractApplication; import co.cask.cdap.api.flow.AbstractFlow; import co.cask.cdap.api.flow.flowlet.AbstractFlowlet; import co.cask.cdap.api.flow.flowlet.FlowletContext; import co.cask.cdap.api.flow.flowlet.OutputEmitter; import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.util.concurrent.TimeUnit; /** * An app to test whether queue pending events metrics are emitted correctly. */ public class PendingMetricTestApp extends AbstractApplication { @Override public void configure() { addFlow(new TestPendingFlow()); } public static class TestPendingFlow extends AbstractFlow { @Override protected void configureFlow() { setName("TestPendingFlow"); setDescription("A flow to test whether queue pending events metrics are emitted correctly."); addFlowlet("source", new Source()); addFlowlet("forward-one", new ForwardOne()); addFlowlet("forward-two", new ForwardTwo()); addFlowlet("sink", new Sink()); connect("source", "forward-one"); connect("source", "forward-two"); connect("forward-one", "sink"); connect("forward-two", "sink"); } } public static class Source extends AbstractFlowlet { private static final Logger LOG = LoggerFactory.getLogger(Source.class); boolean generated = false; @Output("ints") private OutputEmitter<Integer> intOut; @Output("strings") private OutputEmitter<String> stringOut; @Tick(delay = 1L, unit = TimeUnit.MILLISECONDS) void generateOnce() throws InterruptedException { if (generated) { TimeUnit.MILLISECONDS.sleep(50); return; } String numEventsStr = getContext().getRuntimeArguments().get("count"); int numEventsToGenerate = numEventsStr == null ? 2 : Integer.parseInt(numEventsStr); for (int i = 0; i < numEventsToGenerate; i++) { intOut.emit(i); stringOut.emit(Integer.toString(i)); } generated = true; LOG.info("Emitted " + numEventsToGenerate + " events."); } } public static class ForwardOne extends AbstractFlowlet { private static final Logger LOG = LoggerFactory.getLogger(ForwardOne.class); private OutputEmitter<String> out; private File fileToWaitFor; @Override public void initialize(FlowletContext context) throws Exception { super.initialize(context); fileToWaitFor = getTempFile(context, "one"); } @ProcessInput void processInt(int i) throws InterruptedException { waitForFile(fileToWaitFor, TimeUnit.SECONDS.toMillis(5)); out.emit(Integer.toString(i)); LOG.info("Forwarded int " + i); } } public static class ForwardTwo extends AbstractFlowlet { private static final Logger LOG = LoggerFactory.getLogger(ForwardTwo.class); private OutputEmitter<String> out; private File fileToWaitForInt; private File fileToWaitForString; @Override public void initialize(FlowletContext context) throws Exception { super.initialize(context); fileToWaitForInt = getTempFile(context, "two-i"); fileToWaitForString = getTempFile(context, "two-s"); } @ProcessInput void processInt(int i) throws InterruptedException { waitForFile(fileToWaitForInt, TimeUnit.SECONDS.toMillis(5)); out.emit(Integer.toString(i)); LOG.info("Forwarded int " + i); } @ProcessInput void processString(String s) throws InterruptedException { waitForFile(fileToWaitForString, TimeUnit.SECONDS.toMillis(5)); out.emit(s); LOG.info("Forwarded string \"" + s + "\""); } } public static class Sink extends AbstractFlowlet { private static final Logger LOG = LoggerFactory.getLogger(ForwardTwo.class); private File fileToWaitFor; @Override public void initialize(FlowletContext context) throws Exception { super.initialize(context); fileToWaitFor = getTempFile(context, "three"); } @ProcessInput void processString(String s) throws InterruptedException { waitForFile(fileToWaitFor, TimeUnit.SECONDS.toMillis(5)); LOG.info("Received string \"" + s + "\""); } } private static void waitForFile(File file, long timeoutInMillis) throws InterruptedException { long timeoutTime = System.currentTimeMillis() + timeoutInMillis; while (timeoutTime > System.currentTimeMillis()) { if (file.exists()) { return; } TimeUnit.MILLISECONDS.sleep(50); } throw new RuntimeException("timeout waiting for file"); } private static File getTempFile(FlowletContext context, String name) { String path = context.getRuntimeArguments().get("temp"); Assert.assertNotNull(path); return new File(path, name); } }