/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.invalidation; import java.io.Serializable; import java.util.HashSet; import javax.management.MBeanParameterInfo; import javax.management.MBeanOperationInfo; import org.jboss.cache.invalidation.InvalidationManager.BridgeInvalidationSubscriptionImpl; /** * Implementation of InvalidationManagerMBean * * @see org.jboss.cache.invalidation.InvalidationManagerMBean * * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>. * @version $Revision: 81030 $ */ public class InvalidationManager extends org.jboss.system.ServiceMBeanSupport implements InvalidationManagerMBean { // Constants ----------------------------------------------------- public static final String DEFAULT_JMX_SERVICE_NAME = "jboss.cache:service=InvalidationManager"; public static final String DEFAULT_INVALIDERS_JMX_NAME = "jboss.cache:service=InvalidationGroup"; // Attributes ---------------------------------------------------- protected java.util.Hashtable groups = new java.util.Hashtable (); protected java.util.Vector bridgeSubscribers = new java.util.Vector (); protected int hashcode = 0; protected boolean DEFAULT_TO_ASYNCHRONOUS_MODE = false; // Static -------------------------------------------------------- // Constructors -------------------------------------------------- public InvalidationManager () { super(); } public void startService () throws Exception { // bind us in system registry // log.debug ("Starting Invalidation Manager " + this.getServiceName ().toString ()); org.jboss.system.Registry.bind (this.getServiceName ().toString (), this); this.hashcode = this.getServiceName ().hashCode (); } public void stopService () { log.debug ("Stoping Invalidation Manager " + this.getServiceName ().toString ()); org.jboss.system.Registry.unbind (this.getServiceName ().toString ()); } public boolean getIsAsynchByDefault() { return DEFAULT_TO_ASYNCHRONOUS_MODE; } public void setIsAsynchByDefault(boolean flag) { this.DEFAULT_TO_ASYNCHRONOUS_MODE = flag; } public java.util.Collection getInvalidationGroups () { return this.groups.values (); } public InvalidationGroup getInvalidationGroup (String groupName) { synchronized (this.groups) { InvalidationGroup group = (InvalidationGroup)this.groups.get (groupName); if (group == null) { group = createGroup (groupName); } group.addReference (); return group; } } public synchronized BridgeInvalidationSubscription registerBridgeListener (InvalidationBridgeListener listener) { log.debug ("Subscribing a new cache-invalidation bridge"); BridgeInvalidationSubscription subs = new BridgeInvalidationSubscriptionImpl(listener); java.util.Vector newVector = new java.util.Vector (this.bridgeSubscribers); newVector.add (subs); this.bridgeSubscribers = newVector; return subs; } public void batchInvalidate (BatchInvalidation[] invalidations) { this.batchInvalidate (invalidations, this.DEFAULT_TO_ASYNCHRONOUS_MODE); } public void batchInvalidate (BatchInvalidation[] invalidations, boolean asynchronous) { if (log.isTraceEnabled ()) log.trace ("Batch cache invalidation. Caches concerned: " + invalidations.length); this.crossDomainBatchInvalidate (null, invalidations, asynchronous); } public void invalidateAll(String groupName) { invalidateAll(groupName, DEFAULT_TO_ASYNCHRONOUS_MODE); } public void invalidateAll(String groupName, boolean async) { if (log.isTraceEnabled ()) log.trace ("Invalidate all for group: " + groupName); crossDomainInvalidateAll(null, groupName, async); } // Public -------------------------------------------------------- // Z implementation ---------------------------------------------- // Object overrides --------------------------------------------------- public int hashCode () { return hashcode; } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- protected InvalidationGroup createGroup (String groupName) { InvalidationGroup group = new org.jboss.cache.invalidation.InvalidationManager.InvalidationGroupImpl (groupName); this.groups.put (groupName, group); // register the group with JMX so it can be easyly remotly // reached (and thus cache can be invalidated) // try { log.debug ("Creating and registering a new InvalidationGroup: " + groupName); javax.management.ObjectName groupObjectName = new javax.management.ObjectName (DEFAULT_INVALIDERS_JMX_NAME + ",GroupName="+groupName); this.getServer ().registerMBean (group, groupObjectName); } catch (Exception e) { log.debug ("Problem while trying to register a new invalidation group in JMX", e); } // warn bridges // log.debug ("Informing bridges about new group creation ..."); for (int i=0; i<bridgeSubscribers.size (); i++) ((BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i))).groupCreated (groupName); return group; } protected void removeGroup (String groupName) { synchronized (this.groups) { this.groups.remove (groupName); // Remove group from JMX // try { log.debug ("Removing and JMX-unregistering an InvalidationGroup: " + groupName); javax.management.ObjectName groupObjectName = new javax.management.ObjectName (DEFAULT_INVALIDERS_JMX_NAME + ",GroupName="+groupName); this.getServer ().unregisterMBean (groupObjectName); } catch (Exception e) { log.debug ("Problem while trying to un-register a new invalidation group in JMX", e); } // warn bridges // for (int i=0; i<bridgeSubscribers.size (); i++) ((BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i))).groupDropped (groupName); } } protected synchronized void unregisterBridgeListener (BridgeInvalidationSubscription bridgeSubscriber) { // safe remove to avoid problems with iterators // log.debug ("Unsubscription of a cache-invalidation bridge"); java.util.Vector newVector = new java.util.Vector (this.bridgeSubscribers); newVector.remove (bridgeSubscriber); this.bridgeSubscribers = newVector; } protected void doLocalOnlyInvalidation (String groupName, Serializable key, boolean asynchronous) { InvalidationGroupImpl group = (InvalidationGroupImpl)this.groups.get (groupName); if (group != null) group.localOnlyInvalidate (key, asynchronous); } protected void doLocalOnlyInvalidations (String groupName, Serializable[] keys, boolean asynchronous) { InvalidationGroupImpl group = (InvalidationGroupImpl)this.groups.get (groupName); if (group != null) group.localOnlyInvalidate (keys, asynchronous); } protected void doLocalOnlyInvalidateAll (String groupName, boolean asynchronous) { InvalidationGroupImpl group = (InvalidationGroupImpl)this.groups.get (groupName); if (group != null) group.localOnlyInvalidateAll(); } protected void doBridgedOnlyInvalidation (BridgeInvalidationSubscriptionImpl exceptSource, String groupName, Serializable key) { for (int i=0; i<bridgeSubscribers.size (); i++) { BridgeInvalidationSubscriptionImpl bridge = (BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i)); if (bridge != exceptSource) bridge.bridgedInvalidate (groupName, key, this.DEFAULT_TO_ASYNCHRONOUS_MODE); } } protected void doBridgedOnlyInvalidation (BridgeInvalidationSubscriptionImpl exceptSource, String groupName, Serializable[] keys) { for (int i=0; i<bridgeSubscribers.size (); i++) { BridgeInvalidationSubscriptionImpl bridge = (BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i)); if (bridge != exceptSource) bridge.bridgedInvalidate (groupName, keys, this.DEFAULT_TO_ASYNCHRONOUS_MODE); } } protected void doBridgedOnlyInvalidateAll (BridgeInvalidationSubscriptionImpl exceptSource, String groupName) { for (int i=0; i<bridgeSubscribers.size (); i++) { BridgeInvalidationSubscriptionImpl bridge = (BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i)); if (bridge != exceptSource) bridge.bridgedInvalidateAll (groupName, this.DEFAULT_TO_ASYNCHRONOUS_MODE); } } // this is called when an invalidation occurs in one of the group. Common behaviour // can be groupped here. By default, we simply forward the invalidations to the // available bridges. // protected void localGroupInvalidationEvent (String groupName, Serializable key, boolean asynchronous) { for (int i=0; i<bridgeSubscribers.size (); i++) ((BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i))).bridgedInvalidate (groupName, key, asynchronous); } protected void localGroupInvalidationsEvent (String groupName, Serializable[] keys, boolean asynchronous) { for (int i=0; i<bridgeSubscribers.size (); i++) ((BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i))).bridgedInvalidate (groupName, keys, asynchronous); } protected void localGroupInvalidateAllEvent (String groupName, boolean asynchronous) { for (int i=0; i<bridgeSubscribers.size (); i++) ((BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i))).bridgedInvalidateAll (groupName, asynchronous); } // We warn other groups and the local group (if available) // protected void bridgeGroupInvalidationEvent (BridgeInvalidationSubscriptionImpl source, String groupName, Serializable key) { doBridgedOnlyInvalidation (source, groupName, key); doLocalOnlyInvalidation (groupName, key, this.DEFAULT_TO_ASYNCHRONOUS_MODE); } protected void bridgeGroupInvalidationEvent (BridgeInvalidationSubscriptionImpl source, String groupName, Serializable[] keys) { doBridgedOnlyInvalidation (source, groupName, keys); doLocalOnlyInvalidation (groupName, keys, this.DEFAULT_TO_ASYNCHRONOUS_MODE); } protected void bridgeGroupInvalidateAllEvent (BridgeInvalidationSubscriptionImpl source, String groupName) { doBridgedOnlyInvalidateAll (source, groupName); doLocalOnlyInvalidateAll (groupName, this.DEFAULT_TO_ASYNCHRONOUS_MODE); } protected void crossDomainBatchInvalidate (BridgeInvalidationSubscriptionImpl source, BatchInvalidation[] invalidations, boolean asynchronous) { if (invalidations == null) return; // local invalidation first // for (int i=0; i<invalidations.length; i++) { BatchInvalidation currInvalid = invalidations[i]; doLocalOnlyInvalidations (currInvalid.getInvalidationGroupName (), currInvalid.getIds (), asynchronous); } // bridged invalidation next // for (int i=0; i<bridgeSubscribers.size (); i++) { BridgeInvalidationSubscriptionImpl bridge = (BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i)); if (bridge != source) bridge.bridgedBatchInvalidations (invalidations, asynchronous); } } protected void crossDomainInvalidateAll(BridgeInvalidationSubscriptionImpl source, String groupName, boolean asynchronous) { // local invalidation first // doLocalOnlyInvalidateAll(groupName, asynchronous); // bridged invalidation next // for (int i=0; i<bridgeSubscribers.size (); i++) { BridgeInvalidationSubscriptionImpl bridge = (BridgeInvalidationSubscriptionImpl)(bridgeSubscribers.elementAt (i)); if (bridge != source) bridge.bridgedInvalidateAll(groupName, asynchronous); } } // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- /** * This class implements the InvalidationGroup interface. It represent the * meeting point of caches and invaliders for a same group. */ class InvalidationGroupImpl implements InvalidationGroup, javax.management.DynamicMBean { protected org.jboss.logging.Logger igLog = null; protected String groupName = null; protected boolean asynchronous = DEFAULT_TO_ASYNCHRONOUS_MODE; protected HashSet registered = new HashSet(); protected int counter = 0; public int hashCode () { return groupName.hashCode (); } public String getGroupName () { return this.groupName; } public InvalidationGroupImpl (String groupName) { this.groupName = groupName; // JBAS-3924 String escapedClass = getClass().getName().replace('$', '.'); this.igLog = org.jboss.logging.Logger.getLogger(escapedClass + "." + groupName); } public InvalidationManagerMBean getInvalidationManager () { return InvalidationManager.this; } public void invalidate (Serializable key) { this.invalidate (key, this.asynchronous); } public void invalidate (Serializable key, boolean asynchronous) { localOnlyInvalidate (key, asynchronous); localGroupInvalidationEvent (this.groupName, key, asynchronous); } public void invalidate (Serializable[] keys) { this.invalidate (keys, this.asynchronous); } public void invalidate (Serializable[] keys, boolean asynchronous) { localOnlyInvalidate (keys, asynchronous); localGroupInvalidationsEvent (this.groupName, keys, asynchronous); } public void invalidateAll() { invalidateAll(asynchronous); } public void invalidateAll(boolean asynchronous) { localOnlyInvalidateAll(); localGroupInvalidateAllEvent(groupName, asynchronous); } public synchronized void register (Invalidatable newRegistered) { // we make a temp copy to avoid concurrency issues with the invalidate method // HashSet newlyRegistered = new HashSet (this.registered); newlyRegistered.add (newRegistered); this.registered = newlyRegistered; } public synchronized void unregister (Invalidatable oldRegistered) { // we make a temp copy to avoid concurrency issues with the invalidate method // HashSet newlyRegistered = new HashSet(this.registered); newlyRegistered.remove (oldRegistered); this.registered = newlyRegistered; this.removeReference (); } public void setAsynchronousInvalidation (boolean async) { this.asynchronous = async; } public boolean getAsynchronousInvalidation () { return this.asynchronous; } public void addReference () { counter++; igLog.debug ("Counter reference value (++): " + counter); } public int getReferenceCount () { return this.counter; } public void removeReference () { counter--; igLog.debug ("Counter reference value (--): " + counter); if (counter<=0) { removeGroup (this.groupName); //Iterator iter = this.registered.iterator (); //while (iter.hasNext ()) // ((Invalidatable)iter.next ()).groupIsDropped () ; } } // DynamicMBean implementation ---------------------------------------------- public Object getAttribute (String attribute) throws javax.management.AttributeNotFoundException, javax.management.MBeanException, javax.management.ReflectionException { if (attribute == null || attribute.equals ("")) throw new IllegalArgumentException ("null or empty attribute name"); if (attribute.equals ("AsynchronousInvalidation")) return new Boolean(this.asynchronous); else throw new javax.management.AttributeNotFoundException(attribute + " is not a known attribute"); } public javax.management.AttributeList getAttributes (java.lang.String[] attributes) { return null; } public javax.management.MBeanInfo getMBeanInfo () { MBeanParameterInfo serSimpleParam = new MBeanParameterInfo ( "key", Serializable.class.getName (), "Primary key to be invalidated" ); MBeanParameterInfo serArrayParam = new MBeanParameterInfo ( "keys", Serializable[].class .getName (), "Primary keys to be invalidated" ); MBeanParameterInfo asynchParam = new MBeanParameterInfo ( "asynchronous", Boolean.class.getName (), "Indicates if the invalidation should be asynchronous or must be synchronous" ); javax.management.MBeanAttributeInfo[] attrInfo = new javax.management.MBeanAttributeInfo[] { new javax.management.MBeanAttributeInfo("AsynchronousInvalidation", Boolean.class.getName(), "Indicates if invalidation, by default, should be done asynchronously", true, true, false)}; MBeanOperationInfo[] opInfo = { new MBeanOperationInfo("invalidate", "invalidate a single key using default (a)synchronous behaviour", new MBeanParameterInfo[] {serSimpleParam}, void.class.getName(), MBeanOperationInfo.ACTION), new MBeanOperationInfo("invalidate", "invalidate a single key indicating the (a)synchronous behaviour", new MBeanParameterInfo[] {serSimpleParam, asynchParam}, void.class.getName(), MBeanOperationInfo.ACTION), new MBeanOperationInfo("invalidate", "invalidate multiple keys using default (a)synchronous behaviour", new MBeanParameterInfo[] {serArrayParam}, void.class.getName(), MBeanOperationInfo.ACTION), new MBeanOperationInfo("invalidate", "invalidate multiple keys indicating the (a)synchronous behaviour", new MBeanParameterInfo[] {serArrayParam, asynchParam}, void.class.getName(), MBeanOperationInfo.ACTION), new MBeanOperationInfo("invalidateAll", "invalidate all keys using default (a)synchronous behaviour", new MBeanParameterInfo[] {}, void.class.getName(), MBeanOperationInfo.ACTION), new MBeanOperationInfo("invalidateAll", "invalidate all keys with specified (a)synchronous behaviour", new MBeanParameterInfo[] {asynchParam}, void.class.getName(), MBeanOperationInfo.ACTION) }; javax.management.MBeanNotificationInfo[] notifyInfo = null; javax.management.MBeanConstructorInfo[] ctorInfo = new javax.management.MBeanConstructorInfo[] {}; return new javax.management.MBeanInfo(getClass().getName(), "Cache invalidation for group named " + this.groupName, attrInfo, ctorInfo, opInfo, notifyInfo); } public java.lang.Object invoke (java.lang.String actionName, java.lang.Object[] params, java.lang.String[] signature) throws javax.management.MBeanException, javax.management.ReflectionException { if ("invalidate".equals (actionName)) { if (params.length == 1) { if (params[0] instanceof Serializable[]) this.invalidate ((Serializable[])params[0]); else if (params[0] instanceof Serializable) this.invalidate ((Serializable)params[0]); else throw new IllegalArgumentException ("First argument must be Serializable (or array of)"); } else if (params.length == 2) { if (params[0] instanceof Serializable[]) this.invalidate ((Serializable[])params[0], ((Boolean)params[1]).booleanValue ()); else if (params[0] instanceof Serializable) this.invalidate ((Serializable)params[0], ((Boolean)params[1]).booleanValue ()); else throw new IllegalArgumentException ("First argument must be Serializable (or array of)"); } else { throw new IllegalArgumentException ("Unknown operation with these parameters: " + actionName); } } else if("invalidateAll".equals(actionName)) { if(params == null || params.length == 0) { this.invalidateAll(); } else if (params.length == 1) { this.invalidateAll (((Boolean)params[1]).booleanValue ()); } else { throw new IllegalArgumentException ("invalidateAll can take zero or one parameter but got " + params.length); } } else { throw new IllegalArgumentException ("Unknown operation: " + actionName); } return null; } public void setAttribute (javax.management.Attribute attribute) throws javax.management.AttributeNotFoundException, javax.management.InvalidAttributeValueException, javax.management.MBeanException, javax.management.ReflectionException { String attrName = attribute.getName(); if (attrName == null || attrName.equals ("")) throw new IllegalArgumentException ("null or empty attribute name"); if (attrName.equals ("AsynchronousInvalidation")) { Object value = attribute.getValue (); if (value instanceof Boolean) this.asynchronous = ((Boolean)value).booleanValue (); else throw new javax.management.InvalidAttributeValueException("Attribute is of boolean type"); } else throw new javax.management.AttributeNotFoundException(attrName + " is not a known attribute"); } public javax.management.AttributeList setAttributes (javax.management.AttributeList attributes) { return null; } // Protected ------------------------------------------------------------------------ protected void localOnlyInvalidate (Serializable[] keys, boolean asynchronous) { java.util.Iterator iter = this.registered.iterator (); while (iter.hasNext ()) { Invalidatable inv = (Invalidatable)iter.next (); inv.areInvalid (keys); } } protected void localOnlyInvalidate (Serializable key, boolean asynchronous) { java.util.Iterator iter = this.registered.iterator (); while (iter.hasNext ()) { Invalidatable inv = (Invalidatable)iter.next (); inv.isInvalid (key); } } protected void localOnlyInvalidateAll() { java.util.Iterator iter = this.registered.iterator (); while (iter.hasNext ()) { Invalidatable inv = (Invalidatable)iter.next (); inv.invalidateAll(); } } } // *******************************************************************************************33 // *******************************************************************************************33 // *******************************************************************************************33 class BridgeInvalidationSubscriptionImpl implements BridgeInvalidationSubscription { protected InvalidationBridgeListener listener = null; public BridgeInvalidationSubscriptionImpl (InvalidationBridgeListener listener) { this.listener = listener; } public void invalidate (String invalidationGroupName, Serializable key) { bridgeGroupInvalidationEvent (this, invalidationGroupName, key); } public void invalidate (String invalidationGroupName, Serializable[] keys) { bridgeGroupInvalidationEvent (this, invalidationGroupName, keys); } public void invalidateAll(String groupName) { bridgeGroupInvalidateAllEvent(this, groupName); } public void batchInvalidate (BatchInvalidation[] invalidations) { crossDomainBatchInvalidate (this, invalidations, DEFAULT_TO_ASYNCHRONOUS_MODE); } public void unregister () { unregisterBridgeListener (this); } // Internal callbacks // protected void bridgedInvalidate (String invalidationGroupName, Serializable key, boolean asynchronous) { this.listener.invalidate (invalidationGroupName, key, asynchronous); } protected void bridgedInvalidate (String invalidationGroupName, Serializable[] keys, boolean asynchronous) { this.listener.invalidate (invalidationGroupName, keys, asynchronous); } protected void bridgedInvalidateAll (String invalidationGroupName, boolean asynchronous) { this.listener.invalidateAll (invalidationGroupName, asynchronous); } protected void bridgedBatchInvalidations (BatchInvalidation[] invalidations, boolean asynchronous) { this.listener.batchInvalidate (invalidations, asynchronous); } protected void groupCreated (String invalidationGroupName) { this.listener.newGroupCreated (invalidationGroupName); } protected void groupDropped (String invalidationGroupName) { this.listener.groupIsDropped (invalidationGroupName); } } }