/* * 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.servicebean; import net.jini.config.ConfigurationException; import org.rioproject.config.Constants; import org.rioproject.impl.watch.BoundedThresholdManager; import org.rioproject.impl.watch.ThreadDeadlockMonitor; import org.rioproject.impl.watch.ThresholdManager; import org.rioproject.impl.watch.WatchInjector; import org.rioproject.servicebean.ServiceBeanContext; import org.rioproject.event.EventHandler; import org.rioproject.impl.jmx.JMXUtil; import org.rioproject.impl.jmx.MBeanServerFactory; import org.rioproject.opstring.ServiceElement; import org.rioproject.sla.SLA; import org.rioproject.impl.sla.SLAPolicyHandler; import org.rioproject.impl.sla.SLAPolicyHandlerFactory; import org.rioproject.impl.sla.SLAThresholdEventAdapter; import org.rioproject.impl.system.measurable.MeasurableCapability; import org.rioproject.watch.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.management.ObjectName; import java.beans.IntrospectionException; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; import java.lang.reflect.Method; import java.util.*; /** * The ServiceBeanSLAManager manages service-specific SLAs, and as needed uses * the {@link org.rioproject.impl.watch.WatchInjector} to create declarative watches * * @author Dennis Reedy */ public class ServiceBeanSLAManager { private Object impl; private Object proxy; private ServiceBeanContext context; private EventHandler slaEventHandler; /** List of SLAPolicyHandler instances that have been created */ private final List<SLAPolicyHandler> slaPolicyHandlers = Collections.synchronizedList(new ArrayList<SLAPolicyHandler>()); /* A WatchInjector */ private WatchInjector watchInjector; /** Table of ThresholdManager instances to MeasurableCapability * registrations */ private final Map<ThresholdManager, MeasurableCapability> thresholdManagerReg = new HashMap<ThresholdManager, MeasurableCapability>(); /** * Transforms SLAThresholdEvents into JMX Notifications for SLAs that are * system related to those the service has declared */ private SLAThresholdEventAdapter slaAdapter; /* Monitors thread deadlocks in forked vms */ static final String COMPONENT = ServiceBeanSLAManager.class.getName(); static final Logger logger = LoggerFactory.getLogger(ServiceBeanSLAManager.class); public ServiceBeanSLAManager(final Object impl, final Object proxy, final ServiceBeanContext context, final EventHandler slaEventHandler) throws IntrospectionException { if(impl == null) throw new IllegalArgumentException("impl is null"); if(proxy == null) throw new IllegalArgumentException("proxy is null"); if(context == null) throw new IllegalArgumentException("context is null"); this.impl = impl; this.proxy = proxy; this.context = context; this.slaEventHandler = slaEventHandler; watchInjector = new WatchInjector(impl, context); } /** * Terminate the ServiceBeanSLAManager, cleaning up pending resources */ public void terminate() { impl = null; proxy = null; context = null; slaEventHandler = null; if(watchInjector!=null) { watchInjector.terminate(); watchInjector = null; } for(Map.Entry<ThresholdManager, MeasurableCapability> entry : thresholdManagerReg.entrySet()) entry.getKey().clear(); thresholdManagerReg.clear(); /* Disconnect all SLAPolicyHandler instances */ for (SLAPolicyHandler slap : slaPolicyHandlers) { if(slap.getThresholdManager()!=null) slap.getThresholdManager().clear(); slap.disconnect(); } } /** * Add a new SLA. * * @param slas Array of SLA instances to add */ public void addSLAs(final SLA[] slas) { for (SLA sla : slas) { String identifier = sla.getIdentifier(); /* Get the WatchDescriptors from the SLA. If there are no * WatchDescriptors found, use the SLA's ID. Othwerwise, use the * first WatchDescriptor name as the identifier */ WatchDescriptor[] wds = sla.getWatchDescriptors(); if (wds.length > 0) { identifier = wds[0].getName(); } SLAPolicyHandler handler = null; ServiceElement elem = context.getServiceElement(); /* Check if the SLA matches a MeasurableCapability. */ MeasurableCapability mCap = getMeasurableCapability(identifier); if (mCap != null) { logger.trace("[{}] SLA [{}] correlates to a MeasurableCapability", elem.getName(), identifier); try { /* Load the SLA PolicyHandler and set attributes */ handler = createSLAPolicyHandler(sla, null); ThresholdManager tMgr = new BoundedThresholdManager(mCap.getId()); tMgr.setThresholdValues(sla); handler.setThresholdManager(tMgr); mCap.addSecondaryThresholdManager(tMgr); thresholdManagerReg.put(tMgr, mCap); logger.trace("[{}] SLA ID [{}], associated to MeasurableCapability={}, SLAPolicyHandler={}", elem.getName(), identifier, mCap.getClass().getName(), handler.getClass().getName()); } catch (Exception e) { logger.warn("Creating SLAPolicyHandler for system SLA [{}]", sla.getIdentifier(), e); } /* Check if the SLA matches the ThreadDeadlockMonitor. */ } else if(identifier.equals(ThreadDeadlockMonitor.ID)) { WatchDescriptor wDesc = ServiceElementUtil.getWatchDescriptor(elem, ThreadDeadlockMonitor.ID); if(wDesc==null) wDesc = ThreadDeadlockMonitor.getWatchDescriptor(); /* If the service is not forked and running in it's own VM, * we currently do not allow service specific thread deadlock * monitoring. There is one Cybernode-based * ThreadDeadlockMonitor that will send out notifications */ if(wDesc.getMBeanServerConnection()==null && !runningForked()) { logger.warn("Thread deadlock detection is provided at the process level, not " + "enabled on a service-by-service approach within a Cybernode. The SLA declaration for " + "the [{}] service will be ignored. Note that thread deadlock " + "detection has been enabled by the Cybernode.", elem.getName()); return; } if(wDesc.getPeriod()<1000) { logger.info("Thread deadlock monitoring has been disabled " + "for service [{}]. The configured thread deadlock check time was " + "[{}]. To enable thread deadlock monitoring, the thread deadlock " + "check time must be >= 1000 milliseconds.", elem.getName(), wDesc.getPeriod()); return; } logger.info("Setting Thread deadlock detection: {}", sla); try { ClassLoader loader = impl.getClass().getClassLoader(); /* Load the SLA PolicyHandler and set attributes */ handler = createSLAPolicyHandler(sla, loader); Method getThreadDeadlockCalculable = ThreadDeadlockMonitor.class.getMethod("getThreadDeadlockCalculable"); ThreadDeadlockMonitor threadDeadlockMonitor = new ThreadDeadlockMonitor(); if(wDesc.getMBeanServerConnection()!=null) { ThreadMXBean threadMXBean = JMXUtil.getPlatformMXBeanProxy(wDesc.getMBeanServerConnection(), ManagementFactory.THREAD_MXBEAN_NAME, ThreadMXBean.class); threadDeadlockMonitor.setThreadMXBean(threadMXBean); } watchInjector.inject(wDesc, threadDeadlockMonitor, getThreadDeadlockCalculable); } catch (Exception e) { logger.warn("Creating SLAPolicyHandler for SLA [{}]", sla.getIdentifier(), e); } } else { try { handler = createSLAPolicyHandler(sla, impl.getClass().getClassLoader()); /* Inject watch if necessary */ for (WatchDescriptor wd : wds) { try { watchInjector.inject(wd); } catch (ConfigurationException e) { logger.warn("Injecting Watch [{}] for SLA [{}]", wd.getName(), sla.getIdentifier(), e); } } } catch (Exception e) { logger.warn("Creating SLAPolicyHandler for SLA [{}]", sla.getIdentifier(), e); } } if (handler != null) { logger.trace("[{}] Adding SLA [{}] to the Watch Registry for subsequent association", context.getServiceElement().getName(), identifier); context.getWatchRegistry().addThresholdListener(identifier, handler); } else { logger.error("[{}] Could not addSLA [{}] to the Watch Registry for subsequent association, handler is null", context.getServiceElement().getName(), identifier); } } } /** * Update SLAs * * @param slas Array of SLAs to update */ public void updateSLAs(final SLA[] slas) { /* Create a representation of the current Collection so we * can determine if any SLAPolicyHandlers are no longer needed */ ArrayList<SLAPolicyHandler> toDiscardList = new ArrayList<SLAPolicyHandler>(slaPolicyHandlers); /* List for new SLAs, that is SLAs that do not have an ID equal to * a current Watch */ ArrayList<SLA> toAddList = new ArrayList<SLA>(); for (SLA sla : slas) { SLAPolicyHandler slap = getSLAPolicyHandler(sla); if (slap == null) { toAddList.add(sla); } else { toDiscardList.remove(slap); if (SLAPolicyHandlerFactory.slaPolicyHandlerChanged(sla, slap)) { if(logger.isTraceEnabled()) { StringBuilder b = new StringBuilder(); b.append("The SLAPolicyHandler for ["); b.append(sla.getIdentifier()); b.append("] has changed. "); b.append("Configured SLAPolicyHandler=["); b.append(sla.getSlaPolicyHandler()); b.append("], SLAPolicyHandler class=["); b.append(slap.getClass().getName()); logger.trace(b.toString()); } removeSLAPolicyHandler(slap); toAddList.add(sla); } else { if(logger.isTraceEnabled()) { StringBuilder b = new StringBuilder(); b.append("Updating the SLAPolicyHandler for ["); b.append(sla.getIdentifier()); b.append("] with new SLA values: "); b.append(sla); logger.trace(b.toString()); } slap.setSLA(sla); WatchDescriptor[] wds = sla.getWatchDescriptors(); for (WatchDescriptor wd : wds) { try { watchInjector.modify(wd); } catch (ConfigurationException e) { logger.warn("Modifying WatchDescriptor [{}] for SLA [{}]", wd.getName(), sla.getIdentifier(),e); } } } } } /* Add any new SLAs */ addSLAs(toAddList.toArray(new SLA[toAddList.size()])); /* Remove uneeded SLAs */ SLAPolicyHandler[] toDiscard = toDiscardList.toArray( new SLAPolicyHandler[toDiscardList.size()]); for (SLAPolicyHandler d : toDiscard) { removeSLAPolicyHandler(d); } /* Discard uneeded watches */ discardWatches(slas); } /** * If the service bean has registered to JMX, and if there are declared * system SLAs that have been registered and the SLAThresholdEventAdapter is * null, create the adapter and register if */ public void createSLAThresholdEventAdapter() { if(slaAdapter != null) return; if(!slaPolicyHandlers.isEmpty()) { try { ObjectName objectName = JMXUtil.getObjectName(context, "", context.getServiceElement().getName()); if(MBeanServerFactory.getMBeanServer().isRegistered(objectName)) { slaAdapter = new SLAThresholdEventAdapter(objectName, context.getServiceBeanManager().getNotificationBroadcasterSupport()); } } catch(Exception e) { logger.debug("Registering SLAThresholdEventAdapter", e); } } } /** * Create a SLAPolicyHandler using the SLA * * @param sla The SLA to obtain a SLAPolicyHandler for * @param loader The Classloader to use to load the SLAPolicyHandler * * @return An SLAPolicyHandler implementation ready for use * * @throws Exception if the SLAPolicyHandler cannot be created */ private SLAPolicyHandler createSLAPolicyHandler(final SLA sla, final ClassLoader loader) throws Exception { SLAPolicyHandler slappy = SLAPolicyHandlerFactory.create(sla, proxy, slaEventHandler, context, loader); logger.trace("[{}] SLA [{}] Created SLAPolicyHandler [{}]", context.getServiceElement().getName(), sla.getIdentifier(), slappy.getClass().getName()); slaPolicyHandlers.add(slappy); return (slappy); } /* * Get all SLAPolicyHandler instances * * @return Array of SLAPolicyHandler instances. A new array is allocated * each time. If there are no SLAPolicyHandler instances, a * zero-length array is returned */ private SLAPolicyHandler[] getSLAPolicyHandlers() { SLAPolicyHandler[] handlers = slaPolicyHandlers.toArray(new SLAPolicyHandler[slaPolicyHandlers.size()]); return (handlers); } /* * Get a SLAPolicyHandler instance * * @param sla The SLA to use, must not be null * * @return The SLAPolicyHandler instance for the provided SLA or null if * not found or the sla parameter is <code>null</code> */ private SLAPolicyHandler getSLAPolicyHandler(final SLA sla) { if(sla!=null) { SLAPolicyHandler[] slappys = getSLAPolicyHandlers(); for (SLAPolicyHandler slappy : slappys) { if (slappy.getID().equals(sla.getIdentifier())) return (slappy); } } return (null); } /* * Unregister and remove a SLAPolicyHandler */ private void removeSLAPolicyHandler(final SLAPolicyHandler slaPolicyHandler) { slaPolicyHandlers.remove(slaPolicyHandler); slaPolicyHandler.disconnect(); context.getWatchRegistry().removeThresholdListener(slaPolicyHandler.getID(), slaPolicyHandler); ThresholdManager tMgr = slaPolicyHandler.getThresholdManager(); MeasurableCapability mCap = thresholdManagerReg.remove(tMgr); if(mCap != null) { mCap.removeSecondaryThresholdManager(tMgr); } } /* * Discard uneeded Watches. This is determined by comparing * an array of Watch instances, to the collection of known Watch * instances. Discard Watch instances which do not match to * Watch identifiers */ private void discardWatches(final SLA[] serviceSLAs) { if(watchInjector==null) return; String[] createdWatches = watchInjector.getWatchNames(); if(createdWatches.length==0) return; ArrayList<String> list = new ArrayList<String>(Arrays.asList(createdWatches)); WatchDescriptor[] configuredWatches = getWatchDescriptors(serviceSLAs); for (WatchDescriptor wd : configuredWatches) { for (String wName : createdWatches) { if (wd.getName().equals(wName)) { list.remove(wName); break; } } } String[] removals = list.toArray(new String[list.size()]); for (String remove : removals) { watchInjector.remove(remove); } } /* * Get the WatchDescriptor instances from the SLA configs */ private WatchDescriptor[] getWatchDescriptors(final SLA[] slas) { ArrayList<WatchDescriptor> list = new ArrayList<WatchDescriptor>(); for (SLA sla : slas) { WatchDescriptor[] wDesc = sla.getWatchDescriptors(); list.addAll(Arrays.asList(wDesc)); } return (list.toArray(new WatchDescriptor[list.size()])); } /* * Get the MeasurableCapability for a SLA * * @return The MeasurableCapability for a SLA. If a matching * MeasurableCapability cannot be found return null */ private MeasurableCapability getMeasurableCapability(final String id) { MeasurableCapability[] mCaps = context.getComputeResourceManager(). getMatchedMeasurableCapabilities(); for (MeasurableCapability mCap : mCaps) { if (id.equals(mCap.getId())) return (mCap); } return (null); } private boolean runningForked() { return (System.getProperty(Constants.SERVICE_BEAN_EXEC_NAME)!=null); } }