/* * Copyright 2008 the original author or authors. * Copyright 2005 Sun Microsystems, Inc. * * 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.sla; import com.sun.jini.proxy.BasicProxyTrustVerifier; import net.jini.export.Exporter; import net.jini.jeri.BasicILFactory; import net.jini.jeri.BasicJeriExporter; import net.jini.jeri.tcp.TcpServerEndpoint; import net.jini.security.TrustVerifier; import net.jini.security.proxytrust.ServerProxyTrust; import org.rioproject.impl.config.ExporterConfig; import org.rioproject.servicebean.ServiceBeanContext; import org.rioproject.deploy.ServiceBeanInstance; import org.rioproject.deploy.ServiceProvisionListener; import org.rioproject.event.EventHandler; import org.rioproject.opstring.ServiceElement; import org.rioproject.sla.SLA; import org.rioproject.watch.Calculable; import org.rioproject.impl.watch.ThresholdManager; import org.rioproject.watch.ThresholdType; import org.rioproject.watch.ThresholdValues; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.rmi.RemoteException; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * The RelocationPolicyHandler will inform the OperationalStringManager to * relocate the ServiceBean that uses this policy handler. The * RelocationPolicyHandler will look for attributes set that can control it's * operational behavior. * <p> * The <span style="font-family: monospace;">RelocationPolicyHandler </span> * supports the following configuration entries; where each configuration entry * name is associated with the component name <span style="font-family: * monospace;">relocationPolicyHandler </span> <br> * * <ul> * <li><span style="font-weight: bold; font-family: courier * new,courier,monospace;">provisionListenerExporter </span> <br * style="font-family: courier new,courier,monospace;"> <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: <br> * </td> * <td style="vertical-align: top;">Exporter</td> * </tr> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Default: <br> * </td> * <td style="vertical-align: top;">A new <code>BasicJeriExporter</code> with * <ul> * <li>a <code>TcpServerEndpoint</code> created on a random port,</li> * <li>a <code>BasicILFactory</code>,</li> * <li>distributed garbage collection turned off,</li> * <li>keep alive on.</li> * </ul> * <code></code></td> * </tr> * <tr><td style="vertical-align: top; text-align: right; font-weight: * bold;">Description: <br> * </td> * <td style="vertical-align: top;">The Exporter used to export the * ProvisionListener server. A new exporter is obtained every time a * RelocationPolicyHandler needs to export itself.</td> * </tr> * </tbody> </table></li> * </ul> * <span style="font-weight: bold; font-family: courier new,courier,monospace;"> * <br> * </span> * * @author Dennis Reedy */ public class RelocationPolicyHandler extends SLAPolicyHandler implements ServiceProvisionListener, ServerProxyTrust { /** The description of the SLA Handler */ private static final String description = "Relocation Policy Handler"; /** Dampening value for upper thresholds being crossed */ private long upperThresholdDampeningTime; /** Dampening value for lower thresholds being crossed */ private long lowerThresholdDampeningTime; /** The Timer to use for scheduling a relocation task */ private Timer taskTimer; /** The RelocationTask for incrementing */ private RelocationTask relocationTask; /** Action that indicates an relocation request is pending */ public static final String RELOCATION_PENDING = "RELOCATION_PENDING"; /** Action that indicates an relocation request has failed */ public static final String RELOCATION_FAILURE = "RELOCATION_FAILURE"; /** * Action that indicates an relocation request has succeeded. The resultant * Object in the SLAPolicyEvent will be the proxy of the new service */ public static final String RELOCATION_SUCCEEDED = "RELOCATION_SUCCEEDED"; /** The remote ref (e.g. stub or dynamic proxy) */ private Object ourRemoteRef; /** The Exporter */ private Exporter exporter; /** Component name */ private static final String CONFIG_COMPONENT = "relocationPolicyHandler"; /** Logger for this component */ static Logger logger = LoggerFactory.getLogger("org.rioproject.sla"); /** * Construct a RelocationPolicyHandler * * @param sla The SLA for the RelocationPolicyHandler */ public RelocationPolicyHandler(SLA sla) { super(sla); taskTimer = new Timer(true); } /** * Override parent's method to return description for this SLA Handler * * @return The descriptive attribute for this SLA Handler */ @Override public String getDescription() { return (description); } /** * Override parent's method to export the object to the RMI runtime */ @Override public void setThresholdManager(ThresholdManager thresholdManager) { if(ourRemoteRef==null) exportDo(); super.setThresholdManager(thresholdManager); } /** * Override parent's method to unexport */ @Override public void disconnect() { try { exporter.unexport(true); } catch(IllegalStateException e) { if(logger.isTraceEnabled()) logger.trace("RelocationPolicyHandler unexport failed", e); } ourRemoteRef = null; if(taskTimer != null) taskTimer.cancel(); super.disconnect(); } /** * Override parent's initialize method to initialize operational attributes */ @Override public void initialize(Object eventSource, EventHandler eventHandler, ServiceBeanContext context) { super.initialize(eventSource, eventHandler, context); if(exporter==null) { try { exporter = ExporterConfig.getExporter(getConfiguration(), CONFIG_COMPONENT, "provisionListenerExporter"); } catch(Exception e) { logger.warn("Getting provisionListenerExporter, use default", e); } /* If we still dont have an exporter create a default one */ if(exporter==null) { exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0), new BasicILFactory(), false, true); } } try { upperThresholdDampeningTime = (getSLA().getUpperThresholdDampeningTime()==0?1000: getSLA().getUpperThresholdDampeningTime()); lowerThresholdDampeningTime = (getSLA().getLowerThresholdDampeningTime()==0?1000: getSLA().getLowerThresholdDampeningTime()); if(logger.isDebugEnabled()) { logger.debug("["+context.getServiceElement().getName()+"] "+ "RelocationPolicyHandler ["+getID()+"]: properties, "+ "low Threshold="+getSLA().getLowThreshold()+", "+ "high Threshold="+getSLA().getHighThreshold()+", "+ "upperThresholdDampeningTime="+upperThresholdDampeningTime+", "+ "lowerThresholdDampeningTime="+lowerThresholdDampeningTime); } } catch(Exception e) { logger.error("Getting Operational Configuration", e); } } /** * @see org.rioproject.impl.watch.ThresholdListener#notify */ @Override public void notify(Calculable calculable, ThresholdValues thresholdValues, ThresholdType type) { if(logger.isDebugEnabled()) { String status = type.name().toLowerCase(); logger.debug("RelocationPolicyHandler [" + getID() + "]: Threshold [" + calculable.getId() + "] " + status + " value [" + calculable.getValue() + "\n] low [" + thresholdValues.getCurrentLowThreshold() + "]" + " high [" + thresholdValues.getCurrentHighThreshold() + "]"); } if(type == ThresholdType.BREACHED) { double tValue = calculable.getValue(); if(tValue > thresholdValues.getCurrentHighThreshold()) { fireRelocation(upperThresholdDampeningTime, "upper"); } else { fireRelocation(lowerThresholdDampeningTime, "lower"); } /* Threshold has been cleared */ } else { if(relocationTask!=null) { relocationTask.cancel(); relocationTask = null; } } sendSLAThresholdEvent(calculable, thresholdValues, type); } /* * Determine whether a RelocationTask should be created based on the * dampening value provided, or to relocate immediately */ void fireRelocation(long dampener, String type) { if(dampener > 0) { relocationTask = new RelocationTask(); long now = System.currentTimeMillis(); if(logger.isDebugEnabled()) logger.debug("["+context.getServiceElement().getName()+"] "+ "RelocationPolicyHandler ["+getID()+"]: "+ "Schedule relocation task in "+ "["+dampener+"] millis"); try { taskTimer.schedule(relocationTask, new Date(now+ dampener)); } catch(IllegalStateException e) { logger.warn("Force disconnect of "+ "["+context.getServiceElement().getName()+"] "+ "RelocationPolicyHandler", e); disconnect(); } } else { if(logger.isDebugEnabled()) logger.debug("["+context.getServiceElement().getName()+"] "+ "RelocationPolicyHandler ["+getID()+"]: "+ "no "+type+" dampener, perform relocation"); doRelocate(); } } /** * @see org.rioproject.deploy.ServiceProvisionListener#succeeded */ public void succeeded(ServiceBeanInstance jsbInstance) throws RemoteException { try { notifyListeners(new SLAPolicyEvent(this, getSLA(), RELOCATION_SUCCEEDED, jsbInstance.getService())); } catch(Exception e) { logger.warn("Getting service to create SLAPolicyEvent", e); } } /** * @see org.rioproject.deploy.ServiceProvisionListener#failed */ public void failed(ServiceElement sElem, boolean resubmitted) throws RemoteException { notifyListeners(new SLAPolicyEvent(this, getSLA(), RELOCATION_FAILURE)); } /** * Returns a <code>TrustVerifier</code> which can be used to verify that a * given proxy to this policy handler can be trusted */ public TrustVerifier getProxyVerifier() { if(ourRemoteRef==null) exportDo(); return (new BasicProxyTrustVerifier(ourRemoteRef)); } /** * Export the RelocationPolicyHandler */ private void exportDo() { try { ourRemoteRef = exporter.export(this); } catch(RemoteException e) { logger.error( "Exporting RelocationPolicyHandler ["+getID()+"]", e); } } /** * Perform the relocation */ void doRelocate() { notifyListeners(new SLAPolicyEvent(this, getSLA(), RELOCATION_PENDING)); try { if(ourRemoteRef == null) exportDo(); context.getServiceBeanManager().relocate( (ServiceProvisionListener)ourRemoteRef, null); } catch(Exception e) { if(!logger.isTraceEnabled()) { logger.warn("Attempt to invoke relocate method on " + "ProvisionManager ["+ e.getClass().getName()+" : "+ e.getLocalizedMessage()+"]"); } else { logger.warn( "Attempt to invoke relocate method on ProvisionManager", e); } notifyListeners(new SLAPolicyEvent(this, getSLA(), RELOCATION_FAILURE)); } } /** * The RelocationTask is used to schedule a relocation be performed at * some time in the future. */ class RelocationTask extends TimerTask { public void run() { doRelocate(); } } }