/* * 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.config.Config; import com.sun.jini.landlord.*; import net.jini.config.Configuration; import net.jini.config.ConfigurationException; import net.jini.core.lease.Lease; import net.jini.core.lease.LeaseDeniedException; import net.jini.core.lease.UnknownLeaseException; import net.jini.export.Exporter; import net.jini.id.ReferentUuid; import net.jini.id.Uuid; import net.jini.id.UuidFactory; import net.jini.security.TrustVerifier; import net.jini.security.proxytrust.ServerProxyTrust; import org.rioproject.impl.config.ExporterConfig; import org.rioproject.impl.util.TimeConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.rmi.RemoteException; import java.util.HashMap; import java.util.Map; /** * The LandlordLessor manages leased resources using the Landlord protocol. * * <p> * The LandlordLessor supports the following configuration entries; where each * configuration entry name is associated with the component name <span *="" * style="font-family: monospace;">org.rioproject.resources.servicecore</span> * <br> * </p> * <ul> * <li><span style="font-weight: bold;">landlordExporter </span> <table * cellpadding="2" *="" cellspacing="2" border="0" style="text-align: left; * width: 100%;"> <tbody> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Type:</td> * <td style="vertical-align: top;">{@link net.jini.export.Exporter}</td> * </tr> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Default:</td> * <td style="vertical-align: top;">A new {@link net.jini.jeri.BasicJeriExporter} with * <ul> * <li>a {@link net.jini.jeri.tcp.TcpServerEndpoint} created on a random port, * </li> * <li>a {@link net.jini.jeri.BasicILFactory}, </li> * <li>distributed garbage collection turned off, </li> * <li>keep alive on. </li> * </ul> * </td> * </tr> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Description:</td> * <td style="vertical-align: top;">Specifies the Exporter to use to export * this service. This entry is obtained at service start and restart.</td> * </tr> * </tbody> </table></li> * </ul> * * * * <ul> * <li><span style="font-weight: bold;">reapingInterval</span> <table * cellpadding="2" *="" cellspacing="2" border="0" style="text-align: left; * width: 100%;"> <tbody> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Type:</td> * <td style="vertical-align: top;">long</td> * </tr> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Default:</td> * <td style="vertical-align: top;">10 seconds * <br> * </td> * </tr> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Description:</td> * <td style="vertical-align: top;">The amount of time (in milliseconds) to check for expired leases. * Must be a positive long value.</td> * </tr> * </tbody> </table> * </li> * </ul> * * * <ul> * <li><span style="font-weight: bold;">landlordLeasePeriodPolicy </span> <table * cellpadding="2" *="" cellspacing="2" border="0" style="text-align: left; * width: 100%;"> <tbody> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Type:</td> * <td style="vertical-align: top;">{@link com.sun.jini.landlord.LeasePeriodPolicy}</td> * </tr> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Default:</td> * <td style="vertical-align: top;">A new * {@link com.sun.jini.landlord.FixedLeasePeriodPolicy} that allows * leases up to one day, and grants one hour leases for duration requests of * {@link net.jini.core.lease.Lease#ANY}</code> * <br> * </td> * </tr> * <tr> * <td style="vertical-align: top; text-align: right; font-weight: bold;">Description:</td> * <td style="vertical-align: top;">Policy used to determine the length of * initial grants and renewals of the leases on entries. Obtained at service * start and restart.</td> * </tr> * </tbody> </table> * </li> * </ul> * * @author Dennis Reedy */ public class LandlordLessor extends ResourceLessor implements Landlord, ReferentUuid, ServerProxyTrust { /** The default time for a Lease: 1 hour */ public static final long DEFAULT_LEASE_TIME = TimeConstants.ONE_HOUR; /** The maximum time for a Lease: 1 day */ public static final long DEFAULT_MAX_LEASE_TIME = TimeConstants.ONE_DAY; /** This LandlordLessor's uuid */ private final Uuid uuid; /** The Remote Landlord */ private final Landlord landlord; /** The Exporter for the Landlord */ private Exporter exporter; /** Factory we use to create leases */ private final LeaseFactory leaseFactory; /** LeasePolicy */ private LeasePeriodPolicy leasePolicy; /** Component for reading configuration entries and getting the Logger */ private static final String COMPONENT = LandlordLessor.class.getPackage().getName(); private static Logger logger = LoggerFactory.getLogger(COMPONENT); /** * Create a LandlordLessor * * @param config The Configuration object used to initialize operational * values. * * @throws RemoteException if errors occur setting up infrastructure */ public LandlordLessor(final Configuration config) throws RemoteException { this(config, null); } /** * Create a LandlordLessor * * @param config The Configuration object used to initialize operational * values. * @param leasePolicy A LeasePeriodPolicy object to be used for the * LandlordLessor. * * @throws RemoteException if errors occur setting up infrastructure */ public LandlordLessor(final Configuration config, final LeasePeriodPolicy leasePolicy) throws RemoteException { super(); if (config == null) throw new IllegalArgumentException("config is null"); /* Get the LeasePeriodPolicy */ final LeasePeriodPolicy defaultLeasePeriodPolicy = (leasePolicy==null? new FixedLeasePeriodPolicy(DEFAULT_MAX_LEASE_TIME, DEFAULT_LEASE_TIME): leasePolicy); try { this.leasePolicy = (LeasePeriodPolicy)Config.getNonNullEntry(config, COMPONENT, "landlordLeasePeriodPolicy", LeasePeriodPolicy.class, defaultLeasePeriodPolicy); } catch (ConfigurationException e) { logger.warn("Getting LeasePeriodPolicy in LandlordLessor", e); } try { final long reapingInterval = Config.getLongEntry(config, COMPONENT, "reapingInterval", TimeConstants.ONE_SECOND*10, 1, Long.MAX_VALUE); setReapingInterval(reapingInterval); } catch (ConfigurationException e) { logger.warn("Getting reapingInterval in LandlordLessor", e); } /* Create the default Exporter */ try { exporter = ExporterConfig.getExporter(config, COMPONENT, "landlordExporter"); } catch (ConfigurationException e) { logger.warn("Getting Exporter in LandlordLessor", e); } landlord = (Landlord) exporter.export(this); uuid = UuidFactory.generate(); leaseFactory = new LeaseFactory(landlord, uuid); } /** * Stop the LandlordLessor * * @param force if true, unexports the LandlordLessor even if there are * pending or in-progress calls; if false, only unexports the LandlordLessor * if there are no pending or in-progress calls * * @return True or false if the unexport was successful */ public boolean stop(final boolean force) { super.stop(); boolean unexported = false; try { unexported = exporter.unexport(force); } catch (Exception e) { if(logger.isTraceEnabled()) { logger.trace("Unexporting LandlordLessor", e); } } return unexported; } /** * Concrete implementation of parent class * * @see ResourceLessor#newLease */ public Lease newLease(final LeasedResource resource, final long duration) throws LeaseDeniedException { LeasePeriodPolicy.Result leasePeriod = leasePolicy.grant(resource, duration); Lease lease = leaseFactory.newLease(resource.getCookie(), leasePeriod.expiration); resource.setExpiration(leasePeriod.expiration); addLeasedResource(resource); notifyLeaseRegistration(resource); return lease; } /** * Called by the lease when its <code>renew</code> method is called. <br> * * @param cookie Associated with the lease when it was created * @param extension The duration argument passed to the * <code>Lease.renew()</code> call * @return The new duration the lease should have */ public long renew(final Uuid cookie, final long extension) throws LeaseDeniedException, UnknownLeaseException { LeasedResource resource = getLeasedResource(cookie); long granted; if (resource == null) throw new UnknownLeaseException("No lease for cookie: " + cookie); long now = System.currentTimeMillis(); if (resource.getExpiration() <= now) { UnknownLeaseException e = new UnknownLeaseException("Lease has already expired"); if(logger.isTraceEnabled()) { logger.trace("Lease has already expired by [{}] milliseconds, [{}] seconds", (now-resource.getExpiration()), ((now-resource.getExpiration())/1000)); } throw e; } LeasePeriodPolicy.Result leasePeriod = leasePolicy.renew(resource, extension); resource.setExpiration(leasePeriod.expiration); granted = leasePeriod.duration; addLeasedResource(resource); notifyLeaseRenewal(resource); return granted; } /** * Called by the lease map when its <code>renewAll</code> method is * called. * * @param cookie Associated with each lease when it was created <br> * @param extension The duration argument for each lease from the map * @return The results of the renew */ public Landlord.RenewResults renewAll(final Uuid[] cookie, final long[] extension) { int size = cookie.length; long[] granted = new long[size]; Exception[] denied = new Exception[size]; for (int i = 0; i < size; i++) { try { granted[i] = renew(cookie[i], extension[i]); denied[i] = null; } catch (Exception e) { denied[i] = e; } } return new Landlord.RenewResults(granted, denied); } /** * Called by the lease when its <code>cancel</code> method is called. <br> * * @param cookie Associated with the lease when it was created */ public void cancel(final Uuid cookie) throws UnknownLeaseException { if (!remove(cookie)) throw new UnknownLeaseException("No lease for cookie: " + cookie); } /** * Called by the lease map when its <code>cancelAll</code> method is * called. * * @param cookies Associated with the lease when it was created <br> */ public Map<Uuid, Exception> cancelAll(final Uuid[] cookies) { Map<Uuid, Exception> exceptionMap = null; for (Uuid cookie : cookies) { try { cancel(cookie); } catch (Exception e) { if (exceptionMap == null) { exceptionMap = new HashMap<Uuid, Exception>(); } exceptionMap.put(cookie, e); } } /* * If all the leases specified in the cookies could be cancelled return * null. Otherwise, return a Map that for each failed cancel attempt * maps the corresponding cookie object to an exception describing the * failure. */ return exceptionMap; } public TrustVerifier getProxyVerifier() throws RemoteException { return(new LandlordProxyVerifier(landlord, uuid)); } /** * Return the <code>Uuid</code> that has been assigned to the resource this * proxy represents. * * @return the <code>Uuid</code> associated with the resource this proxy * represents. Will not return <code>null</code>. */ public Uuid getReferentUuid() { return(uuid); } }