Package org.rioproject.impl.sla

Source Code of org.rioproject.impl.sla.ScalingPolicyHandler$ScalingTask

/*
* 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.servicebean.ServiceElementChangeListener;
import org.rioproject.deploy.ServiceBeanInstance;
import org.rioproject.deploy.ServiceProvisionListener;
import org.rioproject.event.EventHandler;
import org.rioproject.opstring.OperationalStringManager;
import org.rioproject.opstring.ServiceBeanConfig;
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.NoSuchObjectException;
import java.rmi.RemoteException;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
* The ScalingPolicyHandler will increment and optionally decrement instances of
* the ServiceBean it is associated to based on limits set for the SLA. The
* ScalingPolicyHandler will look for attributes set that can control it's
* operational behavior,.
* <p>
* The ScalingPolicyHandler supports the following configuration entries; where
* each configuration entry name is associated with the component name
* <code>scalingPolicyHandler</code><br>

* <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
* ScalablePolicyHandler needs to export itself.</td>
* </tr>
* </tbody> </table></li>
* </ul>
*
* @author Dennis Reedy
*/
public class ScalingPolicyHandler extends SLAPolicyHandler implements ServiceProvisionListener, ServerProxyTrust {
    /** The description of the SLA Handler */
    private static final String description = "Scaling Policy Handler";
    /** Action that indicates an increment request is pending */
    public static final String INCREMENT_PENDING = "INCREMENT_PENDING";
    /** Action that indicates an increment request has failed */
    public static final String INCREMENT_FAILURE = "INCREMENT_FAILURE";
    /** Action that indicates an increment request has succeeded. The resultant
     * Object in the SLAPolicyEvent will be the proxy of the new service */
    public static final String INCREMENT_SUCCEEDED = "INCREMENT_SUCCEEDED";
    /** Action that indicates that a decrement command with destroy set to true
     * has been sent */
    public static final String DECREMENT_DESTROY_SENT = "DECREMENT_DESTROY_SENT";
    /** Action that indicates that a decrement command failed to be sent */
    public static final String DECREMENT_FAILED = "DECREMENT_FAILED";
    /** The total number of services the ScalingPolicyHandler is aware of */
    private int totalServices;
    /** Flag that indicates we have requested to decrement (and destroy) ourself */
    private boolean haveDecremented = false;
    /**
     * The maximum number of services to increment. If the value is -1, then no limit has been set
     */
    protected int maxServices;
    /**
     * The minimum number of services that are to exist on the network, defaults to 1
     */
    private int minServices = 1;
    /** The ServiceElement */
    private ServiceElement sElem;
    /** 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 an increment or decrement task */
    private Timer taskTimer;
    /** The ScalingTask for incrementing */
    private ScalingTask incrementTask;
    /** The ScalingTask for decrementing */
    private ScalingTask decrementTask;
    /** The remote ref (e.g. stub or dynamic proxy) */
    private Object ourRemoteRef;
    /** The Exporter */
    private Exporter exporter;
    /** A ServiceElementChangeListener to listen for ServiceElement changes */
    private ServiceElementChangeManager svcElementListener;
    /** The number of pending provision requests */
    private int pendingRequests;
    /** Flag to indicate whether this policy handler is 'connected' */
    private boolean connected;
    /** The last calculable */
    protected Calculable lastCalculable;
    /** The last ThresholdValue */
    protected ThresholdValues lastThresholdValues;
    private final Object serviceElementLock = new Object();
    /** Component name */
    private static final String CONFIG_COMPONENT = "scalingPolicyHandler";
    /** A Logger for this component */
    static Logger logger = LoggerFactory.getLogger(ScalingPolicyHandler.class);

