/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.sun.jini.outrigger; import java.util.Set; import java.util.Iterator; import java.util.Vector; /** * <code>TemplateHandle</code> associates one or more * <code>TransitionWatcher</code>s with a template. * Unless otherwise noted all methods are thread safe. */ class TemplateHandle extends BaseHandle { /** * A cache of the <code>EntryHandleTmplDesc</code> indexed * by the number of fields. */ final private Vector descs = new Vector(); /** * The watchers. We use a <code>HashSet</code> because we will * probably do a fair number of removals for each traversal and * the number of watchers managed by one <code>TemplateHandle</code> * will probably never get very large. If this does become an * issue making <code>TransitionWatcher</code> extend * <code>FastList.Node</code> and using a <code>FastList</code> * here would probably be a good choice (though that would require * changing <code>FastList</code> to support overlapping traversals * of different lists from the same thread.) */ final private Set watchers = new java.util.HashSet(); /** * The <code>WatchersForTemplateClass</code> this object * belongs to. */ final private WatchersForTemplateClass owner; /** * Create a handle for the template <code>tmpl</code>. */ TemplateHandle(EntryRep tmpl, WatchersForTemplateClass owner) { super(tmpl); this.owner = owner; } /** * Return the description for the given field count. */ //!! Since the mask/hash algorithm tops out at a certain number of fields, //!! we could avoid some overhead by topping out at the same count. EntryHandleTmplDesc descFor(int numFields) { /* Since setSize can truncate, test and set need to be atomic. * Hold the lock after setting the size so don't calculate * a given desc more than once (though that is only an optimization) */ synchronized (descs) { // Make sure descs is big enough if (numFields >= descs.size()) descs.setSize(numFields + 1); // Do we have a cached value? EntryHandleTmplDesc desc = (EntryHandleTmplDesc)descs.elementAt(numFields); if (desc == null) { // None in cache, calculate one desc = EntryHandle.descFor(rep(), numFields); descs.setElementAt(desc, numFields); } return desc; } } /** * Return <code>true</code> if this template matches the given entry. */ boolean matches(EntryRep entry) { return rep().matches(entry); } /** * Add a watcher to this handle. Assumes that the handle has not * been removed from its <code>FastList</code> and that * the caller owns the lock on <code>this</code>. * @param watcher the watcher to be added. * @throws NullPointerException if watcher is <code>null</code>. */ void addTransitionWatcher(TransitionWatcher watcher) { assert Thread.holdsLock(this) : "addTransitionWatcher() called without lock"; if (watcher == null) throw new NullPointerException("Watcher can not be null"); assert !removed() : "Added watcher to a removed TemplateHandle"; watchers.add(watcher); } /** * Remote a watcher from this handle. Does nothing * if the specified watcher is not associated with * this <code>TemplateHandle</code>. * @param watcher the watcher to be removed. * @throws NullPointerException if watcher is <code>null</code>. */ synchronized void removeTransitionWatcher(TransitionWatcher watcher) { if (watcher == null) throw new NullPointerException("Watcher can not be null"); watchers.remove(watcher); } /** * Iterate over the watchers associated with * this handle calling <code>isInterested</code> on each * and if it returns <code>true</code> adding the watcher to the * passed set. * * @param set The set to accumulate interested watchers * into. * @param transition The transition being processed. * @param ordinal The ordinal associated with <code>transition</code>. * @throws NullPointerException if either argument is <code>null</code>. */ synchronized void collectInterested(Set set, EntryTransition transition, long ordinal) { final Iterator i = watchers.iterator(); while (i.hasNext()) { final TransitionWatcher w = (TransitionWatcher)i.next(); if (w.isInterested(transition, ordinal)) { set.add(w); } } } /** * Return the <code>OutriggerServerImpl</code> this * handle is part of. * @return The <code>OutriggerServerImpl</code> this * handle is part of. */ OutriggerServerImpl getServer() { return owner.getServer(); } /** * Visit each <code>TransitionWatcher</code> and check to see if * it has expired, removing it if it has. * @param now an estimate of the current time expressed as * milliseconds since the beginning of the epoch. */ void reap(long now) { /* This could take a while, instead of blocking all other * access, clone the contents of watchers and * iterate down the clone (we don't do this too often and * watchers should never be that big so a shallow copy * should not be that bad. If it does get bad may * need to switch to a FastList for watchers. * (we don't do this for collection of interested watchers * because calls to isInterested() are designed to be cheap, * calls to removeIfExpired() will grab locks and could * write to disk)) */ final TransitionWatcher content[]; synchronized (this) { content = new TransitionWatcher[watchers.size()]; watchers.toArray(content); } for (int i=0; i<content.length; i++) { content[i].removeIfExpired(now); } } /** * Return <code>true</code> if there are no watchers associated * with this object and <code>false</code> otherwise. Assumes * the call holds the lock on <code>this</code>. * @return <code>true</code> if there are no watchers in this handle. */ boolean isEmpty() { assert Thread.holdsLock(this) : "isEmpty() called without lock"; return watchers.isEmpty(); } }