package org.jboss.system;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import javax.management.JMException;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.ObjectName;
import org.apache.log4j.NDC;
import org.jboss.deployment.DeploymentException;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.deployment.DeploymentState;
import org.jboss.logging.Logger;
import org.jboss.mx.server.ServerConstants;
import org.jboss.mx.util.JBossNotificationBroadcasterSupport;
import org.jboss.mx.util.JMXExceptionDecoder;
import org.jboss.mx.util.ObjectNameFactory;
import org.w3c.dom.Element;
public class ServiceController
extends JBossNotificationBroadcasterSupport
implements ServiceControllerMBean, MBeanRegistration
{
public static final ObjectName DEFAULT_LOADER_REPOSITORY = ObjectNameFactory.create(ServerConstants.DEFAULT_LOADER_NAME);
public static final String JBOSS_INTERNAL_LIFECYCLE = "jbossInternalLifecycle";
public static final String[] JBOSS_INTERNAL_LIFECYCLE_SIG = new String[] { String.class.getName() };
private static final Logger log = Logger.getLogger(ServiceController.class);
MBeanServer server;
protected ServiceCreator creator;
protected ServiceConfigurator configurator;
protected Map nameToServiceMap = Collections.synchronizedMap(new HashMap());
protected List installedServices = new LinkedList();
private long sequenceNo;
public List listDeployed()
{
return new ArrayList(installedServices);
}
public List listIncompletelyDeployed()
{
List id = new ArrayList();
for (Iterator i = installedServices.iterator(); i.hasNext();)
{
ServiceContext sc = (ServiceContext) i.next();
if ( sc.state != ServiceContext.CREATED &&
sc.state != ServiceContext.RUNNING &&
sc.state != ServiceContext.STOPPED &&
sc.state != ServiceContext.DESTROYED )
{
id.add(sc);
}
}
return id;
}
public List listDeployedNames()
{
List names = new ArrayList(installedServices.size());
for (Iterator i = installedServices.iterator(); i.hasNext();)
{
ServiceContext ctx = (ServiceContext) i.next();
names.add(ctx.objectName);
}
return names;
}
public String listConfiguration(ObjectName[] objectNames) throws Exception
{
return configurator.getConfiguration(objectNames);
}
public void validateDeploymentState(DeploymentInfo di, DeploymentState state)
{
ArrayList mbeans = new ArrayList(di.mbeans);
if (di.deployedObject != null)
mbeans.add(di.deployedObject);
boolean mbeansStateIsValid = true;
for (int m = 0; m < mbeans.size(); m++)
{
ObjectName serviceName = (ObjectName) mbeans.get(m);
ServiceContext ctx = this.getServiceContext(serviceName);
if (ctx != null && state == DeploymentState.STARTED)
mbeansStateIsValid &= ctx.state == ServiceContext.RUNNING;
}
if (mbeansStateIsValid == true)
di.state = state;
}
public synchronized List install(Element config, ObjectName loaderName)
throws DeploymentException
{
List mbeans = configurator.install(config, loaderName);
for (Iterator i = mbeans.iterator(); i.hasNext();)
{
ObjectName mbean = (ObjectName) i.next();
installedServices.add(createServiceContext(mbean));
}
return mbeans;
}
public synchronized void register(ObjectName serviceName) throws Exception
{
register(serviceName, null);
}
public synchronized void register(ObjectName serviceName, Collection depends)
throws Exception
{
log.debug("Registering service " + serviceName);
ServiceContext ctx = createServiceContext(serviceName);
log.trace("Pushing NDC: " + ctx.objectName.toString());
NDC.push(ctx.objectName.toString());
try
{
register(ctx, depends);
}
finally
{
NDC.pop();
NDC.remove();
}
}
private void register(ServiceContext ctx, Collection depends) throws Exception
{
if (!installedServices.contains(ctx))
installedServices.add(ctx);
if (depends != null)
{
log.debug("adding depends in ServiceController.register: " + depends);
for (Iterator i = depends.iterator(); i.hasNext();)
{
registerDependency(ctx.objectName, (ObjectName) i.next());
}
}
ctx.proxy = getServiceProxy(ctx.objectName, null);
}
public synchronized void create(ObjectName serviceName) throws Exception
{
create(serviceName, null);
}
public synchronized void create(ObjectName serviceName, Collection depends)
throws Exception
{
log.debug("Creating service " + serviceName);
ServiceContext ctx = createServiceContext(serviceName);
log.trace("Pushing NDC: " + ctx.objectName.toString());
NDC.push(ctx.objectName.toString());
try
{
register(ctx, depends);
if (ctx.state == ServiceContext.CREATED
|| ctx.state == ServiceContext.RUNNING
|| ctx.state == ServiceContext.FAILED)
{
log.debug("Ignoring create request for service: " + ctx.objectName);
return;
}
int oldState = ctx.state;
ctx.state = ServiceContext.CREATED;
for (Iterator iterator = ctx.iDependOn.iterator(); iterator.hasNext();)
{
ServiceContext sc = (ServiceContext) iterator.next();
int state = sc.state;
if (!(state == ServiceContext.CREATED || state == ServiceContext.RUNNING))
{
log.debug("waiting in create of " + serviceName +
" waiting on " + sc.objectName);
ctx.state = oldState;
return;
}
}
try
{
ctx.proxy.create();
sequenceNo++;
Notification createMsg = new Notification(ServiceMBean.CREATE_EVENT,
this, sequenceNo);
createMsg.setUserData(serviceName);
sendNotification(createMsg);
}
catch (Throwable e)
{
ctx.state = ServiceContext.FAILED;
ctx.problem = e;
log.warn("Problem creating service " + serviceName, e);
return;
}
log.debug("Creating dependent components for: " + serviceName
+ " dependents are: " + ctx.dependsOnMe);
ArrayList tmp = new ArrayList(ctx.dependsOnMe);
for (int n = 0; n < tmp.size(); n++)
{
ServiceContext ctx2 = (ServiceContext) tmp.get(n);
create(ctx2.objectName);
}
tmp.clear();
}
finally
{
NDC.pop();
NDC.remove();
}
}
public synchronized void start(ObjectName serviceName) throws Exception
{
log.debug("starting service " + serviceName);
ServiceContext ctx = createServiceContext(serviceName);
NDC.push(ctx.objectName.toString());
try
{
if (!installedServices.contains(ctx))
installedServices.add(ctx);
if (ctx.state == ServiceContext.RUNNING || ctx.state == ServiceContext.FAILED)
{
log.debug("Ignoring start request for service: " + ctx.objectName);
return;
}
if (ctx.proxy == null)
ctx.proxy = getServiceProxy(ctx.objectName, null);
int oldState = ctx.state;
ctx.state = ServiceContext.RUNNING;
for (Iterator iterator = ctx.iDependOn.iterator(); iterator.hasNext();)
{
ServiceContext sctx = (ServiceContext) iterator.next();
int state = sctx.state;
if (!(state == ServiceContext.RUNNING))
{
log.debug("waiting in start " + serviceName + " on " + sctx.objectName);
ctx.state = oldState;
return;
}
}
try
{
ctx.proxy.start();
sequenceNo ++;
Notification startMsg = new Notification(ServiceMBean.START_EVENT,
this, sequenceNo);
startMsg.setUserData(serviceName);
sendNotification(startMsg);
}
catch (Throwable e)
{
ctx.state = ServiceContext.FAILED;
ctx.problem = e;
log.warn("Problem starting service " + serviceName, e);
return;
}
log.debug("Starting dependent components for: " + serviceName
+ " dependent components: " + ctx.dependsOnMe);
ArrayList tmp = new ArrayList(ctx.dependsOnMe);
for (int n = 0; n < tmp.size(); n++)
{
ServiceContext ctx2 = (ServiceContext) tmp.get(n);
start(ctx2.objectName);
}
tmp.clear();
}
finally
{
NDC.pop();
NDC.remove();
}
}
public void restart(ObjectName serviceName) throws Exception
{
log.debug("restarting service " + serviceName);
stop(serviceName);
start(serviceName);
}
public void stop(ObjectName serviceName) throws Exception
{
ServiceContext ctx = (ServiceContext) nameToServiceMap.get(serviceName);
log.debug("stopping service: " + serviceName);
if (ctx == null)
{
log.warn("Ignoring request to stop nonexistent service: " + serviceName);
return;
}
if (ctx.state != ServiceContext.RUNNING) return;
ctx.state = ServiceContext.STOPPED;
log.debug("stopping dependent services for: " + serviceName
+ " dependent services are: " + ctx.dependsOnMe);
ArrayList tmp = new ArrayList(ctx.dependsOnMe);
for (int n = 0; n < tmp.size(); n++)
{
ServiceContext ctx2 = (ServiceContext) tmp.get(n);
ObjectName other = ctx2.objectName;
stop(other);
}
tmp.clear();
if (ctx.proxy != null)
{
try
{
ctx.proxy.stop();
sequenceNo ++;
Notification stopMsg = new Notification(ServiceMBean.STOP_EVENT,
this, sequenceNo);
stopMsg.setUserData(serviceName);
sendNotification(stopMsg);
}
catch (Throwable e)
{
ctx.state = ServiceContext.FAILED;
ctx.problem = e;
log.warn("Problem stopping service " + serviceName, e);
}
}
}
public void destroy(ObjectName serviceName) throws Exception
{
ServiceContext ctx = (ServiceContext) nameToServiceMap.get(serviceName);
log.debug("destroying service: " + serviceName);
if (ctx == null)
{
log.warn("Ignoring request to destroy nonexistent service: " + serviceName);
return;
}
if (ctx.state == ServiceContext.DESTROYED ||
ctx.state == ServiceContext.NOTYETINSTALLED)
return;
ctx.state = ServiceContext.DESTROYED;
log.debug("destroying dependent services for: " + serviceName
+ " dependent services are: " + ctx.dependsOnMe);
ArrayList tmp = new ArrayList(ctx.dependsOnMe);
for (int n = 0; n < tmp.size(); n++)
{
ServiceContext ctx2 = (ServiceContext) tmp.get(n);
ObjectName other = ctx2.objectName;
destroy(other);
}
tmp.clear();
if (ctx.proxy != null)
{
try
{
ctx.proxy.destroy();
sequenceNo++;
Notification destroyMsg = new Notification(ServiceMBean.DESTROY_EVENT,
this, sequenceNo);
destroyMsg.setUserData(serviceName);
sendNotification(destroyMsg);
}
catch (Throwable e)
{
ctx.state = ServiceContext.FAILED;
ctx.problem = e;
log.warn("Problem destroying service " + serviceName, e);
}
}
}
public void remove(ObjectName objectName) throws Exception
{
ServiceContext ctx = (ServiceContext) nameToServiceMap.get(objectName);
log.debug("removing service: " + objectName);
if (ctx == null)
{
log.warn("Ignoring request to remove nonexistent service: " + objectName);
return;
}
Iterator iterator = ctx.iDependOn.iterator();
while (iterator.hasNext())
{
ServiceContext iDependOnContext = (ServiceContext) iterator.next();
iDependOnContext.dependsOnMe.remove(ctx);
if (iDependOnContext.state == ServiceContext.NOTYETINSTALLED
&& iDependOnContext.dependsOnMe.size() == 0)
{
nameToServiceMap.remove(iDependOnContext.objectName);
log.debug("Removing context for nonexistent service it is " +
"no longer recording dependencies: " + iDependOnContext);
}
}
ctx.iDependOn.clear();
if (server.isRegistered(objectName))
{
log.debug("removing " + objectName + " from server");
if (ctx.dependsOnMe.size() == 0)
nameToServiceMap.remove(objectName);
else
{
log.debug("Context not removed, it is recording " +
"dependencies: " + ctx);
}
installedServices.remove(ctx);
creator.remove(objectName);
}
else
{
log.debug("no need to remove " + objectName + " from server");
}
ctx.state = ServiceContext.NOTYETINSTALLED;
}
public void shutdown()
{
log.debug("Stopping " + nameToServiceMap.size() + " services");
List servicesCopy = new ArrayList(installedServices);
int serviceCounter = 0;
ObjectName name = null;
ListIterator i = servicesCopy.listIterator(servicesCopy.size());
while (i.hasPrevious())
{
ServiceContext ctx = (ServiceContext) i.previous();
name = ctx.objectName;
try
{
remove(name);
serviceCounter++;
}
catch (Throwable e)
{
log.error("Could not remove " + name, e);
}
}
log.debug("Stopped " + serviceCounter + " services");
}
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception
{
this.server = server;
creator = new ServiceCreator(server);
configurator = new ServiceConfigurator(server, this, creator);
ServiceContext sc = this.createServiceContext(name);
sc.state = ServiceContext.RUNNING;
log.debug("Controller MBean online");
return name == null ? OBJECT_NAME : name;
}
public void postRegister(Boolean registrationDone)
{
if (!registrationDone.booleanValue())
{
log.info("Registration of ServiceController failed");
}
}
public void preDeregister()
throws Exception
{
}
public void postDeregister()
{
nameToServiceMap.clear();
installedServices.clear();
}
private Service getServiceProxy(ObjectName objectName, String serviceFactory)
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, JMException
{
Service service = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (serviceFactory != null && serviceFactory.length() > 0)
{
Class clazz = loader.loadClass(serviceFactory);
ServiceFactory factory = (ServiceFactory) clazz.newInstance();
service = factory.createService(server, objectName);
}
else
{
MBeanInfo info = server.getMBeanInfo(objectName);
MBeanOperationInfo[] opInfo = info.getOperations();
Class[] interfaces = {Service.class};
InvocationHandler handler = new ServiceProxy(objectName, opInfo);
service = (Service) Proxy.newProxyInstance(loader, interfaces, handler);
}
return service;
}
public ServiceContext getServiceContext(ObjectName serviceName)
{
ServiceContext ctx = (ServiceContext) nameToServiceMap.get(serviceName);
return ctx;
}
synchronized ServiceContext createServiceContext(ObjectName objectName)
{
if (nameToServiceMap.containsKey(objectName))
return (ServiceContext) nameToServiceMap.get(objectName);
ServiceContext ctx = new ServiceContext();
ctx.objectName = objectName;
nameToServiceMap.put(objectName, ctx);
return ctx;
}
void registerDependency(ObjectName needs, ObjectName used)
{
log.debug("recording that " + needs + " depends on " + used);
ServiceContext needsCtx = createServiceContext(needs);
ServiceContext usedCtx = createServiceContext(used);
if (!needsCtx.iDependOn.contains(usedCtx))
{
needsCtx.iDependOn.add(usedCtx);
usedCtx.dependsOnMe.add(needsCtx);
}
}
private static HashMap serviceOpMap = new HashMap();
public class ServiceProxy
implements InvocationHandler
{
private boolean[] hasOp = {false, false, false, false};
private ObjectName objectName;
private boolean hasJBossInternalLifecycle;
public ServiceProxy(ObjectName objectName, MBeanOperationInfo[] opInfo)
{
this.objectName = objectName;
for (int op = 0; op < opInfo.length; op++)
{
MBeanOperationInfo info = opInfo[op];
String name = info.getName();
if (name.equals(JBOSS_INTERNAL_LIFECYCLE))
{
hasJBossInternalLifecycle = true;
continue;
}
Integer opID = (Integer) serviceOpMap.get(name);
if (opID == null)
{
continue;
}
if (info.getReturnType().equals("void") == false)
{
continue;
}
if (info.getSignature().length != 0)
{
continue;
}
hasOp[opID.intValue()] = true;
}
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
String name = method.getName();
if (hasJBossInternalLifecycle)
{
try
{
server.invoke(objectName, JBOSS_INTERNAL_LIFECYCLE, new Object[] { name }, JBOSS_INTERNAL_LIFECYCLE_SIG);
return null;
}
catch (Exception e)
{
throw JMXExceptionDecoder.decode(e);
}
}
Integer opID = (Integer) serviceOpMap.get(name);
if (opID != null && hasOp[opID.intValue()] == true)
{
try
{
String[] sig = {};
server.invoke(objectName, name, args, sig);
}
catch (Exception e)
{
throw JMXExceptionDecoder.decode(e);
}
}
return null;
}
}
static
{
serviceOpMap.put("create", new Integer(0));
serviceOpMap.put("start", new Integer(1));
serviceOpMap.put("destroy", new Integer(2));
serviceOpMap.put("stop", new Integer(3));
}
}