/* * Copyright 2008 the original author or authors. * Copyright 2005 Sun Microsystems, 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 org.rioproject.impl.watch; import org.rioproject.watch.Calculable; import org.rioproject.watch.WatchDataReplicator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.*; /** * Provides a queued approach to replicate a Watch record. */ public abstract class QueuedReplicator implements WatchDataReplicator, Serializable { private static final long serialVersionUID = 1L; private boolean closed = false; private final BlockingQueue<Calculable> replicatorQ = new LinkedBlockingQueue<Calculable>(); private transient ExecutorService execService; private transient CountDownLatch shutdownLatch; private static Logger logger = LoggerFactory.getLogger("org.rioproject.watch"); /** * Performs the actual write to the underlying resource * * @param calculable the Calculable record to replicate * * @throws IOException if the write encounters errors */ protected abstract void replicate(Calculable calculable) throws IOException; /** * Performs the actual write to the underlying resource * * @param calculables Collection of Calculable records to replicate * * @throws IOException if the write encounters errors */ protected abstract void bulkReplicate(Collection<Calculable> calculables) throws IOException; /** * Closes the underlying Resource. This abstract class does not enforce * the implementation of this method. If the underlying resource needs to * be closed, implementing classes need to override this method. */ @SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract") protected void closeResource() { } public synchronized void close() { closed = true; if(replicatorQ!=null && !replicatorQ.isEmpty()) { shutdownLatch = new CountDownLatch(1); try { shutdownLatch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } if (execService != null) { execService.shutdownNow(); execService = null; } if(replicatorQ!=null) replicatorQ.clear(); closeResource(); } /** * Archive a record from the WatchDataSource history by placing it on a * queue * * @param calculable the Calculable record to archive */ public void addCalculable(Calculable calculable) { init(); replicatorQ.add(calculable); } private class ReplicatorTask implements Runnable { public void run() { while(!closed) { try { Calculable calculable = replicatorQ.poll(5, TimeUnit.SECONDS); if(calculable!=null) { replicate(calculable); } } catch(IOException e) { logger.warn("Replication communication failure: ", e); } catch (InterruptedException e) { //logger.warn( "ReplicatorTask interrupted", e); Thread.currentThread().interrupt(); } } try { List<Calculable> drain = new ArrayList<Calculable>(); replicatorQ.drainTo(drain); int numToDrain = drain.size(); if(numToDrain>0) { try { bulkReplicate(drain); } catch(IOException e) { logger.warn("Cannot archive (draining): ", e); } } } finally { if(shutdownLatch!=null) shutdownLatch.countDown(); } } } private void readObject(ObjectInputStream oStream) throws ClassNotFoundException, IOException { oStream.defaultReadObject(); replicatorQ.clear(); init(); } private synchronized void init() { if(execService==null) { execService = Executors.newCachedThreadPool(); execService.submit(new ReplicatorTask()); } } }