    /**
     * Construct a ScalingPolicyHandler
     *
     * @param sla The SLA for the ScalingPolicyHandler
     */
    public ScalingPolicyHandler(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 the object from the RMI runtime and
     * perform cleanup tasks
     */
    @Override
    public void disconnect() {
        if(svcElementListener!=null && context!=null)
            context.getServiceBeanManager().removeListener(svcElementListener);
        try {
            exporter.unexport(true);
        } catch(IllegalStateException e) {
            logger.trace("ScalingPolicyHandler unexport failed", e);
        }
        if(taskTimer != null)
            taskTimer.cancel();
        connected = false;
        super.disconnect();
    }

    /*
     * Override parent's setSLA method to initialize operational attributes
     */
    @Override
    public void setSLA(SLA sla) {
        boolean firstTime = getSLA()==null;
        super.setSLA(sla);
        if(!firstTime)
            initProperties(true);
    }
   
    /*
     * Override parent's initialize method to initialize operational attributes
     */
    @Override
    public void initialize(Object eventSource, EventHandler eventHandler, ServiceBeanContext context) {

        if(context==null)
            throw new IllegalArgumentException("context is null");
        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 don't have an exporter create a default one */
            if(exporter==null) {
                exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0),
                                                 new BasicILFactory(),
                                                 false,
                                                 true);
            }
        }

        boolean update = (this.context==null);
        this.context = context;
        sElem = context.getServiceElement();
        if(svcElementListener==null)
            svcElementListener = new ServiceElementChangeManager();
        context.getServiceBeanManager().addListener(svcElementListener);
        initProperties(update);
        connected = true;
        try {
            totalServices = getTotalKnownServices();
        } catch(Throwable t) {
            logger.warn("[{}] ScalingPolicyHandler [{}]: Discovering peer environment", getName(), getID(), t);
        }
    }     
   
    /*
     * Set the ServiceElement
     */
    private void setServiceElement(ServiceElement newElem) {
        synchronized(serviceElementLock) {
            sElem = newElem;
            //minServices = sElem.getPlanned();
            if(logger.isTraceEnabled()) {
                try {
                    totalServices = getTotalKnownServices();
                } catch(Exception e) {
                    logger.warn("[{}] ScalingPolicyHandler [{}]: Discovering peer environment", getName(), getID(),e);
                }
                logger.trace("[{}] ScalingPolicyHandler [{}]: planned [{}], totalServices [{}], minServices [{}], maxServices [{}]",
                             getName(), getID(), sElem.getPlanned(), totalServices, minServices, maxServices);
            }
        }
    }
   
    /*
     * Get the ServiceElement
     */
    protected ServiceElement getServiceElement() {
        ServiceElement elem;
        synchronized(serviceElementLock) {
            elem = sElem;
        }
        return(elem);
    }

    /*
     * Initialize properties from the SLA
     */
    private void initProperties(boolean update) {
        try {
            maxServices = getSLA().getMaxServices();

            if(!update) {
                minServices = context.getServiceElement().getPlanned();
                Integer ips = (Integer)context.getServiceBeanConfig().getConfigurationParameters().get(ServiceBeanConfig.INITIAL_PLANNED_SERVICES);
                if(ips != null)
                    minServices = ips;
            }

            upperThresholdDampeningTime =
                (getSLA().getUpperThresholdDampeningTime()==0?1000: getSLA().getUpperThresholdDampeningTime());

            lowerThresholdDampeningTime =
                (getSLA().getLowerThresholdDampeningTime()==0?1000:getSLA().getLowerThresholdDampeningTime());

            if(logger.isDebugEnabled()) {
                StringBuilder buffer = new StringBuilder();
                buffer.append("[").append(getName()).append("] ");
                buffer.append(update?"UPDATED ":"INIT ");
                buffer.append("ScalingPolicyHandler [").append(getID()).append("] properties: ");
                buffer.append("low Threshold=").append(getSLA().getLowThreshold()).append(", ");
                buffer.append("high Threshold=").append(getSLA().getHighThreshold()).append(", ");
                buffer.append("maxServices=").append(maxServices).append(", ");
                buffer.append("minServices=").append(minServices).append(", ");
                buffer.append("upperThresholdDampeningTime=").append(upperThresholdDampeningTime).append(", ");
                buffer.append("lowerThresholdDampeningTime=").append(lowerThresholdDampeningTime);
                logger.debug(buffer.toString());
            }
        } catch(Exception e) {
            logger.warn("Getting Operational Configuration", e);
        }
    }

    /**
     * @see org.rioproject.impl.watch.ThresholdListener#notify
     */
    @Override
    public void notify(Calculable calculable, ThresholdValues thresholdValues, ThresholdType type) {
        if(!connected) {
            logger.debug("[{}] ScalingPolicyHandler [{}]: has been disconnected", getName(), getID());
            return;
        }
        lastCalculable = calculable;
        lastThresholdValues = thresholdValues;
        String status = type.name().toLowerCase();
        logger.info("[{}] ScalingPolicyHandler [{}]: Threshold [{}] {} value [{}] low [{}] high [{}]",
                    getName(), getID(), calculable.getId(), status, calculable.getValue(),
                    thresholdValues.getLowThreshold(), thresholdValues.getHighThreshold());

        if(context.getServiceBeanManager().getOperationalStringManager()==null) {
            logger.warn("[{}] [{}]: OperationalStringManager, unable to process event", getName(), getID());
            return;
        }
        if(type == ThresholdType.BREACHED) {
            double tValue = calculable.getValue();
            try {
                totalServices = getTotalKnownServices();
            } catch(Exception e) {
                logger.warn("[{}] ScalingPolicyHandler [{}]: Getting instance count", getName(), getID(), e);
            }
            if(tValue > thresholdValues.getHighThreshold()) {
                boolean increment = false;
                if(maxServices == SLA.UNDEFINED) {
                    logger.debug("[{}] ScalingPolicyHandler [{}]: Unknown MaxServices number, choose to increment",
                                 getName(), getID());
                    increment = true;
                } else {
                    /*
                     * The planned attribute changes as services scale up,
                     * get the latest value for comparision
                     */
                    int planned = getServiceElement().getPlanned();
                    logger.debug("[{}] ScalingPolicyHandler [{}]: planned [{}], totalServices [{}], maxServices [{}]",
                                 getName(), getID(), planned, totalServices, maxServices);
                                                           
                    if(maxServices > totalServices &&
                       totalServices <= planned && maxServices > planned)
                        increment = true;
                    else {
                        logger.debug("[{}] ScalingPolicyHandler [{}]: MaxServices [{}] reached, do not increment",
                                     getName(), getID(), maxServices);
                    }
                }
                if(increment) {
                    if(incrementTask != null) {
                        logger.debug("[{}] ScalingPolicyHandler [{}]: Breached notification, already have an " +
                                     "increment task scheduled. Send SLAThresholdEvent and return",
                                     getName(), getID());
                        sendSLAThresholdEvent(calculable, thresholdValues, type);
                        return;
                    }
                    /* Cancel scheduled decrements */
                    cancelDecrementTask();
                    if(upperThresholdDampeningTime > 0) {
                        incrementTask = new ScalingTask(true);
                        long now = System.currentTimeMillis();
                        logger.debug("[{}] ScalingPolicyHandler [{}]: Schedule increment task in [{}] millis",
                                     getName(), getID(), upperThresholdDampeningTime);
                        try {
                            taskTimer.schedule(incrementTask, new Date(now+upperThresholdDampeningTime));
                        } catch (IllegalStateException e) {
                            logger.warn("Force disconnect of [{}] ScalingPolicyHandler {}: {}",
                                        getName(), e.getClass().getName(), e.getMessage());
                            disconnect();   
                        }
                    } else {
                        logger.debug("[{}] ScalingPolicyHandler [{}]: no upper dampener, perform increment", getName(), getID());
                        doIncrement();
                    }
                }
            } else {
                if(decrementTask != null) {
                    logger.debug("[{}] ScalingPolicyHandler [{}]: Breached notification, already have an " +
                                 "decrement task scheduled. Send SLAThresholdEvent and return", getName(), getID());
                    sendSLAThresholdEvent(calculable, thresholdValues, type);
                    return;
                }
                if(totalServices > minServices) {
                    /* cancel scheduled increments */
                    cancelIncrementTask();
                    if(lowerThresholdDampeningTime > 0) {
                        scheduleDecrement();                       
                    } else {
                        logger.debug("[{}] ScalingPolicyHandler [{}]: no lower dampener, perform decrement",
                                     getName(), getID());
                        doDecrement();
                    }
                } else {
                    logger.debug("[{}] ScalingPolicyHandler [{}]: MinServices [{}] reached, Total services [{}] do not decrement",
                                 getName(), getID(), minServices, totalServices);
                }
            }

        /* Threshold has been CLEARED */
        } else {
            cancelIncrementTask();
            cancelDecrementTask();
           
            /* If we have pending increment tasks, trim them up. Make sure however
             * that we are not trimming pending requests that will drop the
             * minServices and/or maintain value below what has been declared */
            OperationalStringManager opMgr = context.getServiceBeanManager().
                                                     getOperationalStringManager();           
            int pendingCount = getPendingRequestCount(opMgr);
            logger.trace("[{}] ScalingPolicyHandler [{}] totalServices={}, pendingCount={}, pendingRequests={}, planned={}",
                         getName(), getID(), totalServices, pendingCount, pendingRequests, getServiceElement().getPlanned());
            if(((totalServices+pendingCount)+pendingRequests) >
               getServiceElement().getPlanned()) {
                try {
                    int numTrimmed = opMgr.trim(getServiceElement(), pendingRequests);
                    logger.trace("[{}] numTrimmed={}", getID(), numTrimmed);
                } catch(NoSuchObjectException e) {
                    logger.warn("Remote manager decomissioned for [{}] ScalingPolicyHandler [{}], force disconnect",
                                getName(), getID());
                    disconnect();
                } catch(Exception e) {
                    logger.warn("[{}] Trimming Pending Requests", getID(), e);
                }
            }
           
        }
        sendSLAThresholdEvent(calculable, thresholdValues, type);
    }

    private void cancelIncrementTask() {
        if(incrementTask != null) {
            logger.debug("[{}] ScalingPolicyHandler [{}]: cancel increment task", getName(), getID());
            incrementTask.cancel();
            incrementTask = null;
        }
    }

    private void cancelDecrementTask() {
        if(decrementTask != null) {
            logger.debug("[{}] ScalingPolicyHandler [{}]: cancel decrement task", getName(), getID());
            decrementTask.cancel();
            decrementTask = null;
        }
    }

    /**
     * @see org.rioproject.deploy.ServiceProvisionListener#succeeded
     */
    public void succeeded(ServiceBeanInstance jsbInstance) {
        try {
            pendingRequests--;
            notifyListeners(new SLAPolicyEvent(this, getSLA(), INCREMENT_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) {
        if(!resubmitted)
            pendingRequests--;
        notifyListeners(new SLAPolicyEvent(this, getSLA(), INCREMENT_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));
    }

    /*
     * Get the number of pending requests from the OperationalStringManager
     */
    protected int getPendingRequestCount(OperationalStringManager opMgr) {
        int pendingCount = 0;
        try {           
            opMgr.getClass().getMethod("getPendingCount", ServiceElement.class);
            ServiceElement elem = getServiceElement();
            pendingCount = opMgr.getPendingCount(elem);
        } catch (NoSuchObjectException e) {          
            logger.warn("Remote manager decomissioned for [{}] ScalingPolicyHandler [{}], force disconnect",
                        getName(), getID());
            disconnect();
        } catch (Throwable t) {
            logger.warn("Using pre-3.2 {}, pending count not available", opMgr.getClass().getName());
        }   
        return(pendingCount);
    }

    protected int getTotalKnownServices() throws Exception {
        OperationalStringManager opMgr = context.getServiceBeanManager().getOperationalStringManager();
        if(opMgr == null)
            throw new Exception("OperationalStringManager is null");
        ServiceBeanInstance[] instances =opMgr.getServiceBeanInstances(getServiceElement());
        return (instances.length);
    }

    /**
     * Do the increment
     */
    protected void doIncrement() {
        if(!(//lastType == ThresholdEvent.BREACHED &&
             (lastCalculable.getValue() > lastThresholdValues.getHighThreshold()))) {
            logger.debug("[{}] ScalingPolicyHandler [{}]: INCREMENT CANCELLED, operating below High Threshold, value [{}] high [{}]",
                         getName(), getID(), lastCalculable.getValue(), lastThresholdValues.getHighThreshold());
            return;
        }
        try {
            OperationalStringManager opMgr = context.getServiceBeanManager().getOperationalStringManager();
            if(opMgr==null) {
                logger.debug("[{}] No OperationalStringManager, increment aborted", getName());
                return;
            }
            ServiceElement elem = getServiceElement();
            ServiceBeanInstance[] instances = opMgr.getServiceBeanInstances(elem);
            int pendingCount = getPendingRequestCount(opMgr);                      
            int realTotal = instances.length+pendingCount;
           
            /* If we have an unbounded maxServices property, always increment.
             * Otherwise check values to determine if incrementing is needed */
            boolean increment = false;           
            if(maxServices==SLA.UNDEFINED) {
                increment = true;
            } else if(maxServices > realTotal &&
                realTotal <= getServiceElement().getPlanned() &&
                maxServices > getServiceElement().getPlanned()) {                   
                increment = true;
            }
           
            if(increment) {
                logger.debug("[{}] Current instance count=[{}], Current pending count=[{}], "+
                             "Planned=[{}], MaxServices=[{}], ScalingPolicyHandler [{}]: INCREMENT_PENDING",
                             getName(), instances.length, pendingCount, getServiceElement().getPlanned(),
                             (maxServices==SLA.UNDEFINED? "undefined": Integer.toString(maxServices)),
                             getID());
                notifyListeners(new SLAPolicyEvent(this, getSLA(), INCREMENT_PENDING));
                if(ourRemoteRef==null)
                    exportDo();
               
                context.getServiceBeanManager().increment((ServiceProvisionListener)ourRemoteRef);
                logger.trace("[{}] Requested increment through ServiceBeanManager", getName());
                pendingRequests++;
            } else {
                logger.debug("[{}] ScalingPolicyHandler [{}]: Current instance count=[{}], "+
                             "Current pending count=[{}], Planned=[{}], MaxServices=[{}], INCREMENT CANCELLED",
                             getName(), getID(), instances.length, pendingCount, getServiceElement().getPlanned(),
                             (maxServices==SLA.UNDEFINED? "Undefined": Integer.toString(maxServices)));
            }
        } catch(java.rmi.NoSuchObjectException e) {
            logger.warn("Remote manager decomissioned for [{}] ScalingPolicyHandler [{}], force disconnect",
                        getName(), getID());
            disconnect();
        } catch(Throwable t) {
            logger.warn("INCREMENT FAILED", t);
            notifyListeners(new SLAPolicyEvent(this, getSLA(), INCREMENT_FAILURE));
        }
    }

    /**
     * Create and schedule a decrement request
     */
    void scheduleDecrement() {
        decrementTask = new ScalingTask(false);
        long now = System.currentTimeMillis();
        logger.debug("[{}] ScalingPolicyHandler [{}]: schedule decrement task in [{}] millis",
                     getName(), getID(), lowerThresholdDampeningTime);
        try {
            taskTimer.schedule(decrementTask, new Date(now+lowerThresholdDampeningTime));
        } catch (IllegalStateException e) {
            logger.warn("Force disconnect of [{}] ScalingPolicyHandler", getName(), e);
            disconnect();   
        }
    }

    /**
     * Do the decrement
     *
     * @return true if the decrement needs to be rescheduled
     */
    protected boolean doDecrement() {
        if(!(//lastType == ThresholdEvent.BREACHED &&
                (lastCalculable.getValue() < lastThresholdValues.getLowThreshold()))) {
            logger.debug("[{}] ScalingPolicyHandler [{}]: DECREMENT CANCELLED, operating above Low Threshold, value [{}] low [{}]",
                         getName(), getID(), lastCalculable.getValue(), lastThresholdValues.getLowThreshold());
            return false;
        }
        OperationalStringManager opMgr = context.getServiceBeanManager().getOperationalStringManager();
        if(opMgr==null) {
            logger.debug("[{}] ScalingPolicyHandler [{}]: unable to process decrement, " +
                         "null OperationalStringManager, abort decrement request",
                         getName(), getID());
            //scheduleDecrement();
            return false;
        }
        boolean reschedule = true;
        try {
            ServiceBeanInstance[] instances = opMgr.getServiceBeanInstances(getServiceElement());
            totalServices = instances.length;
            for (ServiceBeanInstance instance : instances) {
                if (instance.getServiceBeanID().equals(
                    context.getServiceBeanManager().getServiceID())) {
                    reschedule = false;
                    break;
                }
            }
        } catch(Exception e) {
            logger.warn("[{}] ScalingPolicyHandler [{}] getting instance count", getName(), getID(), e);
        }
       
        if(reschedule) {
            logger.debug("[{}] ScalingPolicyHandler [{}]: instance not in OperationalStringManager, reschedule "+
                         "decrement request", getName(), getID());
            //scheduleDecrement();
            return true;
        }
        if(totalServices > minServices) {
            try {
                haveDecremented = true;
                context.getServiceBeanManager().decrement(true);
                notifyListeners(new SLAPolicyEvent(this, getSLA(), DECREMENT_DESTROY_SENT));
                logger.debug("[{}] ScalingPolicyHandler [{}]: DECREMENT_DESTROY_SENT. totalServices=[{}], "+
                             "minServices=[{}], lastCalculable=[{}], currentLowThreshold=[{}]",
                             getName(), getID(), totalServices, minServices, lastCalculable.getValue(),
                             lastThresholdValues.getLowThreshold());
            } catch(Exception e) {
                logger.warn("DECREMENT FAILED", e);
                notifyListeners(new SLAPolicyEvent(this, getSLA(), DECREMENT_FAILED));
            }
        } else {
            logger.debug("[{}] ScalingPolicyHandler [{}]: INCREMENT CANCELLED, totalServices=[{}], minServices=[{}]",
                         getName(), getID(), totalServices, minServices);
        }
        return false;
    }

    /**
     * Export the ScalingPolicyHandler
     */
    private void exportDo() {
        try {           
            ourRemoteRef = exporter.export(this);
        } catch(RemoteException e) {
            logger.warn("Exporting ScalingPolicyHandler ["+getID()+"]", e);
        }
    }

    /**
     * The ServiceElementChangeManager listens for changes made to the
     * ServiceElement
     */
    class ServiceElementChangeManager implements ServiceElementChangeListener {

        /* (non-Javadoc)
         * @see org.rioproject.core.servicebean.ServiceElementChangeListener#changed
         */
        public void changed(ServiceElement preElem, ServiceElement postElem) {
            logger.trace("[{}] ScalingPolicyHandler[{}]: ServiceElement change notification", getName(), getID());
            setServiceElement(postElem);               
        }
    }
   
    /**
     * The ScalingTask is used to schedule either an increment or decrement
     * task to be performed at some time in the future. This behavior provides
     * better control over command to either increase or decrease the number of
     * services the ScalingPolicyHandler is attached to
     */
    class ScalingTask extends TimerTask {
        boolean increment;

        /**
         * Create a ScalingTask
         *
         * @param increment Flag to indicate whether to increment or
         * decrement. If true then increment, otherwise decrement
         */
        ScalingTask(boolean increment) {
            this.increment = increment;
        }

        /**
         * The action to be performed by this timer task.
         */
        public void run() {
            if(increment) {
                logger.debug("[{}] ScalingPolicyHandler [{}]: running increment task", getName(), getID());
                try {
                    doIncrement();
                } finally {
                    incrementTask = null;
                }
            } else {
                logger.debug("[{}] ScalingPolicyHandler [{}]: running decrement task", getName(), getID());
                boolean reschedule = false;
                try {
                    if(!haveDecremented)
                        reschedule = doDecrement();
                } finally {
                    if(reschedule)
                        scheduleDecrement();
                    else
                        decrementTask = null;
                }
            }
        }
    }
}
TOP

Related Classes of org.rioproject.impl.sla.ScalingPolicyHandler$ScalingTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.