/*
* 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 java.text.MessageFormat;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.overlord.commons.services.ServiceClose;
import org.overlord.commons.services.ServiceInit;
import org.overlord.rtgov.active.collection.predicate.Predicate;
import org.overlord.rtgov.common.util.RTGovProperties;
import org.overlord.rtgov.internal.active.collection.ACManagement;
/**
* This class provides the abstract base implementation of the ActiveCollectionManager
* interface. This class provides a general implementation that can be used by
* derived implementations in different environments.
*
*/
public abstract class AbstractActiveCollectionManager implements ActiveCollectionManager {
private static final Logger LOG=Logger.getLogger(AbstractActiveCollectionManager.class.getName());
private static final long HOUSE_KEEPING_INTERVAL = 10000;
private java.util.Map<String, ActiveCollection> _activeCollections=
new java.util.HashMap<String, ActiveCollection>();
private java.util.Map<String, ActiveCollectionSource> _activeCollectionSources=
new java.util.HashMap<String, ActiveCollectionSource>();
private java.util.Map<String, java.lang.ref.SoftReference<ActiveCollection>> _derivedActiveCollections=
new java.util.HashMap<String, java.lang.ref.SoftReference<ActiveCollection>>();
private java.util.Map<String, ActiveCollection> _derivedActiveCollectionsRetain=
new java.util.HashMap<String, ActiveCollection>();
private java.util.List<ActiveCollectionListener> _activeCollectionListeners=
new java.util.ArrayList<ActiveCollectionListener>();
private Long _houseKeepingInterval;
private HouseKeeper _houseKeeper=null;
private ActiveCollectionContext _context=new DefaultActiveCollectionContext(this);
private ACManagement _management;
private boolean _initialized=false;
/**
* The default constructor.
*/
public AbstractActiveCollectionManager() {
_houseKeepingInterval = RTGovProperties.getPropertyAsLong("ActiveCollectionManager.houseKeepingInterval",
HOUSE_KEEPING_INTERVAL);
}
/**
* This method initializes the Active Collection Manager.
*/
@ServiceInit
public synchronized void init() {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Initialize active collection manager (initialised already? "+_initialized+")");
}
if (!_initialized) {
_houseKeeper = new HouseKeeper();
_management = new ACManagement(this);
_management.init();
_initialized = true;
}
}
/**
* This method sets the house keeping interval.
*
* @param interval The interval
*/
public void setHouseKeepingInterval(long interval) {
_houseKeepingInterval = interval;
}
/**
* This method gets the house keeping interval.
*
* @return The interval
*/
public long getHouseKeepingInterval() {
if (_houseKeepingInterval == null) {
return (HOUSE_KEEPING_INTERVAL);
}
return (_houseKeepingInterval);
}
/**
* {@inheritDoc}
*/
public void register(ActiveCollectionSource acs) throws Exception {
ActiveCollection ac=null;
// Check whether active collection for name has already been created
synchronized (_activeCollections) {
if (_activeCollectionSources.containsKey(acs.getName())) {
throw new IllegalArgumentException("Active collection source already exists for '"
+acs.getName()+"'");
}
// Initialize the active collection source
acs.init(_context);
_activeCollectionSources.put(acs.getName(), acs);
LOG.info("Registered active collection source '"+acs.getName()+"'");
// If not lazy instantiation
if (!acs.getLazy()) {
ac = acs.getActiveCollection();
_activeCollections.put(acs.getName(), ac);
// Check for derived collections
if (acs.getDerivedActiveCollections().size() > 0) {
for (ActiveCollection dac : acs.getDerivedActiveCollections()) {
_derivedActiveCollectionsRetain.put(dac.getName(), dac);
}
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Registered active collection '"+acs.getName()+"' immediately");
}
}
}
if (ac != null) {
synchronized (_activeCollectionListeners) {
for (int i=0; i < _activeCollectionListeners.size(); i++) {
_activeCollectionListeners.get(i).registered(ac);
}
}
}
}
/**
* {@inheritDoc}
*/
public void unregister(ActiveCollectionSource acs) throws Exception {
ActiveCollection ac=null;
synchronized (_activeCollections) {
ac = _activeCollections.remove(acs.getName());
if (ac != null) {
// Active collection needs to be unregistered before closing
// the active collection source, as the source unregisters
// any active change listeners associated with the collection
synchronized (_activeCollectionListeners) {
for (int i=0; i < _activeCollectionListeners.size(); i++) {
_activeCollectionListeners.get(i).unregistered(ac);
}
}
}
if (!_activeCollectionSources.containsKey(acs.getName())) {
throw new IllegalArgumentException("Active collection '"
+acs.getName()+"' is not registered");
}
// Close the active collection source
acs.close();
acs.setActiveCollection(null);
_activeCollectionSources.remove(acs.getName());
// Remove derived collections
for (ActiveCollection dac : acs.getDerivedActiveCollections()) {
_derivedActiveCollectionsRetain.remove(dac.getName());
}
}
LOG.info("Unregistered active collection for source '"+acs.getName()+"'");
}
/**
* {@inheritDoc}
*/
public ActiveCollection getActiveCollection(String name) {
ActiveCollection ret=null;
synchronized (_activeCollections) {
ret = _activeCollections.get(name);
if (ret == null) {
if (_activeCollectionSources.containsKey(name)) {
ActiveCollectionSource acs=_activeCollectionSources.get(name);
ret = acs.getActiveCollection();
if (ret != null) {
_activeCollections.put(acs.getName(), ret);
// Check for derived collections
if (acs.getDerivedActiveCollections().size() > 0) {
for (ActiveCollection dac : acs.getDerivedActiveCollections()) {
_derivedActiveCollectionsRetain.put(dac.getName(), dac);
}
}
// Register listeners
synchronized (_activeCollectionListeners) {
for (int i=0; i < _activeCollectionListeners.size(); i++) {
_activeCollectionListeners.get(i).registered(ret);
}
}
}
} else {
// Check if a retained derived active collection
ret = _derivedActiveCollectionsRetain.get(name);
}
}
}
if (ret == null) {
synchronized (_derivedActiveCollections) {
// Check if a derived active collection
java.lang.ref.SoftReference<ActiveCollection> ref=_derivedActiveCollections.get(name);
if (ref != null) {
ret = ref.get();
}
}
}
return (ret);
}
/**
* {@inheritDoc}
*/
public java.util.Collection<ActiveCollection> getActiveCollections() {
return (Collections.synchronizedMap(_activeCollections).values());
}
/**
* {@inheritDoc}
*/
public ActiveCollection create(String name, ActiveCollection parent, Predicate predicate,
java.util.Map<String,Object> properties) {
ActiveCollection ret=null;
synchronized (_derivedActiveCollections) {
// Check if collection already exists
java.lang.ref.SoftReference<ActiveCollection> ref=_derivedActiveCollections.get(name);
if (ref != null) {
ret = ref.get();
if (ret == null) {
_derivedActiveCollections.remove(name);
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("Removing soft reference to active collection '"+name+"'");
}
}
}
if (ret == null) {
ret = parent.derive(name, _context, predicate, properties);
_derivedActiveCollections.put(name, new java.lang.ref.SoftReference<ActiveCollection>(ret));
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("Derived active collection '"+name+"' with predicate: "+predicate);
}
}
}
return (ret);
}
/**
* {@inheritDoc}
*/
public void remove(String name) {
synchronized (_derivedActiveCollections) {
_derivedActiveCollections.remove(name);
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("Removed derived active collection '"+name+"'");
}
}
}
/**
* This method performs the cleanup task on the top level
* active collections.
*/
protected void cleanup() {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Running active collection cleanup ....");
}
synchronized (_activeCollections) {
for (ActiveCollectionSource acs : _activeCollectionSources.values()) {
if (acs.hasActiveCollection()) {
ActiveCollection ac=acs.getActiveCollection();
try {
ac.cleanup();
} catch (Exception e) {
LOG.log(Level.SEVERE, MessageFormat.format(
java.util.PropertyResourceBundle.getBundle(
"active-collection.Messages").getString("ACTIVE-COLLECTION-3"),
ac.getName()), e);
}
// Check whether the high water mark has been breached
if (ac.getHighWaterMark() > 0) {
if (ac.getHighWaterMarkWarningIssued()) {
if (ac.getSize() < ac.getHighWaterMark()) {
// TODO: Currently log message, but should also
// report via MBean when implemented
LOG.info("Active collection '"+ac.getName()
+"' has returned below its high water mark ("
+ac.getHighWaterMark()+")");
// Reset warning indicator
ac.setHighWaterMarkWarningIssued(false);
}
} else if (ac.getSize() > ac.getHighWaterMark()) {
// Issue warning
// TODO: Currently log message, but should also
// report via MBean when implemented
LOG.warning("Active collection '"+ac.getName()
+"' has exceeded its high water mark ("
+ac.getHighWaterMark()+")");
ac.setHighWaterMarkWarningIssued(true);
}
}
}
}
}
}
/**
* {@inheritDoc}
*/
public void addActiveCollectionListener(ActiveCollectionListener l) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Register active collection listener="+l);
}
synchronized (_activeCollectionListeners) {
_activeCollectionListeners.add(l);
}
}
/**
* {@inheritDoc}
*/
public void removeActiveCollectionListener(ActiveCollectionListener l) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Unregister active collection listener="+l);
}
synchronized (_activeCollectionListeners) {
_activeCollectionListeners.remove(l);
}
}
/**
* This method closes the Active Collection Manager.
*/
@ServiceClose
public void close() {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Close active collection manager");
}
if (_houseKeeper != null) {
_houseKeeper.cancel();
}
if (_management != null) {
_management.close();
_management = null;
}
synchronized (_activeCollectionListeners) {
for (int i=_activeCollectionListeners.size()-1; i >= 0; i--) {
removeActiveCollectionListener(_activeCollectionListeners.get(i));
}
_activeCollectionListeners.clear();
}
synchronized (_activeCollectionSources) {
for (int i=_activeCollectionSources.size()-1; i >= 0; i--) {
ActiveCollectionSource acs=_activeCollectionSources.get(i);
try {
unregister(acs);
} catch (Exception e) {
LOG.log(Level.SEVERE, "Failed to unregister active collection source '"
+acs.getName()+"'", e);
}
}
_activeCollectionSources.clear();
_activeCollections.clear();
}
// Clear derived collections
_derivedActiveCollections.clear();
_derivedActiveCollectionsRetain.clear();
}
/**
* This class implements the housekeeping functionality to
* cleanup the top level active collections periodically.
*
*/
public class HouseKeeper extends java.util.TimerTask {
private java.util.Timer _timer=new java.util.Timer();
/**
* This is the constructor.
*/
public HouseKeeper() {
_timer.scheduleAtFixedRate(this, getHouseKeepingInterval(), getHouseKeepingInterval());
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
cleanup();
}
}
}