package org.jboss.system.server;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.management.Attribute;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.jboss.Version;
import org.jboss.deployment.IncompleteDeploymentException;
import org.jboss.deployment.MainDeployerMBean;
import org.jboss.logging.Logger;
import org.jboss.mx.loading.RepositoryClassLoader;
import org.jboss.mx.server.ServerConstants;
import org.jboss.mx.util.JBossNotificationBroadcasterSupport;
import org.jboss.mx.util.JMXExceptionDecoder;
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.mx.util.ObjectNameFactory;
import org.jboss.net.protocol.URLStreamHandlerFactory;
import org.jboss.system.ServiceControllerMBean;
import org.jboss.util.StopWatch;
import org.jboss.util.file.FileSuffixFilter;
import org.jboss.util.file.Files;
public class ServerImpl
implements Server, ServerImplMBean, NotificationEmitter
{
private final static ObjectName DEFAULT_LOADER_NAME =
ObjectNameFactory.create(ServerConstants.DEFAULT_LOADER_NAME);
private Logger log;
private final Version version = Version.getInstance();
private final Package jbossPackage = Package.getPackage("org.jboss");
private ServerConfigImpl config;
private MBeanServer server;
private Date startDate;
private boolean started;
private ShutdownHook shutdownHook;
private LifeThread lifeThread;
private JBossNotificationBroadcasterSupport broadcasterSupport;
private ObjectName bootstrapUCLName;
public ServerImpl()
{
}
public void init(final Properties props) throws IllegalStateException, Exception
{
if (props == null)
throw new IllegalArgumentException("props is null");
if (config != null)
throw new IllegalStateException("already initialized");
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
doInit(props);
}
finally
{
Thread.currentThread().setContextClassLoader(oldCL);
}
}
private void doInit(final Properties props) throws Exception
{
this.config = new ServerConfigImpl(props);
broadcasterSupport = new JBossNotificationBroadcasterSupport();
boolean overrideTmpDir = Boolean.getBoolean("jboss.server.temp.dir.overrideJavaTmpDir");
if( overrideTmpDir )
{
File serverTmpDir = config.getServerTempDir();
System.setProperty("java.io.tmpdir", serverTmpDir.getCanonicalPath());
}
log = Logger.getLogger(Server.class);
initURLHandlers();
config.initURLs();
log.info("Starting JBoss (MX MicroKernel)...");
if (jbossPackage != null)
{
log.info("Release ID: " +
jbossPackage.getImplementationTitle() + " " +
jbossPackage.getImplementationVersion());
}
else
{
log.warn("could not get package info to display release, either the " +
"jar manifest in jboss-system.jar has been mangled or you're " +
"running unit tests from ant outside of JBoss itself.");
}
log.debug("Using config: " + config);
log.debug("Server type: " + getClass());
log.info("Home Dir: " + config.getHomeDir());
log.info("Home URL: " + config.getHomeURL());
log.info("Library URL: " + config.getLibraryURL());
log.info("Patch URL: " + config.getPatchURL());
log.info("Server Name: " + config.getServerName());
log.info("Server Home Dir: " + config.getServerHomeDir());
log.info("Server Home URL: " + config.getServerHomeURL());
log.info("Server Data Dir: " + config.getServerDataDir());
log.info("Server Temp Dir: " + config.getServerTempDir());
log.info("Server Config URL: " + config.getServerConfigURL());
log.info("Server Library URL: " + config.getServerLibraryURL());
log.info("Root Deployment Filename: " + config.getRootDeploymentFilename());
}
private void initURLHandlers()
{
if (config.getRequireJBossURLStreamHandlerFactory())
{
internalInitURLHandlers();
} else
{
try
{
internalInitURLHandlers();
}
catch (SecurityException e)
{
log.warn("You do not have permissions to set URLStreamHandlerFactory", e);
} catch (Error e)
{
log.warn("URLStreamHandlerFactory already set", e);
} } }
private void internalInitURLHandlers()
{
try
{
URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory());
URLStreamHandlerFactory.preload();
}
catch (Error error)
{ log.warn("Caught Throwable Error, this probably means " +
"we've already set the URLStreamHAndlerFactory before");
}
String handlerPkgs = System.getProperty("java.protocol.handler.pkgs");
if (handlerPkgs != null)
{
handlerPkgs += "|org.jboss.net.protocol";
}
else
{
handlerPkgs = "org.jboss.net.protocol";
}
System.setProperty("java.protocol.handler.pkgs", handlerPkgs);
}
public ServerConfig getConfig() throws IllegalStateException
{
if (config == null)
throw new IllegalStateException("not initialized");
return config;
}
public boolean isStarted()
{
return started;
}
public void start() throws IllegalStateException, Exception
{
getConfig();
if (started)
throw new IllegalStateException("already started");
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
try
{
doStart();
}
catch (Exception e)
{
JMXExceptionDecoder.rethrow(e);
}
}
catch (Throwable t)
{
log.debug("Failed to start", t);
if (t instanceof Exception)
throw (Exception)t;
if (t instanceof Error)
throw (Error)t;
throw new org.jboss.util.UnexpectedThrowable(t);
}
finally
{
Thread.currentThread().setContextClassLoader(oldCL);
}
}
private void doStart() throws Exception
{
StopWatch watch = new StopWatch(true);
startDate = new Date();
log.info("Starting General Purpose Architecture (GPA)...");
String builder = System.getProperty(ServerConstants.MBEAN_SERVER_BUILDER_CLASS_PROPERTY,
ServerConstants.DEFAULT_MBEAN_SERVER_BUILDER_CLASS);
System.setProperty(ServerConstants.MBEAN_SERVER_BUILDER_CLASS_PROPERTY, builder);
if (config.getPlatformMBeanServer() == true)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass("java.lang.management.ManagementFactory");
Method method = clazz.getMethod("getPlatformMBeanServer", null);
server = (MBeanServer)method.invoke(null, null);
MBeanServerLocator.setJBoss(server);
}
else
{
server = MBeanServerFactory.createMBeanServer("jboss");
}
log.debug("Created MBeanServer: " + server);
server.registerMBean(this, ServerImplMBean.OBJECT_NAME);
server.registerMBean(config, ServerConfigImplMBean.OBJECT_NAME);
RepositoryClassLoader ucl = initBootLibraries();
bootstrapUCLName = ucl.getObjectName();
server.registerMBean(ucl, bootstrapUCLName);
Thread.currentThread().setContextClassLoader(ucl);
createMBean("org.jboss.system.server.ServerInfo",
"jboss.system:type=ServerInfo");
ObjectName controller = createMBean("org.jboss.system.ServiceController",
"jboss.system:service=ServiceController");
ObjectName mainDeployer = startBootService(controller,
"org.jboss.deployment.MainDeployer", "jboss.system:service=MainDeployer");
server.setAttribute(mainDeployer,
new Attribute("ServiceController", controller));
shutdownHook = new ShutdownHook(controller, mainDeployer);
shutdownHook.setDaemon(true);
try
{
Runtime.getRuntime().addShutdownHook(shutdownHook);
log.debug("Shutdown hook added");
}
catch (Exception e)
{
log.warn("Failed to add shutdown hook; ignoring", e);
}
startBootService(controller, "org.jboss.deployment.JARDeployer",
"jboss.system:service=JARDeployer");
startBootService(controller, "org.jboss.deployment.SARDeployer",
"jboss.system:service=ServiceDeployer");
log.info("Core system initialized");
MainDeployerMBean md = (MainDeployerMBean)
MBeanProxyExt.create(MainDeployerMBean.class, mainDeployer, server);
try
{
md.deploy(config.getServerConfigURL() + config.getRootDeploymentFilename());
}
catch (IncompleteDeploymentException e)
{
log.error("Root deployment has missing dependencies; continuing", e);
}
lifeThread = new LifeThread();
lifeThread.start();
started = true;
Notification msg = new Notification(START_NOTIFICATION_TYPE, this, 1);
msg.setUserData(new Long(watch.getLapTime()));
sendNotification(msg);
watch.stop();
if (jbossPackage != null)
{
log.info("JBoss (MX MicroKernel) [" + jbossPackage.getImplementationVersion() +
"] Started in " + watch);
}
else
{
log.warn("could not get package info to display release, either the " +
"jar manifest in jboss-boot.jar has been mangled or you're " +
"running unit tests from ant outside of JBoss itself.");
}
}
private ObjectName createMBean(final String classname, String name)
throws Exception
{
ObjectName mbeanName = null;
if( name != null )
mbeanName = new ObjectName(name);
try
{
URL xmbeanDD = new URL("resource:xmdesc/"+classname+"-xmbean.xml");
Object resource = server.instantiate(classname);
Object[] args = {resource, xmbeanDD};
String[] sig = {Object.class.getName(), URL.class.getName()};
ObjectInstance instance = server.createMBean("org.jboss.mx.modelmbean.XMBean",
mbeanName,
bootstrapUCLName,
args,
sig);
mbeanName = instance.getObjectName();
log.debug("Created system XMBean: "+mbeanName);
}
catch(Exception e)
{
log.debug("Failed to create xmbean for: "+classname);
mbeanName = server.createMBean(classname, mbeanName).getObjectName();
log.debug("Created system MBean: " + mbeanName);
}
return mbeanName;
}
private ObjectName startBootService(final ObjectName controllerName,
final String classname, final String name)
throws Exception
{
ObjectName mbeanName = createMBean(classname, name);
Object[] args = { mbeanName };
String[] sig = { ObjectName.class.getName() };
server.invoke(controllerName, "create", args, sig);
server.invoke(controllerName, "start", args, sig);
return mbeanName;
}
private RepositoryClassLoader initBootLibraries() throws Exception
{
List list = new ArrayList();
URL patchURL = config.getPatchURL();
if (patchURL != null)
{
if (patchURL.getProtocol().equals("file"))
{
File dir = new File(patchURL.getFile());
if (dir.exists())
{
list.add(dir.toURL());
File[] jars = dir.listFiles(new FileSuffixFilter(new String[] { ".jar", ".zip" }, true));
for (int j = 0; jars != null && j < jars.length; j++)
{
list.add(jars[j].getCanonicalFile().toURL());
}
}
}
else
{
list.add(patchURL);
}
}
list.add(config.getServerConfigURL());
log.debug("Boot url list: " + list);
RepositoryClassLoader loader = null;
for (Iterator iter = list.iterator(); iter.hasNext();)
{
URL url = (URL)iter.next();
log.debug("Creating loader for URL: " + url);
Object[] args = {url, Boolean.TRUE};
String[] sig = {"java.net.URL", "boolean"};
loader = (RepositoryClassLoader) server.invoke(DEFAULT_LOADER_NAME, "newClassLoader", args, sig);
}
return loader;
}
public void shutdown() throws IllegalStateException
{
if (!started)
throw new IllegalStateException("not started");
final ServerImpl server = this;
log.debug("Shutting down");
boolean exitOnShutdown = config.getExitOnShutdown();
boolean blockingShutdown = config.getBlockingShutdown();
log.debug("exitOnShutdown: " + exitOnShutdown);
log.debug("blockingShutdown: " + blockingShutdown);
lifeThread.interrupt();
if (exitOnShutdown)
{
server.exit(0);
}
else if (blockingShutdown)
{
shutdownHook.shutdown();
} else
{
new Thread()
{
public void run()
{
shutdownHook.shutdown();
}
}.start();
}
}
public void exit(final int exitcode)
{
new Thread()
{
public void run()
{
log.info("Shutting down the JVM now!");
Runtime.getRuntime().exit(exitcode);
}
}.start();
}
public void exit()
{
exit(1);
}
public void halt(final int exitcode)
{
Notification msg = new Notification(STOP_NOTIFICATION_TYPE, this, 2);
sendNotification(msg);
new Thread()
{
public void run()
{
System.err.println("Halting the system now!");
Runtime.getRuntime().halt(exitcode);
}
}.start();
}
public void halt()
{
halt(1);
}
private void logMemoryUsage(final Runtime rt)
{
log.info("Total/free memory: " + rt.totalMemory() + "/" + rt.freeMemory());
}
public void runGarbageCollector()
{
Runtime rt = Runtime.getRuntime();
logMemoryUsage(rt);
rt.gc();
log.info("Hinted to the JVM to run garbage collection");
logMemoryUsage(rt);
}
public void runFinalization()
{
Runtime.getRuntime().runFinalization();
log.info("Hinted to the JVM to run any pending object finalizations");
}
public void traceMethodCalls(final Boolean flag)
{
Runtime.getRuntime().traceMethodCalls(flag.booleanValue());
}
public void traceInstructions(final Boolean flag)
{
Runtime.getRuntime().traceInstructions(flag.booleanValue());
}
public Date getStartDate()
{
return startDate;
}
public String getVersion()
{
return version.toString();
}
public String getVersionName()
{
return version.getName();
}
public String getBuildNumber()
{
return version.getBuildNumber();
}
public String getBuildJVM()
{
return version.getBuildJVM();
}
public String getBuildOS()
{
return version.getBuildOS();
}
public String getBuildID()
{
return version.getBuildID();
}
public String getBuildDate()
{
return version.getBuildDate();
}
public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback)
{
broadcasterSupport.addNotificationListener(listener, filter, handback);
}
public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException
{
broadcasterSupport.removeNotificationListener(listener);
}
public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback)
throws ListenerNotFoundException
{
broadcasterSupport.removeNotificationListener(listener, filter, handback);
}
public MBeanNotificationInfo[] getNotificationInfo()
{
return broadcasterSupport.getNotificationInfo();
}
public void sendNotification(Notification notification)
{
broadcasterSupport.sendNotification(notification);
}
private class LifeThread
extends Thread
{
Object lock = new Object();
LifeThread()
{
super("JBossLifeThread");
}
public void run()
{
synchronized (lock)
{
try
{
lock.wait();
}
catch (InterruptedException ignore)
{
}
}
log.info("LifeThread.run exits!");
}
}
private class ShutdownHook
extends Thread
{
private ObjectName controller;
private ObjectName mainDeployer;
private boolean forceHalt = true;
public ShutdownHook(final ObjectName controller, final ObjectName mainDeployer)
{
super("JBoss Shutdown Hook");
this.controller = controller;
this.mainDeployer = mainDeployer;
String value = System.getProperty("jboss.shutdown.forceHalt", null);
if (value != null) {
forceHalt = new Boolean(value).booleanValue();
}
}
public void run()
{
shutdown();
if (forceHalt) {
System.out.println("Halting VM");
Runtime.getRuntime().halt(0);
}
}
public void shutdown()
{
log.info("JBoss SHUTDOWN: Undeploying all packages");
shutdownDeployments();
log.debug("Shutting down all services");
System.out.println("Shutting down");
shutdownServices();
removeMBeans();
log.debug("Deleting server tmp/deploy directory");
File tmp = config.getServerTempDir();
File tmpDeploy = new File(tmp, "deploy");
Files.delete(tmpDeploy);
log.info("Shutdown complete");
System.out.println("Shutdown complete");
}
protected void shutdownDeployments()
{
try
{
server.invoke(mainDeployer,
"shutdown",
new Object[0],
new String[0]);
}
catch (Exception e)
{
Throwable t = JMXExceptionDecoder.decode(e);
log.error("Failed to shutdown deployer", t);
}
}
protected void shutdownServices()
{
try
{
server.invoke(controller,
"shutdown",
new Object[0],
new String[0]);
}
catch (Exception e)
{
Throwable t = JMXExceptionDecoder.decode(e);
log.error("Failed to shutdown services", t);
}
}
protected void removeMBeans()
{
try
{
server.unregisterMBean(ServiceControllerMBean.OBJECT_NAME);
server.unregisterMBean(ServerConfigImplMBean.OBJECT_NAME);
server.unregisterMBean(ServerImplMBean.OBJECT_NAME);
}
catch (Exception e)
{
Throwable t = JMXExceptionDecoder.decode(e);
log.error("Failed to unregister mbeans", t);
}
try
{
MBeanServerFactory.releaseMBeanServer(server);
}
catch (Exception e)
{
Throwable t = JMXExceptionDecoder.decode(e);
log.error("Failed to release mbean server", t);
}
}
}
}