/*
* Copyright to the original author or authors.
*
* 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.rioproject.impl.service;
import com.sun.jini.landlord.LeasedResource;
import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.id.Uuid;
import org.rioproject.impl.util.TimeConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
* Abstract class to manage the service's leased resources.
*
* @author Dennis Reedy
*/
@SuppressWarnings("unused")
public abstract class ResourceLessor {
/** A hash of resources to cookies */
private final Map<Uuid, LeasedResource> resources = new ConcurrentHashMap<Uuid, LeasedResource>();
/** A Thread which will clean up stale leases */
private Thread reaper = null;
private long reapingInterval = TimeConstants.ONE_SECOND*10;
/** A LinkedList of LeaseListener objects */
private final List<LeaseListener> listeners = new LinkedList<LeaseListener>();
/** Component for getting the Logger */
private static final String COMPONENT_NAME = ResourceLessor.class.getPackage().getName();
/** The Logger */
private static final Logger logger = LoggerFactory.getLogger(ResourceLessor.class);
/**
* Check to make sure that the LeasedResource lease has not expired yet <br>
*
* @param resource The LeasedResource
*
* @return Returns true if the lease on the passed resource has not expired
* yet
*/
public boolean ensure(final LeasedResource resource) {
return(resource.getExpiration() > currentTime());
}
public void setReapingInterval(long reapingInterval) {
this.reapingInterval = reapingInterval;
}
/**
* Create a new lease <br>
*
* @param resource to be leased
* @param duration Time requested for <code>Lease</code>
*
* @throws LeaseDeniedException If the lease has been denied
* @return A new Lease
*/
public abstract Lease newLease(LeasedResource resource, long duration) throws LeaseDeniedException;
/**
* Remove a leased resource from the list of managed leases. <br>
*
* @param resource ServiceResource to remove
*
* @return true if the lease was removed
*/
public boolean remove(final LeasedResource resource) {
return (remove(resource.getCookie()));
}
/**
* Remove all leased resources .<br>
*/
public void removeAll() {
LeasedResource[] resources = getLeasedResources();
for (LeasedResource resource : resources) {
remove(resource.getCookie());
}
}
/**
* Remove a leased resource from the list of managed leases. <br>
*
* @param cookie Object to remove
*
* @return boolean True if removed false if not removed
*/
public boolean remove(final Uuid cookie) {
LeasedResource resource;
boolean removed = false;
synchronized(resources) {
resource = resources.remove(cookie);
}
if(resource != null) {
notifyLeaseRemoval(resource);
removed = true;
}
return (removed);
}
/**
* Add a LeaseListener <br>
*
* @param listener the LeaseListener to add
*/
public void addLeaseListener(final LeaseListener listener) {
listeners.add(listener);
}
/**
* Remove a LeaseListener <br>
*
* @param listener the LeaseListener to remove
*/
public void removeLeaseListener(final LeaseListener listener) {
listeners.remove(listener);
}
/**
* @return the total number of service resources that have been leased
*/
public int total() {
int size;
synchronized(resources) {
size = resources.size();
}
return (size);
}
/**
* Add a LeasedResource for a new Lease or renewing a Lease
*
* @param resource The resource to add or update
*
* @throws IllegalArgumentException if the resource is null
*/
public void addLeasedResource(final LeasedResource resource) {
if(resource == null)
throw new IllegalArgumentException("resource is null");
synchronized(this) {
if(reaper==null) {
reaper = new LeaseReaper(reapingInterval);
reaper.setDaemon(true);
reaper.start();
}
}
synchronized(resources) {
resources.put(resource.getCookie(), resource);
}
}
/**
* Get a LeasedResource
*
* @param cookie The Uuid of the LeasedResource to get
*
* @return The LeasedResource for the Uuid
*
* @throws IllegalArgumentException if the cookie is null
*/
public LeasedResource getLeasedResource(final Uuid cookie) {
if(cookie == null)
throw new IllegalArgumentException("cookie is null");
LeasedResource resource;
synchronized(resources) {
resource = resources.get(cookie);
}
return(resource);
}
/**
* This method returns a snapshot of the LeasedResource objects that
* this ResourceLessor is managing
*
* @return An array of LeasedResource elements. If no LeasedResource
* objects are found return a zero-length array. A new array is returned
* each time
*/
public LeasedResource[] getLeasedResources() {
LeasedResource[] leasedResources;
synchronized(resources) {
Collection<LeasedResource> c = resources.values();
leasedResources = c.toArray(new LeasedResource[c.size()]);
}
if(logger.isTraceEnabled())
logger.trace("Number of resources: {}, returned: {}", resources.size(), leasedResources.length);
return (leasedResources);
}
/**
* Notify LeaseListener instances of a new registration
*
* @param resource The LeasedResource
*/
public void notifyLeaseRegistration(final LeasedResource resource) {
for (LeaseListener listener : listeners)
listener.register(resource);
}
/**
* Notify LeaseListener instances of a lease renewal
*
* @param resource The LeasedResource
*/
public void notifyLeaseRenewal(final LeasedResource resource) {
for (LeaseListener listener : listeners)
listener.renewed(resource);
}
/**
* Notify LeaseListener instances of a lease expiration
*
* @param resource The LeasedResource
*/
public void notifyLeaseExpiration(final LeasedResource resource) {
for (LeaseListener listener : listeners)
listener.expired(resource);
}
/**
* Notify LeaseListener instances of a lease removal
*
* @param resource The LeasedResource
*/
public void notifyLeaseRemoval(final LeasedResource resource) {
for (LeaseListener listener : listeners)
listener.removed(resource);
}
/**
* Stop and clean up all resources
*/
public void stop() {
if(reaper!=null)
reaper.interrupt();
reaper = null;
removeAll();
}
/**
* Method that provides some notion of the current time in milliseconds
* since the beginning of the epoch. Default implementation calls
* System.currentTimeMillis()
*
* @return The current time
*/
public long currentTime() {
return (System.currentTimeMillis());
}
/**
* Clean up leases that have not been renewed. Check every 30 seconds for
* stale leases
*/
protected class LeaseReaper extends Thread {
private final long reapingInterval;
public LeaseReaper(long reapingInterval) {
super("LeaseReaper");
this.reapingInterval = reapingInterval;
}
public void run() {
while (reaper != null || !isInterrupted()) {
try {
Thread.sleep(reapingInterval);
} catch(InterruptedException e) {
break;
}
Set<Entry<Uuid,LeasedResource>> mapEntries;
synchronized(resources) {
mapEntries = resources.entrySet();
}
for (Entry<Uuid, LeasedResource> entry : mapEntries) {
LeasedResource lr = entry.getValue();
if (!ensure(lr)) {
if (logger.isDebugEnabled())
logger.debug("Lease expired for resource {}, cookie {}",
((ServiceResource) lr).getResource().toString(), lr.getCookie());
remove(lr.getCookie());
notifyLeaseExpiration(lr);
}
}
}
}
}
}