package org.jacorb.notification.filter; /* * JacORB - a free Java ORB * * Copyright (C) 1999-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import org.jacorb.config.*; import org.slf4j.Logger; import org.jacorb.notification.AbstractMessage; import org.jacorb.notification.EventTypeWrapper; import org.jacorb.notification.MessageFactory; import org.jacorb.notification.conf.Attributes; import org.jacorb.notification.conf.Default; import org.jacorb.notification.interfaces.Disposable; import org.jacorb.notification.interfaces.EvaluationContextFactory; import org.jacorb.notification.interfaces.GCDisposable; import org.jacorb.notification.interfaces.JMXManageable; import org.jacorb.notification.interfaces.Message; import org.jacorb.notification.lifecycle.IServantLifecyle; import org.jacorb.notification.lifecycle.ServantLifecyleControl; import org.jacorb.notification.util.DisposableManager; import org.jacorb.notification.util.LogUtil; import org.jacorb.notification.util.WildcardMap; import org.jacorb.util.ObjectUtil; import org.omg.CORBA.Any; import org.omg.CosNotification.EventType; import org.omg.CosNotification.Property; import org.omg.CosNotification.StructuredEvent; import org.omg.CosNotifyComm.NotifySubscribe; import org.omg.CosNotifyFilter.ConstraintExp; import org.omg.CosNotifyFilter.ConstraintInfo; import org.omg.CosNotifyFilter.ConstraintNotFound; import org.omg.CosNotifyFilter.FilterOperations; import org.omg.CosNotifyFilter.FilterPOATie; import org.omg.CosNotifyFilter.InvalidConstraint; import org.omg.CosNotifyFilter.UnsupportedFilterableData; import org.omg.PortableServer.POA; import org.omg.PortableServer.Servant; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * The Filter interface defines the behaviors supported by objects which encapsulate constraints * used by the proxy objects associated with an event channel in order to determine which events * they receive will be forwarded, and which will be discarded. Each object supporting the Filter * interface can encapsulate a sequence of any number of constraints. Each event received by a proxy * object which has one or more objects supporting the Filter interface associated with it must * satisfy at least one of the constraints associated with one of its associated Filter objects in * order to be forwarded (either to another proxy object or to the consumer, depending on the type * of proxy the filter is associated with), otherwise it will be discarded. <br> * Each constraint encapsulated by a filter object is a structure comprised of two main components. * The first component is a sequence of data structures, each of which indicates an event type * comprised of a domain and a type name. The second component is a boolean expression over the * properties of an event, expressed in some constraint grammar (more on this below). For a given * constraint, the sequence of event type structures in the first component nominates a set of event * types to which the constraint expression in the second component applies. Each element of the * sequence can contain strings which will be matched for equality against the domain_name and * type_name fields of each event being evaluated by the filter object, or it could contain strings * with wildcard symbols (*), indicating a pattern match should be performed against the type * contained in each event, rather than a comparison for equality when determining if the boolean * expression should be applied to the event, or the event should simply be discarded without even * attempting to apply the boolean expression. Note that an empty sequence included as the first * component of a constraint implies that the associated expression applies to all types of events, * as does a sequence comprised of a single element whose domain and type name are both set to * either the empty string or else the wildcard symbol alone contained in quotes. <br> * The constraint expressions associated with a particular object supporting the Filter interface * are expressed as strings which obey the syntax of a particular constraint grammar (i.e., a BNF). * Every conformant implementation of this service must support constraint expressions expressed in * the default constraint grammar described in Section 2.4, "The Default Filter Constraint * Language," on page 2-23. In addition, implementations may support other constraint grammars, * and/or users of this service may implement their own filter objects which allow constraints to be * expressed in terms of an alternative constraint grammar. As long as such user-defined filter * objects support the Filter interface, they can be attached to Proxy or Admin objects in the same * fashion as the default Filter objects supported by the implementation of the service are, and the * channel should be able to use them to filter events in the same fashion. <br> * The Filter interface supports the operations required to manage the constraints associated with * an object instance which supports the interface, along with a readonly attribute which identifies * the particular constraint grammar in which the constraints encapsulated by this object have * meaning. In addition, the Filter interface supports three variants of the match operation which * can be invoked by an associated proxy object upon receipt of an event (the specific variant * selected depends upon whether the event is received in the form of an Any, a Structured Event, or * a Typed Event), to determine if the event should be forwarded or discarded, based on whether or * not the event satisfies at least one criteria encapsulated by the filter object. The Filter * interface also supports operations which enable a client to associate with the target filter * object any number of "callbacks" which are notified each time there is a change to the list of * event types which the constraints encapsulated by the filter object could potentially cause * proxies to which the filter is attached to receive. Operations are also defined to support * administration of this callback list by unique identifier. <br> * * @jmx.mbean name = "Filter" * @jboss.xmbean * * @author Alphonse Bendt * @author John Farrell */ public abstract class AbstractFilter implements GCDisposable, IServantLifecyle, FilterOperations, JMXManageable, AbstractFilterMBean { private static int sCount_ = 0; private final int number_ = ++sCount_; final static RuntimeException NOT_SUPPORTED = new UnsupportedOperationException( "this operation is not supported"); public static final int NO_CONSTRAINTS_MATCH = -2; public static final int CONSTRAINTS_EMPTY = -1; private static final String EMPTY_EVENT_TYPE_CONSTRAINT_KEY = AbstractMessage .calcConstraintKey("*", "*"); // ////////////////////////////////////// private final DisposableManager disposables_ = new DisposableManager(); private final CallbackManager callbackManager_ = new CallbackManager(); /** * contains the associated constraints. as access to constraints_ is controlled by * constraintsLock_ its safe to use unsynchronized HashMap here */ protected final Map constraints_ = new HashMap(); protected final WildcardMap wildcardMap_; protected final ReadWriteLock constraintsLock_; private final AtomicInteger constraintIdPool_ = new AtomicInteger(0); protected final MessageFactory messageFactory_; private final FilterUsageDecorator filterUsageDecorator_; private final POA poa_; private final Logger logger_; private final EvaluationContextFactory evaluationContextFactory_; private final long maxIdleTime_; private final ServantLifecyleControl servantLifecyle_; // ////////////////////////////////////// protected AbstractFilter(Configuration config, EvaluationContextFactory evaluationContextFactory, MessageFactory messageFactory, POA poa) throws ConfigurationException { super(); poa_ = poa; logger_ = LogUtil.getLogger(config, getClass().getName()); if (logger_.isInfoEnabled()) { logger_.info("Created filter for Grammar: " + constraint_grammar()); } messageFactory_ = messageFactory; evaluationContextFactory_ = evaluationContextFactory; constraintsLock_ = new ReentrantReadWriteLock(); wildcardMap_ = newWildcardMap(config); disposables_.addDisposable(callbackManager_); filterUsageDecorator_ = new FilterUsageDecorator(this); maxIdleTime_ = config.getAttributeAsLong(Attributes.DEAD_FILTER_INTERVAL, Default.DEFAULT_DEAD_FILTER_INTERVAL); servantLifecyle_ = new ServantLifecyleControl(this, config); } public final Servant newServant() { return new FilterPOATie(filterUsageDecorator_.getFilterOperations()); } public final POA getPOA() { return poa_; } private WildcardMap newWildcardMap(Configuration config) throws ConfigurationException { String wildcardMapImpl = config.getAttribute(Attributes.WILDCARDMAP_CLASS, Default.DEFAULT_WILDCARDMAP_IMPL); try { Class wildcardMapClazz = ObjectUtil.classForName(wildcardMapImpl); Constructor ctor = wildcardMapClazz.getConstructor(new Class[0]); return (WildcardMap) ctor.newInstance(new Object[0]); } catch (ClassNotFoundException e) { // ignore } catch (IllegalArgumentException e) { // ignore } catch (InstantiationException e) { // ignore } catch (IllegalAccessException e) { // ignore } catch (InvocationTargetException e) { // ignore } catch (SecurityException e) { // ignore } catch (NoSuchMethodException e) { // ignore } throw new ConfigurationException(wildcardMapImpl + " is no valid WildcardMap Implementation"); } public final org.omg.CORBA.Object activate() { return servantLifecyle_.activate(); } public final void deactivate() { servantLifecyle_.deactivate(); } protected int newConstraintId() { return constraintIdPool_.getAndIncrement(); } /** * The <code>add_constraints</code> operation is invoked by a client in order to associate one * or more new constraints with the target filter object. The operation accepts as input a * sequence of constraint data structures, each element of which consists of a sequence of event * type structures (described in Section 3.2.1, "The Filter Interface," on page 3-14) and a * constraint expressed within the constraint grammar supported by the target object. Upon * processing each constraint, the target object associates a numeric identifier with the * constraint that is unique among all constraints it encapsulates. If any of the constraints in * the input sequence is not a valid expression within the supported constraint grammar, the * InvalidConstraint exception is raised. This exception contains as data the specific * constraint expression that was determined to be invalid. Upon successful processing of all * input constraint expressions, the <code>add_constraints</code> operation returns a sequence * in which each element will be a structure including one of the input constraint expressions, * along with the unique identifier assigned to it by the target filter object. <br> * Note that the semantics of the <code>add_constraints</code> operation are such that its * sideeffects are performed atomically upon the target filter object. Once * <code>add_constraints</code> is invoked by a client, the target filter object is * temporarily disabled from usage by any proxy object it may be associated with. The operation * is then carried out, either successfully adding all of the input constraints to the target * object or none of them (in the case one of the input expressions was invalid). Upon * completion of the operation, the target filter object is effectively re-enabled and can once * again be used by associated filter objects in order to make event forwarding decisions. */ public ConstraintInfo[] add_constraints(ConstraintExp[] constraintExp) throws InvalidConstraint { final FilterConstraint[] _arrayFilterConstraint = newFilterConstraints(constraintExp); // access writeonly lock constraintsLock_.writeLock().lock(); try { return add_constraint(constraintExp, _arrayFilterConstraint); } finally { // give up the lock constraintsLock_.writeLock().unlock(); } } private ConstraintInfo[] add_constraint(ConstraintExp[] constraintExp, FilterConstraint[] filterConstraints) { final ConstraintInfo[] _arrayConstraintInfo = new ConstraintInfo[filterConstraints.length]; for (int _x = 0; _x < constraintExp.length; _x++) { int _constraintId = newConstraintId(); _arrayConstraintInfo[_x] = new ConstraintInfo(constraintExp[_x], _constraintId); ConstraintEntry _entry = new ConstraintEntry(filterConstraints[_x], _arrayConstraintInfo[_x]); addEventTypeMappingsForConstraint(_entry); constraints_.put(new Integer(_constraintId), _entry); notifyCallbacks(); } return _arrayConstraintInfo; } private void addEventTypeMappingsForConstraint(ConstraintEntry entry) { int _eventTypeCount = entry.getEventTypeCount(); if (_eventTypeCount == 0) { addConstraintEntryToWildcardMap(EMPTY_EVENT_TYPE_CONSTRAINT_KEY, entry); } else { for (int _y = 0; _y < _eventTypeCount; ++_y) { EventTypeWrapper _eventTypeWrapper = entry.getEventTypeWrapper(_y); addConstraintEntryToWildcardMap(_eventTypeWrapper.getConstraintKey(), entry); } } } private void addConstraintEntryToWildcardMap(String constraintKey, ConstraintEntry constraintEntry) { List _listOfConstraintEntry = (List) wildcardMap_.getNoExpansion(constraintKey); if (_listOfConstraintEntry == null) { _listOfConstraintEntry = new LinkedList(); wildcardMap_.put(constraintKey, _listOfConstraintEntry); } _listOfConstraintEntry.add(constraintEntry); } private FilterConstraint[] newFilterConstraints(ConstraintExp[] constraintExp) throws InvalidConstraint { FilterConstraint[] _arrayFilterConstraint = new FilterConstraint[constraintExp.length]; // creation of the FilterConstraint's may cause a // InvalidConstraint Exception. Note that the State of the // Filter has not been changed yet. for (int _x = 0; _x < constraintExp.length; _x++) { _arrayFilterConstraint[_x] = newFilterConstraint(constraintExp[_x]); } return _arrayFilterConstraint; } /** * create a new FilterConstraint based on the provided ConstraintExp */ protected abstract FilterConstraint newFilterConstraint(ConstraintExp constraintExp) throws InvalidConstraint; public void modify_constraints(int[] deleteIds, ConstraintInfo[] constraintInfo) throws ConstraintNotFound, InvalidConstraint { // write lock constraintsLock_.writeLock().lock(); try { Integer[] _deleteKeys = checkConstraintsToBeDeleted(deleteIds); FilterConstraint[] _arrayConstraintEvaluator = checkConstraintsToBeModified(constraintInfo); deleteConstraints(_deleteKeys); modifyConstraints(constraintInfo, _arrayConstraintEvaluator); notifyCallbacks(); } finally { constraintsLock_.writeLock().unlock(); } } private void modifyConstraints(ConstraintInfo[] constraintInfo, FilterConstraint[] filterConstraints) { for (int _x = 0; _x < constraintInfo.length; _x++) { Integer _key = new Integer(constraintInfo[_x].constraint_id); ConstraintEntry _updatedEntry = new ConstraintEntry(filterConstraints[_x], constraintInfo[_x]); // overwrite existing entry constraints_.put(_key, _updatedEntry); int _eventTypeCount = _updatedEntry.getEventTypeCount(); for (int _y = 0; _y < _eventTypeCount; ++_y) { EventTypeIdentifier _eventTypeIdentifier = _updatedEntry.getEventTypeWrapper(_y); List _listOfConstraintEvaluator = (List) wildcardMap_ .getNoExpansion(_eventTypeIdentifier.getConstraintKey()); // list should NEVER be null as the constraint is modified and therefor // should have existed before. _listOfConstraintEvaluator.add(_updatedEntry); } } } private FilterConstraint[] checkConstraintsToBeModified(ConstraintInfo[] constraintInfo) throws InvalidConstraint, ConstraintNotFound { FilterConstraint[] _arrayConstraintEvaluator = new FilterConstraint[constraintInfo.length]; for (int _x = 0; _x < constraintInfo.length; ++_x) { if (constraints_.containsKey(new Integer(constraintInfo[_x].constraint_id))) { _arrayConstraintEvaluator[_x] = newFilterConstraint(constraintInfo[_x].constraint_expression); } else { throw new ConstraintNotFound(constraintInfo[_x].constraint_id); } } return _arrayConstraintEvaluator; } private Integer[] checkConstraintsToBeDeleted(int[] idsToBeDeleted) throws ConstraintNotFound { final Integer[] _deleteKeys = new Integer[idsToBeDeleted.length]; for (int _x = 0; _x < idsToBeDeleted.length; ++_x) { _deleteKeys[_x] = new Integer(idsToBeDeleted[_x]); if (!constraints_.containsKey(_deleteKeys[_x])) { throw new ConstraintNotFound(idsToBeDeleted[_x]); } } return _deleteKeys; } private void deleteConstraints(Integer[] keys) { for (int _x = 0; _x < keys.length; ++_x) { ConstraintEntry _deletedEntry = (ConstraintEntry) constraints_.remove(keys[_x]); removeEventTypeMappingForConstraint(keys[_x], _deletedEntry); } } private void removeEventTypeMappingForConstraint(Integer key, ConstraintEntry deletedEntry) { int _eventTypeCount = deletedEntry.getEventTypeCount(); for (int _y = 0; _y < _eventTypeCount; ++_y) { EventTypeIdentifier _eventTypeIdentifier = deletedEntry.getEventTypeWrapper(_y); List _listOfConstraintEvaluator = (List) wildcardMap_ .getNoExpansion(_eventTypeIdentifier.getConstraintKey()); Iterator _i = _listOfConstraintEvaluator.iterator(); // traverse list of wildcard mappings. as a sideeffect mappings for deleted // constraints are removed. while (_i.hasNext()) { ConstraintEntry _constraint = (ConstraintEntry) _i.next(); if (_constraint.getConstraintId() == key.intValue()) { _i.remove(); break; } } } } public ConstraintInfo[] get_constraints(int[] ids) throws ConstraintNotFound { final Lock _lock = constraintsLock_.readLock(); _lock.lock(); try { final ConstraintInfo[] _constraintInfo = new ConstraintInfo[ids.length]; for (int _x = 0; _x < ids.length; ++_x) { Integer _key = new Integer(ids[_x]); if (constraints_.containsKey(_key)) { _constraintInfo[_x] = ((ConstraintEntry) constraints_.get(_key)) .getConstraintInfo(); } else { throw new ConstraintNotFound(ids[_x]); } } return _constraintInfo; } finally { _lock.unlock(); } } public ConstraintInfo[] get_all_constraints() { constraintsLock_.readLock().lock(); try { ConstraintInfo[] _constraintInfo = new ConstraintInfo[constraints_.size()]; Iterator _i = constraints_.values().iterator(); for (int i = 0; i < _constraintInfo.length; i++) { _constraintInfo[i] = ((ConstraintEntry) _i.next()).getConstraintInfo(); } return _constraintInfo; } finally { constraintsLock_.readLock().unlock(); } } public void remove_all_constraints() { constraintsLock_.writeLock().lock(); try { constraints_.clear(); wildcardMap_.clear(); notifyCallbacks(); } finally { constraintsLock_.writeLock().unlock(); } } /** * @jmx.managed-operation description = "Destroy this Filter" impact = "ACTION" */ public void destroy() { dispose(); } /** * call and use this Iterator inside a acquired read lock section only. */ private Iterator getConstraintsForEvent(Message event) { String _key = event.getConstraintKey(); return getIterator(_key); } public Iterator getIterator(Object key) { Object[] _entries = wildcardMap_.getWithExpansion(key); return new ConstraintIterator(_entries); } /** * Iterator over an Array of Lists. If a List is depleted this Iterator will switch * transparently to the next available list. */ static private class ConstraintIterator implements Iterator { private final Object[] arrayOfLists_; private Iterator currentIterator_; private int currentListIdx_ = 0; ConstraintIterator(Object[] arrayOfLists) { arrayOfLists_ = arrayOfLists; if (arrayOfLists_.length == 0) { currentIterator_ = null; } else { switchIterator(); } } private void switchIterator() { currentIterator_ = ((List) arrayOfLists_[currentListIdx_]).iterator(); } public boolean hasNext() { return currentIterator_ != null && currentIterator_.hasNext(); } public Object next() { if (currentIterator_ == null) { throw new NoSuchElementException(); } Object _nextValue = currentIterator_.next(); if (!currentIterator_.hasNext() && currentListIdx_ < arrayOfLists_.length - 1) { ++currentListIdx_; switchIterator(); } return _nextValue; } public void remove() { throw NOT_SUPPORTED; } } /** * generic version of the match operation */ private int match_ReadLock(EvaluationContext evaluationContext, Message event) throws UnsupportedFilterableData { constraintsLock_.readLock().lock(); try { return match_NoLock(evaluationContext, event); } finally { constraintsLock_.readLock().unlock(); } } private int match_NoLock(EvaluationContext evaluationContext, Message event) throws UnsupportedFilterableData { if (!constraints_.isEmpty()) { Iterator _entries = getConstraintsForEvent(event); while (_entries.hasNext()) { ConstraintEntry _entry = (ConstraintEntry) _entries.next(); try { boolean _result = _entry.getFilterConstraint().evaluate(evaluationContext, event).getBool(); if (_result) { return _entry.getConstraintId(); } } catch (PropertyDoesNotExistException e) { // non critical exception. ignore // and continue with next Constraint if (logger_.isInfoEnabled()) { logger_.info("tried to access non existing Property: " + e.getMessage()); } else if (logger_.isDebugEnabled()) { logger_.debug("tried to access non existing Property", e); } } catch (EvaluationException e) { logger_.error("Error evaluating filter", e); throw new UnsupportedFilterableData(e.getMessage()); } } return NO_CONSTRAINTS_MATCH; } logger_.info("Filter has no Expressions"); return CONSTRAINTS_EMPTY; } public boolean match(Any anyEvent) throws UnsupportedFilterableData { return match_internal(anyEvent) >= 0; } /** * match Any to associated constraints. return the id of the first matching filter or * NO_CONSTRAINT. */ protected int match_internal(Any anyEvent) throws UnsupportedFilterableData { final EvaluationContext _evaluationContext = evaluationContextFactory_ .newEvaluationContext(); try { final Message _event = messageFactory_.newMessage(anyEvent); try { return match_ReadLock(_evaluationContext, _event); } finally { _event.dispose(); } } finally { _evaluationContext.dispose(); } } public boolean match_structured(StructuredEvent structuredevent) throws UnsupportedFilterableData { return match_structured_internal(structuredevent) >= 0; } /** * match the StructuredEvent to the associated constraints. return the id of the first matching * filter or NO_CONSTRAINT. */ protected int match_structured_internal(StructuredEvent structuredEvent) throws UnsupportedFilterableData { final EvaluationContext _evaluationContext = evaluationContextFactory_ .newEvaluationContext(); try { final Message _event = messageFactory_.newMessage(structuredEvent); try { return match_ReadLock(_evaluationContext, _event); } finally { _event.dispose(); } } finally { _evaluationContext.dispose(); } } /** * match the TypedEvent to the associated constraints. return the id of the first matching * filter or NO_CONSTRAINT. */ protected int match_typed_internal(Property[] typedEvent) throws UnsupportedFilterableData { final EvaluationContext _evaluationContext = evaluationContextFactory_ .newEvaluationContext(); try { final Message _event = messageFactory_.newMessage(typedEvent); try { return match_ReadLock(_evaluationContext, _event); } finally { _event.dispose(); } } finally { _evaluationContext.dispose(); } } public boolean match_typed(Property[] properties) throws UnsupportedFilterableData { return match_typed_internal(properties) >= 0; } public int attach_callback(NotifySubscribe notifySubscribe) { return callbackManager_.attach_callback(notifySubscribe); } public void detach_callback(int id) { callbackManager_.detach_callback(id); } public int[] get_callbacks() { return callbackManager_.get_callbacks(); } private void notifyCallbacks() { final Iterator i = constraints_.keySet().iterator(); final List eventTypes = new ArrayList(); while (i.hasNext()) { Object key = i.next(); ConstraintEntry value = (ConstraintEntry) constraints_.get(key); int ets = value.getEventTypeCount(); for (int j = 0; j < ets; ++j) { EventTypeWrapper et = value.getEventTypeWrapper(j); eventTypes.add(et.getEventType()); } } callbackManager_.replaceWith((EventType[]) eventTypes .toArray(EventTypeWrapper.EMPTY_EVENT_TYPE_ARRAY)); } public void dispose() { deactivate(); disposables_.dispose(); } public void registerDisposable(Disposable disposeHook) { disposables_.addDisposable(disposeHook); } /** * @jmx.managed-attribute description = "last usage = invoke match operation" access = * "read-only" */ public Date getLastUsage() { return filterUsageDecorator_.getLastUsage(); } /** * @jmx.managed-attribute description = "date this filter was created" access = "read-only" */ public Date getCreationDate() { return filterUsageDecorator_.getCreationDate(); } /** * @jmx.managed-attribute description = "number of match invocations on this filter" access = * "read-only" */ public long getMatchCount() { return filterUsageDecorator_.getMatchCount(); } /** * @jmx.managed-attribute description = "number of match_structured invocations on this filter" * access = "read-only" */ public long getMatchStructuredCount() { return filterUsageDecorator_.getMatchStructuredCount(); } /** * @jmx.managed-attribute description = "number of match_typed invocations on this filter" * access = "read-only" */ public long getMatchTypedCount() { return filterUsageDecorator_.getMatchTypedCount(); } /** * @jmx.managed-operation description = "List all Constraints" impact = "INFO" */ public String listContraints() { StringBuffer buffer = new StringBuffer(); Iterator i = constraints_.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); ConstraintEntry _constraintEntry = (ConstraintEntry) entry.getValue(); _constraintEntry.appendToBuffer(buffer); } return buffer.toString(); } public final String getJMXObjectName() { return "type=filter, number=" + number_ + ", grammar=" + constraint_grammar(); } public String[] getJMXNotificationTypes() { return new String[0]; } public void setJMXCallback(JMXCallback callback) { // ignored } public void attemptDispose() { attemptDispose(this, getLastUsage(), maxIdleTime_); } static void attemptDispose(Disposable disposable, Date lastUsage, long maxIdleTime) { if (maxIdleTime <= 0) { return; } if (lastUsage.getTime() + maxIdleTime < System.currentTimeMillis()) { disposable.dispose(); } } }