/* * 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 com.sun.jini.config.Config; import com.sun.jini.proxy.BasicProxyTrustVerifier; import net.jini.config.Configuration; import net.jini.config.ConfigurationException; import net.jini.export.Exporter; import net.jini.security.TrustVerifier; import net.jini.security.proxytrust.ServerProxyTrust; import org.rioproject.impl.config.ExporterConfig; import org.rioproject.watch.Calculable; import org.rioproject.watch.ThresholdValues; import org.rioproject.watch.WatchDataReplicator; import org.rioproject.watch.WatchDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.List; /** The WatchDataSourceImpl provides support for the WatchDataSource interface. The WatchDataSourceImpl supports the following configuration entries; where each configuration entry name is associated with the component name <span style="font-family: monospace;">org.rioproject.watch </span> <br> </p> <ul> <li><span style="font-weight: bold; font-family: courier new,courier,monospace;">watchDataSourceExporter </span> <br style="font-family: courier new,courier,monospace;"> <table cellpadding="2" cellspacing="2" border="0" style="text-align: left; width: 100%;"> <tbody> <tr> <td style="vertical-align: top; text-align: right; font-weight: bold;">Type: <br> </td> <td style="vertical-align: top;">Exporter</td> </tr> <tr> <td style="vertical-align: top; text-align: right; font-weight: bold;">Default: <br> </td> <td style="vertical-align: top;">A new <code>BasicJeriExporter</code> with <ul> <li>a <code>TcpServerEndpoint</code> created on a random port,</li> <li>a <code>BasicILFactory</code>,</li> <li>distributed garbage collection turned off,</li> <li>keep alive on.</li> </ul> <code></code></td> </tr> <tr> <td style="vertical-align: top; text-align: right; font-weight: bold;">Description: <br> </td> <td style="vertical-align: top;">The Exporter used to export the WatchDataSourceImpl server. A new exporter is obtained every time a WatchDataSourceImpl needs to export itself.</td> </tr> </tbody> </table> </li> </ul> <ul> <li><span style="font-weight: bold; font-family: courier new,courier,monospace;">collectionSize</span> <br style="font-family: courier new,courier,monospace;"> <table cellpadding="2" cellspacing="2" border="0" style="text-align: left; width: 100%;"> <tbody> <tr> <td style="vertical-align: top; text-align: right; font-weight: bold;">Type: <br> </td> <td style="vertical-align: top;">int</td> </tr> <tr> <td style="vertical-align: top; text-align: right; font-weight: bold;">Default: <br> </td> <td style="vertical-align: top;">1000</td> </tr> <tr> <td style="vertical-align: top; text-align: right; font-weight: bold;">Description: <br> </td> <td style="vertical-align: top;">The size of the WatchDataSource history collection.</td> </tr> </tbody> </table> </li> </ul> <p> */ public class WatchDataSourceImpl implements WatchDataSource, ServerProxyTrust { /** Defines the default history size */ public final static int DEFAULT_COLLECTION_SIZE = 50; /** Defines the default history max size */ public final static int MAX_COLLECTION_SIZE = 1000; /** The current history maximum size */ private int max = DEFAULT_COLLECTION_SIZE; /** The history */ private final ArrayList<Calculable> history = new ArrayList<Calculable>(); /** Holds value of property id. */ private String id = null; /** The class name used to view the WatchDataSource */ private String viewClass; /** Configuration for the WatchDataSource */ private Configuration config; /** The Exporter for the WatchDataSource */ private Exporter exporter; /** Object supporting remote semantics required for an WatchDataSource */ private WatchDataSource proxy; /** Flag to indicate whether the WatchDataSource is initialized */ private boolean initialized = false; /** Flag to indicate whether the WatchDataSource is exported */ private boolean exported = false; /** Flag to indicate whether the WatchDataSource is closed */ private boolean closed = false; /** A reference to the ThresholdManager if the WDS is is for a ThresholdWatch */ private ThresholdManager thresholdManager; /** Component for accessing configuration */ protected static final String COMPONENT = "org.rioproject.watch"; /** A suitable Logger */ protected static Logger logger = LoggerFactory.getLogger(WatchDataSourceImpl.class); private final List<WatchDataReplicator> replicators = new ArrayList<WatchDataReplicator>(); /** * Create a WatchDataSourceImpl */ public WatchDataSourceImpl() { } /** * Constructs WatchDataSourceImpl object. * * @param id The ID of the WatchDataSource * @param config Configuration object for use */ public WatchDataSourceImpl(String id, Configuration config) { if(id==null) throw new IllegalArgumentException("id is null"); if(config==null) throw new IllegalArgumentException("config is null"); this.id = id; this.config = config; doInit(); } public void initialize() throws RemoteException { doInit(); export(); } void setThresholdManager(ThresholdManager thresholdManager) { this.thresholdManager = thresholdManager; } /* * Do initialization */ private void doInit() { if(initialized) return; if(config==null) throw new IllegalStateException("config is null"); int collectionSize; try { collectionSize = Config.getIntEntry(config, COMPONENT, "collectionSize", DEFAULT_COLLECTION_SIZE, 1, MAX_COLLECTION_SIZE); } catch(ConfigurationException e) { logger.trace("Getting WatchDataSource collection size", e); collectionSize = DEFAULT_COLLECTION_SIZE; } logger.trace("Watch [{}] history collection size={}", id, collectionSize); max = collectionSize; initialized = true; } /** * Export the WatchDataSourceImpl using a configured Exporter, defaulting to * BasicJeriExporter * * @return A proxy to use for the WatchDataSource * * @throws RemoteException if there are errors exporting the WatchDataSource */ public WatchDataSource export() throws RemoteException { if(exported && proxy!=null) return(proxy); if(config != null) { try { exporter = ExporterConfig.getExporter(config, COMPONENT, "watchDataSourceExporter"); } catch(Exception e) { logger.error("Getting watchDataSourceExporter", e); } } proxy = (WatchDataSource)exporter.export(this); exported = true; return (proxy); } /** * Get the WatchDataSource proxy * * @return The WatchDataSource proxy */ public WatchDataSource getProxy() { return (proxy); } public void setConfiguration(Configuration config) { this.config = config; } /** * Unexport the WatchDataSourceImpl * * @param force If true, unexports the WatchDataSourceImpl even if there * are pending or in-progress calls; if false, only unexports the * WatchDataSourceImpl if there are no pending or in-progress calls */ public void unexport(boolean force) { if(!exported) return; try { exporter.unexport(force); exported = false; proxy = null; } catch(IllegalStateException e) { /* Ignore */ } } /** * @see org.rioproject.watch.WatchDataSource#getID */ public String getID() { return (id); } /** * Setter for property id. * * @param id New value of property id. */ public void setID(String id) { if(id==null) throw new IllegalArgumentException("id is null"); this.id = id; } /** * @see WatchDataSource#setMaxSize */ public void setMaxSize(int size) { synchronized(history) { if(size < this.max) { // If the size is less then the current maximum, reset the maximum max = size; if(history.size() > size) { trimHistory((history.size() - size) - 1); history.trimToSize(); } } else history.ensureCapacity(size); this.max = size; logger.trace("Watch [{}] history collection size={}", id, size); } } /** * @see org.rioproject.watch.WatchDataSource#clear */ public void clear() { synchronized(history) { history.clear(); } } /* * Trims the history. Always starting at the beginning, with an index of 0 * * @param range The number or records to trim */ private void trimHistory(int range) { if(range == 1) { history.remove(0); logger.trace("Removed first entry to make room in {} history, size now {}", id, history.size()); } else { List subList = history.subList(0, range); history.removeAll(subList); history.trimToSize(); logger.trace("Removed {} entries to make room in {} history, size now {}", id, range, history.size()); } } /** * @see org.rioproject.watch.WatchDataSource#getMaxSize */ public int getMaxSize() { return (max); } /** * @see org.rioproject.watch.WatchDataSource#getCurrentSize */ public int getCurrentSize() { int size; synchronized(history) { size = history.size(); } return (size); } /** * @see org.rioproject.watch.WatchDataSource#addCalculable */ @SuppressWarnings("unchecked") public void addCalculable(Calculable calculable) { if(calculable==null) throw new IllegalArgumentException("calculable is null"); if(!closed) { addToHistory(calculable); for(WatchDataReplicator replicator : getWatchDataReplicators()) { if(logger.isTraceEnabled()) logger.trace("Replicating [{}] to {} {}", calculable.toString(), replicator, replicator.getClass().getName()); replicator.addCalculable(calculable); } } } private void addToHistory(Calculable calculable) { synchronized(history) { if(history.size() == max) trimHistory(1); if(history.size() > max) trimHistory((history.size() - max) - 1); history.add(calculable); if(logger.isTraceEnabled()) logger.trace("[{}] Adding [{}] to history", id, calculable.toString()); } } /** * @see org.rioproject.watch.WatchDataSource#getCalculable */ public Calculable[] getCalculable() { Calculable[] calcs; synchronized(history) { calcs = history.toArray(new Calculable[history.size()]); } return calcs; } /** * @see org.rioproject.watch.WatchDataSource#getCalculable */ public Calculable[] getCalculable(String id) { if(id==null) throw new IllegalArgumentException("id is null"); Calculable[] calcs; synchronized(history) { calcs = history.toArray(new Calculable[history.size()]); } List<Calculable> list = new ArrayList<Calculable>(); for(Calculable c : calcs) { if(c.getId().equals(id)) list.add(c); } return list.toArray(new Calculable[list.size()]); } /** * @see org.rioproject.watch.WatchDataSource#getCalculable(long, long) */ public Calculable[] getCalculable(long from, long to) { List<Calculable> list = new ArrayList<Calculable>(); if(to>from) { synchronized(history) { for(Calculable calc : history) { if(calc.getWhen()>=from && calc.getWhen()<=to) list.add(calc); } } } return (list.toArray(new Calculable[list.size()])); } /** * @see org.rioproject.watch.WatchDataSource#getLastCalculable */ public Calculable getLastCalculable() { Calculable c = null; try { synchronized(history) { c = history.get(history.size() - 1); } } catch(IndexOutOfBoundsException ex) { /* ignore */ } return c; } /** * Make sure the archival file is closed before garbage collection */ protected void finalize() throws Throwable { for(WatchDataReplicator replicator : getWatchDataReplicators()) replicator.close(); super.finalize(); } /** * @see org.rioproject.watch.WatchDataSource#getThresholdValues */ public ThresholdValues getThresholdValues() { ThresholdValues thresholdValues; if(thresholdManager!=null) thresholdValues = thresholdManager.getThresholdValues(); else thresholdValues = new ThresholdValues(); return thresholdValues; } /** * @see org.rioproject.watch.WatchDataSource#setThresholdValues */ public void setThresholdValues(ThresholdValues tValues) { if(thresholdManager!=null) thresholdManager.setThresholdValues(tValues); } /** * @see org.rioproject.watch.WatchDataSource#close */ public void close() { for(WatchDataReplicator replicator : getWatchDataReplicators()) replicator.close(); synchronized(replicators) { replicators.clear(); } closed = true; unexport(true); } /** * @see org.rioproject.watch.WatchDataSource#setView */ public void setView(String viewClass) { this.viewClass = viewClass; } /** * @see org.rioproject.watch.WatchDataSource#getView */ public String getView() { return (viewClass); } /** * Returns a <code>TrustVerifier</code> which can be used to verify that a * given proxy to this WatchDataSource can be trusted */ public TrustVerifier getProxyVerifier() { return (new BasicProxyTrustVerifier(proxy)); } public boolean addWatchDataReplicator(WatchDataReplicator replicator) { boolean added = false; if(replicator==null) return added; synchronized(replicators) { if(!replicators.contains(replicator)) added = replicators.add(replicator); } return added; } public boolean removeWatchDataReplicator(WatchDataReplicator replicator) { boolean removed = false; if(replicator==null) return removed; synchronized(replicators) { removed = replicators.remove(replicator); } return removed; } public WatchDataReplicator[] getWatchDataReplicators() { WatchDataReplicator[] wdrs; synchronized(replicators) { wdrs = replicators.toArray(new WatchDataReplicator[replicators.size()]); } return wdrs; } }