/* * Copyright 2008 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.cybernode.service; import com.sun.jini.start.LifeCycle; import net.jini.config.Configuration; import net.jini.core.lookup.ServiceID; import net.jini.export.Exporter; import net.jini.id.Uuid; import org.rioproject.config.Constants; import org.rioproject.config.GroovyConfig; import org.rioproject.deploy.ServiceBeanInstance; import org.rioproject.deploy.ServiceBeanInstantiationException; import org.rioproject.deploy.ServiceRecord; import org.rioproject.event.EventHandler; import org.rioproject.exec.ServiceBeanExecListener; import org.rioproject.exec.ServiceBeanExecutor; import org.rioproject.impl.config.ExporterConfig; import org.rioproject.impl.container.ServiceBeanContainerListener; import org.rioproject.impl.container.ServiceBeanLoaderResult; import org.rioproject.impl.container.ServiceLogUtil; import org.rioproject.impl.exec.JVMProcessMonitor; import org.rioproject.impl.exec.VirtualMachineHelper; import org.rioproject.impl.fdh.FaultDetectionListener; import org.rioproject.impl.fdh.JMXFaultDetectionHandler; import org.rioproject.impl.opstring.OpStringManagerProxy; import org.rioproject.impl.servicebean.ServiceBeanActivation; import org.rioproject.impl.servicebean.ServiceBeanAdapter; import org.rioproject.impl.servicebean.ServiceElementUtil; import org.rioproject.impl.system.ComputeResource; import org.rioproject.impl.system.measurable.MeasurableCapability; import org.rioproject.impl.system.measurable.cpu.CPU; import org.rioproject.impl.system.measurable.memory.Memory; import org.rioproject.opstring.OperationalStringManager; import org.rioproject.opstring.ServiceElement; import org.rioproject.rmi.RegistryUtil; import org.rioproject.servicebean.ServiceBeanContext; import org.rioproject.system.ComputeResourceUtilization; import org.rioproject.system.SystemWatchID; import org.rioproject.system.capability.PlatformCapability; import org.rioproject.watch.WatchDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Provides support to create a ServiceBean in it's own JVM. * * @author Dennis Reedy */ @SuppressWarnings({"unused", "PMD.AvoidThrowingRawExceptionTypes"}) public class ServiceBeanExecutorImpl implements ServiceBeanExecutor, ServiceBeanContainerListener, FaultDetectionListener<String> { private ServiceBeanContainerImpl container; private Exporter exporter; private String execBindName; private ServiceBeanContext context; private int cybernodeRegistryPort; private Registry registry; private ServiceBeanExecListener listener; private JMXFaultDetectionHandler fdh; private ComputeResource computeResource; private ComputeResourcePolicyHandler computeResourcePolicyHandler; private ServiceBeanInstance instance; private final String myID; private final AtomicBoolean inProcess = new AtomicBoolean(false); static final String CONFIG_COMPONENT = "org.rioproject.cybernode"; private Logger logger = LoggerFactory.getLogger(ServiceBeanExecutorImpl.class); /** * Create a ServiceBeanExecutor launched from the ServiceStarter * framework * * @param configArgs Configuration arguments * @param lifeCycle The LifeCycle object that started the * ServiceBeanExecutor * * @throws Exception if bootstrapping fails */ public ServiceBeanExecutorImpl(final String[] configArgs, final LifeCycle lifeCycle) throws Exception { myID = VirtualMachineHelper.getID(); String sPort = System.getProperty(Constants.REGISTRY_PORT, "0"); if (Integer.parseInt(sPort) == 0) logger.warn("The RMI Registry port provided (or obtained by default) is 0. " + "Because it is 0, the port will default to {}. This port may be " + "taken by another RMI Registry instance. If there is an MBeanServer " + "bound to that RMI Registry instance, the ServiceBeanExecutorImpl will monitor the " + "ability to connect to that MBeanServer. If that MBeanServer is terminated, " + "the ServiceBeanExecutorImpl will also terminate. Care should be taken to not " + "use the default RMI Registry port.", Registry.REGISTRY_PORT); cybernodeRegistryPort = Integer.parseInt(sPort); execBindName = System.getProperty(Constants.SERVICE_BEAN_EXEC_NAME, "ServiceBeanExecutorImpl"); bootstrap(configArgs); logger.info("Started ServiceBeanExecutor for {}", execBindName); } private void bootstrap(final String[] configArgs) throws Exception { ClassLoader cCL = Thread.currentThread().getContextClassLoader(); Configuration config = new GroovyConfig(configArgs, cCL); logger.debug("Loaded config"); container = new ServiceBeanContainerImpl(config); computeResource = new ComputeResource(config); /* Setup persistent provisioning attributes */ boolean provisionEnabled = (Boolean) config.getEntry("org.rioproject.cybernode", "provisionEnabled", Boolean.class, true); computeResource.setPersistentProvisioning(provisionEnabled); String provisionRoot = Environment.setupProvisionRoot(provisionEnabled, config); computeResource.setPersistentProvisioningRoot(provisionRoot); MeasurableCapability[] mCaps = loadMeasurables(config); for(MeasurableCapability mCap : mCaps) { computeResource.addMeasurableCapability(mCap); mCap.start(); } container.setComputeResource(computeResource); container.addListener(this); context = ServiceBeanActivation.getServiceBeanContext(CONFIG_COMPONENT, "Cybernode", configArgs, config, getClass().getClassLoader()); registry = LocateRegistry.getRegistry(cybernodeRegistryPort); exporter = ExporterConfig.getExporter(config, "org.rioproject.cybernode", "exporter"); int createdRegistryPort = RegistryUtil.getRegistry(); if(createdRegistryPort>0) { System.setProperty(Constants.REGISTRY_PORT, Integer.toString(createdRegistryPort)); } else { throw new RuntimeException("Unable to create RMI Registry"); } Remote proxy = exporter.export(this); registry.bind(execBindName, proxy); new Thread(new CreateFDH(config, this)).start(); } public String getID() { return myID; } public void setUuid(final Uuid uuid) { if(uuid==null) throw new IllegalArgumentException("uuid cannot be null"); container.setUuid(uuid); } public void setServiceBeanExecListener(final ServiceBeanExecListener listener) { this.listener = listener; } public void applyPlatformCapabilities(final PlatformCapability[] pCaps) { if(pCaps==null) return; for(PlatformCapability pCap : pCaps) { logger.info("Adding [{}] capability", pCap.getName()); computeResource.addPlatformCapability(pCap); } } @Override public ServiceBeanInstance getServiceBeanInstance() { return instance; } public ServiceBeanInstance instantiate(final ServiceElement sElem, final OperationalStringManager opStringMgr) throws ServiceBeanInstantiationException { try { inProcess.set(true); logger.info("Instantiating {}, service counter={}", sElem.getName(), container.getServiceCounter()); if (container.getServiceCounter() > 0) throw new ServiceBeanInstantiationException("ServiceBeanExecutor has already instantiated a service"); OperationalStringManager opMgr = opStringMgr; try { opMgr = OpStringManagerProxy.getProxy(sElem.getOperationalStringName(), opStringMgr, context.getDiscoveryManagement()); } catch (Exception e) { logger.warn("Unable to create proxy for OperationalStringManager, using provided OperationalStringManager", e); } /* Set up thread deadlock detection */ ServiceElementUtil.setThreadDeadlockDetector(sElem, null); /* Get the SLA ThresholdEvent wired up */ EventHandler slaThresholdEventHandler = null; instance = container.activate(sElem, opMgr, null); ServiceBeanDelegateImpl delegate = (ServiceBeanDelegateImpl) container.getServiceBeanDelegate(instance.getServiceBeanID()); ServiceBeanLoaderResult result = delegate.getLoadedServiceResult(); if (result.getImpl() instanceof ServiceBeanAdapter) { slaThresholdEventHandler = ((ServiceBeanAdapter) result.getImpl()).getSLAEventHandler(); } else { if (result.getBeanAdapter() != null) { slaThresholdEventHandler = result.getBeanAdapter().getSLAEventHandler(); } else { String className = result.getImpl() == null ? "<NO IMPLEMENTATION>" : result.getImpl().getClass().getName(); logger.warn("SLAThresholdEvent notifications from this forked service are not enabled, service class does not provide support for Rio event registration {}.", className); } } if (slaThresholdEventHandler != null) { computeResourcePolicyHandler = new ComputeResourcePolicyHandler(sElem, slaThresholdEventHandler, null, instance); computeResource.addThresholdListener(computeResourcePolicyHandler); } } finally { inProcess.set(false); } return instance; } public void update(final ServiceElement element, final OperationalStringManager opStringMgr) { container.update(new ServiceElement[]{element}, opStringMgr); } public ComputeResourceUtilization getComputeResourceUtilization() { return computeResource.getComputeResourceUtilization(); } public void serviceInstantiated(final ServiceRecord record) { ServiceElement service = record.getServiceElement(); logger.info("Instantiated {}, {}", ServiceLogUtil.logName(service), ServiceLogUtil.discoveryInfo(service)); try { listener.serviceInstantiated(record); } catch (RemoteException e) { logger.warn("Notifying Cybernode that the service is active", e); } } public void serviceDiscarded(final ServiceRecord record) { logger.info("Destroying ServiceBeanExecutor for {}", execBindName); if(fdh!=null) fdh.terminate(); if(computeResourcePolicyHandler!=null) { computeResourcePolicyHandler.terminate(); } exporter.unexport(true); try { registry.unbind(execBindName); } catch (Exception e) { logger.warn("{}: {}, Unbinding from RMI Registry", e.getClass().getName(), e.getMessage()); } if(record!=null) { try { listener.serviceDiscarded(record); } catch (RemoteException e) { logger.warn("{}: {}, Notifying Cybernode that we are exiting", e.getClass().getName(), e.getMessage()); } } /* Perform the system exit in a thread, allowing the method to return */ new Thread(new Runnable() { public void run() { while(inProcess.get()) { logger.info("Waiting for inProcess to clear..."); try { Thread.sleep(100); } catch (InterruptedException e) { // ignore } } try { Thread.sleep(TimeUnit.SECONDS.toMillis(1)); } catch (InterruptedException e) { // ignore } System.exit(0); } }).start(); } public void serviceFailure(final Object service, final String pid) { logger.warn("Parent Cybernode has orphaned us, exiting"); container.terminate(); serviceDiscarded(null); } public WatchDataSource[] fetch() { List<WatchDataSource> watchDataSources = new ArrayList<WatchDataSource>(); for(MeasurableCapability mCap : computeResource.getMeasurableCapabilities()) { watchDataSources.add(mCap.getWatchDataSource()); } return watchDataSources.toArray(new WatchDataSource[watchDataSources.size()]); } public WatchDataSource fetch(final String id) { WatchDataSource watchDataSource = null; for(MeasurableCapability mCap : computeResource.getMeasurableCapabilities()) { if(mCap.getId().equals(id)) { watchDataSource = mCap.getWatchDataSource(); break; } } return watchDataSource; } private MeasurableCapability[] loadMeasurables(final Configuration config) { List<MeasurableCapability> measurables = new ArrayList<MeasurableCapability>(); /* Create the Memory MeasurableCapability */ MeasurableCapability memory = new Memory(config); if(memory.isEnabled()) measurables.add(memory); try{ MeasurableCapability cpu = new CPU(config, SystemWatchID.PROC_CPU, true); if(cpu.isEnabled()) measurables.add(cpu); } catch (RuntimeException e) { logger.warn("JVM CPU monitoring not supported"); } return measurables.toArray(new MeasurableCapability[measurables.size()]); } private class CreateFDH implements Runnable { private final Configuration config; private final FaultDetectionListener<String> faultDetectionListener; private CreateFDH(final Configuration config, final FaultDetectionListener<String> faultDetectionListener) { this.config = config; this.faultDetectionListener = faultDetectionListener; } public void run() { final String parentPID = System.getProperty(Constants.PROCESS_ID); if(parentPID!=null) { JVMProcessMonitor.getInstance().monitor(parentPID, faultDetectionListener); } else { logger.error("Cybernode PID is null, unable to setup FDH to make sure Cybernode doesn't orphan us"); } } } class WrappedFaultDetectionListener implements FaultDetectionListener<ServiceID> { private final FaultDetectionListener<String> wrapped; WrappedFaultDetectionListener(FaultDetectionListener<String> wrapped) { this.wrapped = wrapped; } @Override public void serviceFailure(Object service, ServiceID serviceID) { wrapped.serviceFailure(service, serviceID==null?null:serviceID.toString()); } } }