Java tutorial
/** * Copyright 2012 RK * * 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 rk.java.compute.cep; import java.util.Collection; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.springframework.util.StopWatch; import rk.java.compute.cep.Compute.IPriceEventSink; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; /** * Executor Service for {@link Compute} instances. * <P> * Subscribes to tick events via {@link #listen(TickEvent)} and dispatches * to appropriate {@link Compute}. * <P> * {@link Compute} also uses {@link EventBus} as an output sink. * * @author RK * */ public class ComputeService { private final BlockingQueue<IPriceTick> feedQueue; private final ConcurrentHashMap<String, Compute> handlerCache; private final StopWatch stopWatch; private ExecutorService executorService; private CompletionService<StopWatch> ecs; private Future<StopWatch> dispatchTaskFuture; private volatile long ticksReceivedCount = 0; private volatile long ticksProcessedCount; private int shutdownCalls = 0; private final int numberOfTickSources; private IPriceEventSink eventbus; /** * @param numberOfTickSources - used to build quorum for shutdown * @param eventBus - an {@link EventBus} that's used for the creation of {@link Compute} instances */ public ComputeService(final int numberOfTickSources, IPriceEventSink eventBus) { this(new LinkedBlockingQueue<IPriceTick>(), numberOfTickSources, eventBus); } public ComputeService(final int numberOfTickSources, final int threadPoolSize, IPriceEventSink eventBus) { this(new LinkedBlockingQueue<IPriceTick>(), numberOfTickSources, eventBus); executorService = Executors.newFixedThreadPool(threadPoolSize); ecs = new ExecutorCompletionService<StopWatch>(executorService); } public ComputeService(BlockingQueue<IPriceTick> queue, final int numberOfTickSources, IPriceEventSink eventbus) { this.feedQueue = queue; this.numberOfTickSources = numberOfTickSources; this.eventbus = eventbus; this.handlerCache = new ConcurrentHashMap<String, Compute>(); this.stopWatch = new StopWatch("Dispatcher Task"); executorService = Executors.newCachedThreadPool(); ecs = new ExecutorCompletionService<StopWatch>(executorService); dispatchTaskFuture = ecs.submit(new Callable<StopWatch>() { @Override public StopWatch call() throws Exception { stopWatch.start(); run(); stopWatch.stop(); return stopWatch; } }); } private final void run() { while (!Thread.currentThread().isInterrupted()) { IPriceTick tick = null; try { tick = feedQueue.take(); ticksProcessedCount++; if (tick == IPriceTick.POISON) { if (++shutdownCalls >= numberOfTickSources) { shutdownHandlers(); return; } else { continue; } } String instrument = tick.getInstrument(); Compute handler = getOrCreateHandler(instrument); handler.add(tick); } catch (InterruptedException inte) { Thread.currentThread().interrupt(); } } } /** * @param unit * @param timeout * @param unit * @return {@link #stopWatch} which contains basic runtime stats * @throws TimeoutException * @throws ExecutionException * @throws InterruptedException * */ public StopWatch shutDownAndAwaitTermination(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { StopWatch stopWatch = this.dispatchTaskFuture.get(timeout, unit); this.executorService.shutdown(); this.executorService.awaitTermination(timeout, unit); return stopWatch; } /** * Get the handler for this instrument if one exists - if not * then create one. * If one does exist and it's broken the create and replace it * in the cache. * * @param instrument * @return */ private Compute getOrCreateHandler(String instrument) { Compute handler; if (!handlerCache.containsKey(instrument)) { handler = new Compute(instrument, this.eventbus); Compute putIfAbsent = handlerCache.putIfAbsent(instrument, handler); if (putIfAbsent != null) { handler = putIfAbsent; } else { this.ecs.submit(handler); } } else { handler = handlerCache.get(instrument); } return handler; } private void shutdownHandlers() { System.out.println("Shutting down dispatcher. "); System.out.println("Dispatcher's HandlerCache size = " + this.handlerCache.size()); Collection<Compute> values = handlerCache.values(); for (Compute compute : values) { compute.add(IPriceTick.POISON); } } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "ComputeService [stopWatch=" + this.stopWatch + ", handlerCache.size()=" + this.handlerCache.size() + ", ticksReceivedCount=" + this.ticksReceivedCount + ", ticksProcessedCount=" + this.ticksProcessedCount + "]"; } /** * This is a convention callback for {@link EventBus#register(Object)} * <P> * Registers with the given bus for {@link TickEvent} via {@link #listen(TickEvent)} * @param bus */ public void subscribeToTickEventsFrom(EventBus bus) { bus.register(this); } /** * Subscribes to {@link EventBus} for {@link TickEvent}s * @param tickEvent */ @Subscribe public void listen(IPriceTick tickEvent) { this.feedQueue.add(tickEvent); this.ticksReceivedCount++; } /** * @return the ticksProcessedCount */ public long getTicksProcessedCount() { return this.ticksProcessedCount; } /** * @return the ticksReceivedCount */ public long getTicksReceivedCount() { return this.ticksReceivedCount; } }