/* * 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.mahalo; import java.util.Map; import java.rmi.RemoteException; import java.lang.ref.WeakReference; import net.jini.core.lease.UnknownLeaseException; import net.jini.id.Uuid; import com.sun.jini.thread.WakeupManager; import com.sun.jini.collection.WeakTable; import com.sun.jini.landlord.LeasedResource; /** * Lease Mgr implementation that aggressively expires leases as they * expiration times occur. Synchronizes on resource before canceling it. * * @author Sun Microsystems, Inc. * * @see com.sun.jini.mahalo.LeaseManager */ class LeaseExpirationMgr implements LeaseManager, WeakTable.KeyGCHandler { /** * Interface that allows LeaseExpirationMgr to expire resources. * This is the same as the * <code>com.sun.jini.landlord.Landlord.cancel()<code> method less * the <code>RemoteException</code> in the throws clause. Mixing * this interface into a <code>Landlord</code> implementation * allows the <code>LeaseExpirationMgr</code> to cancel leases * without having to deal with <code>RemoteException</code>. */ static interface Expirer { /** * Called by a LeaseExpirationMgr when it needs to expire a * resource. The value of the <code>cookie</code> parameter is * obtained from <code>getCookie()</code> method of the * <code>LeasedResource</code> being expired. */ public void cancel(Uuid cookie) throws UnknownLeaseException; } // Map of resources to tickets private WeakTable ticketMap = new WeakTable(this); private Expirer landlord; private WakeupManager expirationQueue = new WakeupManager(new WakeupManager.ThreadDesc(null, true)); /** * Create a <code>LeaseExpirationMgr</code> to aggressively expire * the leases of the passed landlord (implementing * <code>Expirer</code> is trivial for a <code>Landlord</code>. */ LeaseExpirationMgr(Expirer landlord) { this.landlord = landlord; } /** * Terminate the <code>LeaseExpirationMgr</code>, killing any * threads it has started */ void terminate() { expirationQueue.stop(); expirationQueue.cancelAll(); } // purposefully inherit doc comment from supertype public void register(LeasedResource resource) { schedule(resource); } // purposefully inherit doc comment from supertype public void renewed(LeasedResource resource) { // Remove the old event expirationQueue.cancel( (WakeupManager.Ticket)ticketMap.remove(resource) ); // Schedule the new event schedule(resource); } /** * Schedule a leased resource to be reaped in the future. Called * when a resource gets a lease, or a lease is renewed. */ private void schedule(LeasedResource resource) { final WakeupManager.Ticket ticket = expirationQueue.schedule(resource.getExpiration(), new Canceler(resource)); ticketMap.getOrAdd(resource, ticket); } // purposefully inherit doc comment from supertype // Called when LeaseResource we are tracking is garbage collected public void keyGC(Object value) { final WakeupManager.Ticket ticket = (WakeupManager.Ticket)value; expirationQueue.cancel(ticket); } /** * Objects that do the actually cancel the resource in question, stuck * in <code>WakeupManager</code> */ private class Canceler implements Runnable { private final WeakReference resourceRef; /** * Create a <code>Canceler</code> for the passed resource */ Canceler(LeasedResource resource) { resourceRef = new WeakReference(resource); } /** * Check the associated resource's expiration against the * current time, canceling the resource if its time has * passed. Synchronize on the resource before checking the * expiration time. */ public void run() { final LeasedResource resource = (LeasedResource)resourceRef.get(); if (resource == null) // Already gone return; synchronized (resource) { if (resource.getExpiration() <= System.currentTimeMillis()) { try { ticketMap.remove(resource); landlord.cancel(resource.getCookie()); } catch (UnknownLeaseException e) { // Don't care, probably already gone } } // else Someone must have just renewed the resource, // don't need to re-register since that will be done // by the renewer } } } }