/* * 2012-3 Red Hat Inc. and/or its affiliates and other contributors. * * 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.overlord.rtgov.active.collection; import org.overlord.rtgov.active.collection.predicate.Predicate; /** * This class provides the base Active Collection implementation. * */ public abstract class ActiveCollection implements ActiveCollectionMBean, java.lang.Iterable<Object> { private String _name=null; private ActiveCollectionVisibility _visibility=null; private java.util.List<ActiveChangeListener> _listeners= new java.util.ArrayList<ActiveChangeListener>(); private ActiveCollectionAdapter _adapter=null; private long _itemExpiration=0; private int _maxItems=0; private int _highWaterMark=0; private boolean _highWaterMarkWarningIssued=false; private java.util.Map<String,Object> _properties=null; /** * This constructor initializes the active collection. * * @param name The name */ public ActiveCollection(String name) { _name = name; } /** * This constructor initializes the active collection. * * @param acs The Active Collection source */ public ActiveCollection(ActiveCollectionSource acs) { _name = acs.getName(); _visibility = acs.getVisibility(); _itemExpiration = acs.getItemExpiration(); _maxItems = acs.getMaxItems(); _highWaterMark = acs.getHighWaterMark(); _properties = acs.getProperties(); } /** * This constructor initializes the active collection as a derived * version of the supplied collection, that applies the supplied predicate. * * @param name The name * @param parent The parent collection * @param context The context * @param predicate The predicate * @param properties The optional properties */ protected ActiveCollection(String name, ActiveCollection parent, ActiveCollectionContext context, Predicate predicate, java.util.Map<String,Object> properties) { this(name); _adapter = new ActiveCollectionAdapter(parent, context, predicate); _visibility = parent.getVisibility(); _properties = properties; } /** * This method returns the name of the active collection. * * @return The name */ public String getName() { return (_name); } /** * This method returns the parent active collection. * * @return The parent active collection, if defined. */ protected ActiveCollection getParent() { return (_adapter == null ? null : _adapter.getParent()); } /** * This method returns the predicate. * * @return The predicate, if defined. */ protected Predicate getPredicate() { return (_adapter == null ? null : _adapter.getPredicate()); } /** * This method returns the context. * * @return The context, if defined. */ protected ActiveCollectionContext getContext() { return (_adapter == null ? null : _adapter.getContext()); } /** * This method returns the visibility. * * @return The visibility */ public ActiveCollectionVisibility getVisibility() { return (_visibility); } /** * This method sets the visibility. * * @param visibility The visibility */ protected void setVisibility(ActiveCollectionVisibility visibility) { _visibility = visibility; } /** * This method returns the item expiration duration. * * @return The number of milliseconds that the item should remain * in the active collection, or 0 if not relevant */ public long getItemExpiration() { return (_itemExpiration); } /** * This method sets the item expiration duration. * * @param expire The item expiration duration, or zero * for no expiration duration */ protected void setItemExpiration(long expire) { _itemExpiration = expire; } /** * This method returns the maximum number of items that should be * contained within the active collection. The default policy will * be to remove oldest entry when maximum number is reached. * * @return The maximum number of items, or 0 if not relevant */ public int getMaxItems() { return (_maxItems); } /** * This method sets the maximum number of items * that will be in the active collection. * * @param max The maximum number of items, or zero * for no limit */ protected void setMaxItems(int max) { _maxItems = max; } /** * This method gets the high water mark, used to indicate * when a warning should be issued. * * @return The high water mark, or 0 if not relevant */ public int getHighWaterMark() { return (_highWaterMark); } /** * This method sets the high water mark, used to indicate * when a warning should be issued. * * @param highWaterMark The high water mark */ protected void setHighWaterMark(int highWaterMark) { _highWaterMark = highWaterMark; } /** * This method determines whether the high water mark * warning has been issued. * * @return Whether the high water mark has been issued */ protected boolean getHighWaterMarkWarningIssued() { return (_highWaterMarkWarningIssued); } /** * This method sets whether the high water mark * warning has been issued. * * @param issued Whether the high water mark has been issued */ protected void setHighWaterMarkWarningIssued(boolean issued) { _highWaterMarkWarningIssued = issued; } /** * This method returns the value of a name property. * * @param name The name * @param def The optional default value, if the property is not defined * @return The property, or null if not found */ protected Object getProperty(String name, Object def) { Object ret=_properties == null ? null : _properties.get(name); if (ret == null) { ret = def; } return (ret); } /** * This method determines whether this is a derived active collection. * * @return Whether the collection is derived */ protected boolean isDerived() { return (_adapter != null); } /** * This property identifies whether the collection is active. If not, * then no notifications will be generated, and the content for derived * collections will be generated on demand. * * @return Whether the derived collection is created "on demand" */ protected boolean isActive() { return ((Boolean)getProperty("active", true)); } /** * This method performs any required cleanup, associated with * the active collection, related to the max items and item * expiration properties. */ protected abstract void cleanup(); /** * This method adds an Active Change Listener to listen * for notifications of change to the active collection. * * @param l The listener */ public void addActiveChangeListener(ActiveChangeListener l) { synchronized (_listeners) { _listeners.add(l); } } /** * This method removes an Active Change Listener. * * @param l The listener */ public void removeActiveChangeListener(ActiveChangeListener l) { synchronized (_listeners) { _listeners.remove(l); } } /** * This method returns the list of active change listeners. * * @return The list of active change listeners */ public java.util.List<ActiveChangeListener> getActiveChangeListeners() { return (java.util.Collections.unmodifiableList(_listeners)); } /** * This method returns the size of the active collection. * * @return The size */ public abstract int getSize(); /** * This method adds the supplied object to the active collection. * If the optional key is provided, it can either be an index * if inserting into a particular position in a list (otherwise * default is to add to the end of the list), or a specific value * intended to be the key for a map. * * @param key The optional key * @param value The value */ protected abstract void doInsert(Object key, Object value); /** * This method notifies interested listeners that the entry with the * supplied key (which may be an index into a list) has been inserted * with the supplied value. A 'null' key for a list means the value * was added to the end of the list. * * @param key The key, or list index, for the inserted value * @param value The inserted value */ protected void inserted(Object key, Object value) { synchronized (_listeners) { if (_listeners.size() > 0) { for (ActiveChangeListener l : _listeners) { l.inserted(key, value); } } } } /** * This method updates the supplied value within the active collection, * based on the supplied key. If the active collection is a list, then * the key will be an integer reflecting the index of the element being * updated. If the active collection is a map, then the key will be * associated with the element to be updated. * * @param key The key * @param value The value */ protected abstract void doUpdate(Object key, Object value); /** * This method notifies interested listeners that the entry with the * supplied key (which may be an index into a list) has been replaced * with the supplied value. * * @param key The key, or list index, for the replaced value * @param value The replacement value */ protected void updated(Object key, Object value) { synchronized (_listeners) { if (_listeners.size() > 0) { for (ActiveChangeListener l : _listeners) { l.updated(key, value); } } } } /** * This method removes the supplied object from the active collection. * Generally the key should be provided, as this will be the most * efficient means to identify the item to be removed. However if * not provided, then the value will be used to locate the item. * * @param key The optional key * @param value The value */ protected abstract void doRemove(Object key, Object value); /** * This method notifies interested listeners that the entry with the * supplied key (which may be an index into a list) has been removed * having the supplied value. * * @param key The key, or list index, for the removed value * @param value The removed value */ protected void removed(Object key, Object value) { synchronized (_listeners) { if (_listeners.size() > 0) { for (ActiveChangeListener l : _listeners) { l.removed(key, value); } } } } /** * {@inheritDoc} */ protected void finalize() throws Throwable { super.finalize(); if (_adapter != null) { _adapter.close(); } } /** * This method derives a child active collection from this parent collection, * with the specified name, and filtered using the supplied predicate. * * @param name The derived collection name * @param context The context * @param predicate The predicate * @param properties The optional properties * @return The derived collection */ protected abstract ActiveCollection derive(String name, ActiveCollectionContext context, Predicate predicate, java.util.Map<String,Object> properties); /** * This method queries the active collection, using the supplied spec. * * @param qs The query spec * @return The query results */ public abstract java.util.List<Object> query(QuerySpec qs); /** * This class provides a bridge between the parent and derived active * collections. * */ public class ActiveCollectionAdapter implements ActiveChangeListener { private ActiveCollection _parent=null; // Strong ref to ensure not garbage collected // while child still needs it private ActiveCollectionContext _context=null; private Predicate _predicate=null; /** * This constructor initializes the fields within the adapter. * * @param parent The parent active collection * @param context The context * @param predicate The predicate used to filter changes applied to the * active collection */ public ActiveCollectionAdapter(ActiveCollection parent, ActiveCollectionContext context, Predicate predicate) { _parent = parent; _context = context; _predicate = predicate; // Register to receive change notifications from parent // active collection _parent.addActiveChangeListener(this); } /** * The parent active collection. * * @return The parent */ protected ActiveCollection getParent() { return (_parent); } /** * The predicate. * * @return The predicate */ protected Predicate getPredicate() { return (_predicate); } /** * The context. * * @return The context */ protected ActiveCollectionContext getContext() { return (_context); } /** * {@inheritDoc} */ public void inserted(Object key, Object value) { if (_predicate.evaluate(_context, value)) { doInsert(key, value); } } /** * {@inheritDoc} */ public void updated(Object key, Object value) { if (_predicate.evaluate(_context, value)) { doUpdate(key, value); } } /** * {@inheritDoc} */ public void removed(Object key, Object value) { if (_predicate.evaluate(_context, value)) { doRemove(key, value); } } /** * This method closes the adapter. */ public void close() { _parent.removeActiveChangeListener(this); _parent = null; } } }