From 3fea82d8a44c4c2d610eef6f9aff26a4112b258f Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 10 Dec 2009 23:21:06 +0000 Subject: [PATCH] 296765 JMX Connector Server and ShutdownThread git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1140 7e9141cc-0065-0410-87d8-b60c137991c4 --- VERSION.txt | 1 + jetty-jmx/src/main/config/etc/jetty-jmx.xml | 93 +++++---- .../eclipse/jetty/jmx/ConnectorServer.java | 101 ++++++++++ .../org/eclipse/jetty/jmx/MBeanContainer.java | 181 +++++++++++------- .../java/org/eclipse/jetty/server/Server.java | 123 +----------- .../jetty/util/thread/ShutdownThread.java | 103 ++++++++++ 6 files changed, 374 insertions(+), 228 deletions(-) create mode 100644 jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java create mode 100644 jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java diff --git a/VERSION.txt b/VERSION.txt index 3e1cd0b9afa..fa8c062e9e9 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,6 +1,7 @@ jetty-7.0.2-SNAPSHOT + JSON parses NaN as null + 290765 Reset input for HttpExchange retry. + + 296765 JMX Connector Server and ShutdownThread + 297421 Hide server/system classes from WebAppClassLoader.getResources jetty-7.0.1.v20091125 25 November 2009 diff --git a/jetty-jmx/src/main/config/etc/jetty-jmx.xml b/jetty-jmx/src/main/config/etc/jetty-jmx.xml index 2d48b6ca20e..4df07109f30 100644 --- a/jetty-jmx/src/main/config/etc/jetty-jmx.xml +++ b/jetty-jmx/src/main/config/etc/jetty-jmx.xml @@ -9,51 +9,62 @@ - - - - + + + + - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + - - service:jmx:rmi:///jndi/rmi:///jettymbeanserver - + - - - - --> - + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java new file mode 100644 index 00000000000..db0f8d2feb1 --- /dev/null +++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java @@ -0,0 +1,101 @@ +// ======================================================================== +// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.jmx; + +import java.lang.management.ManagementFactory; +import java.util.Map; + +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.thread.ShutdownThread; + + +/* ------------------------------------------------------------ */ +/** + * AbstractLifeCycle wrapper for JMXConnector Server + */ +public class ConnectorServer extends AbstractLifeCycle +{ + JMXConnectorServer _connectorServer; + + /* ------------------------------------------------------------ */ + /** + * Constructs connector server + * + * @param serviceURL the address of the new connector server. + * The actual address of the new connector server, as returned + * by its getAddress method, will not necessarily be exactly the same. + * @param name object name string to be assigned to connector server bean + * @throws Exception + */ + public ConnectorServer(JMXServiceURL serviceURL, String name) + throws Exception + { + this(serviceURL, null, name); + } + + /* ------------------------------------------------------------ */ + /** + * Constructs connector server + * + * @param serviceURL the address of the new connector server. + * The actual address of the new connector server, as returned + * by its getAddress method, will not necessarily be exactly the same. + * @param environment a set of attributes to control the new connector + * server's behavior. This parameter can be null. Keys in this map must + * be Strings. The appropriate type of each associated value depends on + * the attribute. The contents of environment are not changed by this call. + * @param name object name string to be assigned to connector server bean + * @throws Exception + */ + public ConnectorServer(JMXServiceURL serviceURL, Map environment, String name) + throws Exception + { + MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer(); + _connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(serviceURL, environment, mbeanServer); + mbeanServer.registerMBean(_connectorServer,new ObjectName(name)); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() + */ + @Override + public void doStart() + throws Exception + { + _connectorServer.start(); + ShutdownThread.register(0, this); + + Log.info("JMX Remote URL: {}", _connectorServer.getAddress().toString()); + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop() + */ + @Override + public void doStop() + throws Exception + { + ShutdownThread.deregister(this); + _connectorServer.stop(); + } +} diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java index f16477379ce..cbf35f6dfbe 100644 --- a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java +++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java @@ -15,9 +15,10 @@ package org.eclipse.jetty.jmx; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; import javax.management.MBeanServer; @@ -30,27 +31,45 @@ import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.Container; import org.eclipse.jetty.util.component.Container.Relationship; import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.thread.ShutdownThread; + +/* ------------------------------------------------------------ */ +/** + * Container class for the MBean instances + */ public class MBeanContainer extends AbstractLifeCycle implements Container.Listener { private final MBeanServer _server; - private final WeakHashMap _beans = new WeakHashMap(); - private final HashMap _unique = new HashMap(); + private final WeakHashMap _beans = new WeakHashMap(); + private final HashMap _unique = new HashMap(); + private final MultiMap _relations = new MultiMap(); private String _domain = null; - private MultiMap _relations = new MultiMap(); - + /* ------------------------------------------------------------ */ + /** + * Lookup an object name by instance + * + * @param object instance for which object name is looked up + * @return object name associated with specified instance, or null if not found + */ public synchronized ObjectName findMBean(Object object) { ObjectName bean = (ObjectName)_beans.get(object); return bean==null?null:bean; } + /* ------------------------------------------------------------ */ + /** + * Lookup an instance by object name + * + * @param oname object name of instance + * @return instance associated with specified object name, or null if not found + */ public synchronized Object findBean(ObjectName oname) { - for (Iterator iter = _beans.entrySet().iterator(); iter.hasNext();) + for (Map.Entry entry : _beans.entrySet()) { - Map.Entry entry = (Map.Entry) iter.next(); ObjectName bean = (ObjectName)entry.getValue(); if (bean.equals(oname)) return entry.getKey(); @@ -58,30 +77,65 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste return null; } + /* ------------------------------------------------------------ */ + /** + * Constructs MBeanContainer + * + * @param server instance of MBeanServer for use by container + */ public MBeanContainer(MBeanServer server) { - this._server = server; + _server = server; + + try + { + start(); + } + catch (Exception e) + { + Log.ignore(e); + } } + /* ------------------------------------------------------------ */ + /** + * Retrieve instance of MBeanServer used by container + * + * @return instance of MBeanServer + */ public MBeanServer getMBeanServer() { return _server; } + /* ------------------------------------------------------------ */ + /** + * Set domain to be used to add MBeans + * + * @param domain domain name + */ public void setDomain (String domain) { - _domain =domain; + _domain = domain; } + /* ------------------------------------------------------------ */ + /** + * Retrieve domain name used to add MBeans + * + * @return domain name + */ public String getDomain() { return _domain; } - public void doStart() - { - } - + /* ------------------------------------------------------------ */ + /** + * Implementation of Container.Listener interface + * + * @see org.eclipse.jetty.util.component.Container.Listener#add(org.eclipse.jetty.util.component.Container.Relationship) + */ public synchronized void add(Relationship relationship) { ObjectName parent=(ObjectName)_beans.get(relationship.getParent()); @@ -100,10 +154,14 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste if (parent!=null && child!=null) _relations.add(parent,relationship); - - } + /* ------------------------------------------------------------ */ + /** + * Implementation of Container.Listener interface + * + * @see org.eclipse.jetty.util.component.Container.Listener#remove(org.eclipse.jetty.util.component.Container.Relationship) + */ public synchronized void remove(Relationship relationship) { ObjectName parent=(ObjectName)_beans.get(relationship.getParent()); @@ -112,21 +170,26 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste _relations.removeValue(parent,relationship); } + /* ------------------------------------------------------------ */ + /** + * Implementation of Container.Listener interface + * + * @see org.eclipse.jetty.util.component.Container.Listener#removeBean(java.lang.Object) + */ public synchronized void removeBean(Object obj) { ObjectName bean=(ObjectName)_beans.remove(obj); if (bean!=null) { - List r=_relations.getValues(bean); - if (r!=null && r.size()>0) + List beanRelations = _relations.getValues(bean); + if (beanRelations!=null && beanRelations.size()>0) { - Log.debug("Unregister {}", r); - Iterator iter = new ArrayList(r).iterator(); - while (iter.hasNext()) + Log.debug("Unregister {}", beanRelations); + List removeList = new ArrayList(beanRelations); + for (Relationship relation : removeList) { - Relationship rel = (Relationship)iter.next(); - rel.getContainer().update(rel.getParent(),rel.getChild(),null,rel.getRelationship(),true); + relation.getContainer().update(relation.getParent(),relation.getChild(),null,relation.getRelationship(),true); } } @@ -146,6 +209,12 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste } } + /* ------------------------------------------------------------ */ + /** + * Implementation of Container.Listener interface + * + * @see org.eclipse.jetty.util.component.Container.Listener#addBean(java.lang.Object) + */ public synchronized void addBean(Object obj) { try @@ -218,57 +287,29 @@ public class MBeanContainer extends AbstractLifeCycle implements Container.Liste } } + /* ------------------------------------------------------------ */ + /** + * Perform actions needed to start lifecycle + * + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() + */ + public void doStart() + { + ShutdownThread.register(this); + } + + /* ------------------------------------------------------------ */ + /** + * Perform actions needed to stop lifecycle + * + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop() + */ public void doStop() { - while (_beans.size()>0) - removeBean(_beans.keySet().iterator().next()); - } - - private class ShutdownHook extends Thread - { - private final ObjectName mletName; - private final ObjectName adaptorName; - private final ObjectName processorName; - - public ShutdownHook(ObjectName mletName, ObjectName adaptorName, ObjectName processorName) + Set removeSet = new HashSet(_beans.keySet()); + for (Object removeObj : removeSet) { - this.mletName = mletName; - this.adaptorName = adaptorName; - this.processorName = processorName; - } - - public void run() - { - halt(); - unregister(processorName); - unregister(adaptorName); - unregister(mletName); - } - - private void halt() - { - try - { - _server.invoke(adaptorName, "stop", null, null); - } - catch (Exception e) - { - Log.warn(e); - } - } - - private void unregister(ObjectName objectName) - { - try - { - _server.unregisterMBean(objectName); - Log.debug("Unregistered " + objectName); - } - catch (Exception e) - { - Log.warn(e); - } + removeBean(removeObj); } } - } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 14cf632ea31..ec43ac15140 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -41,6 +41,7 @@ import org.eclipse.jetty.util.component.Container; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ShutdownThread; import org.eclipse.jetty.util.thread.ThreadPool; /* ------------------------------------------------------------ */ @@ -54,7 +55,6 @@ import org.eclipse.jetty.util.thread.ThreadPool; */ public class Server extends HandlerWrapper implements Attributes { - private static final ShutdownHookThread hookThread = new ShutdownHookThread(); private static final String _version; static { @@ -120,7 +120,9 @@ public class Server extends HandlerWrapper implements Attributes { _stopAtShutdown=stop; if (stop) - hookThread.add(this); + ShutdownThread.register(this); + else + ShutdownThread.deregister(this); } /* ------------------------------------------------------------ */ @@ -189,7 +191,7 @@ public class Server extends HandlerWrapper implements Attributes protected void doStart() throws Exception { if (getStopAtShutdown()) - hookThread.add(this); + ShutdownThread.register(this); Log.info("jetty-"+_version); HttpGenerator.setServerVersion(_version); @@ -309,7 +311,7 @@ public class Server extends HandlerWrapper implements Attributes } mex.ifExceptionThrow(); - hookThread.remove(this); + ShutdownThread.deregister(this); } /* ------------------------------------------------------------ */ @@ -517,119 +519,6 @@ public class Server extends HandlerWrapper implements Attributes _dependentBeans.remove(o); _container.removeBean(o); } - - - - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - /** - * ShutdownHook thread for stopping all servers. - * - * Thread is hooked first time list of servers is changed. - */ - private static class ShutdownHookThread extends Thread - { - private boolean _hooked = false; - private final Set _servers = new CopyOnWriteArraySet(); - - /** - * Hooks this thread for shutdown. - * - * @see java.lang.Runtime#addShutdownHook(java.lang.Thread) - */ - private void createShutdownHook() - { - if (!_hooked) - { - try - { - Method shutdownHook = java.lang.Runtime.class.getMethod("addShutdownHook", Thread.class); - shutdownHook.invoke(Runtime.getRuntime(), this); - _hooked = true; - } - catch (Exception e) - { - if (Log.isDebugEnabled()) - Log.debug("No shutdown hook in JVM ", e); - } - } - } - - /** - * Add Server to servers list. - */ - public void add(Server server) - { - _servers.add(server); - if (server.getStopAtShutdown()) - createShutdownHook(); - } - - /** - * Contains Server in servers list? - */ - public boolean contains(Server server) - { - return _servers.contains(server); - } - - public Iterable getServers() - { - return _servers; - } - - /** - * Clear list of Servers. - */ - public void clear() - { - _servers.clear(); - } - - /** - * Remove Server from list. - */ - public boolean remove(Server server) - { - createShutdownHook(); - return _servers.remove(server); - } - - /** - * Stop all Servers in list. - */ - @Override - public void run() - { - setName("Shutdown"); - Log.info("Shutdown hook executing"); - for (Server svr : _servers) - { - if (svr == null || !svr.getStopAtShutdown() || !svr.isRunning()) - continue; - try - { - svr.stop(); - } - catch (Exception e) - { - Log.warn(e); - } - Log.info("Shutdown hook complete"); - - // Try to avoid JVM crash - try - { - Thread.sleep(1000); - } - catch (Exception e) - { - Log.warn(e); - } - } - } - } /* ------------------------------------------------------------ */ /* diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java new file mode 100644 index 00000000000..bae25570e63 --- /dev/null +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java @@ -0,0 +1,103 @@ +// ======================================================================== +// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== + +package org.eclipse.jetty.util.thread; + +import java.io.ObjectStreamException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.log.Log; + + +/* ------------------------------------------------------------ */ +/** + * ShutdownThread is a shutdown hook thread implemented as + * singleton that maintains a list of lifecycle instances + * that are registered with it and provides ability to stop + * these lifecycles upon shutdown of the Java Virtual Machine + */ +public class ShutdownThread extends Thread +{ + private static final ShutdownThread _thread = new ShutdownThread(); + + private final List _lifeCycles = new CopyOnWriteArrayList(); + + /* ------------------------------------------------------------ */ + /** + * Default constructor for the singleton + * + * Registers the instance as shutdown hook with the Java Runtime + */ + private ShutdownThread() + { + Runtime.getRuntime().addShutdownHook(this); + } + + /* ------------------------------------------------------------ */ + /** + * Returns the instance of the singleton + * + * @return + */ + public static ShutdownThread getInstance() + { + return _thread; + } + + /* ------------------------------------------------------------ */ + public static synchronized void register(LifeCycle... lifeCycles) + { + _thread._lifeCycles.addAll(Arrays.asList(lifeCycles)); + } + + /* ------------------------------------------------------------ */ + public static synchronized void register(int index, LifeCycle... lifeCycles) + { + _thread._lifeCycles.addAll(index,Arrays.asList(lifeCycles)); + } + + /* ------------------------------------------------------------ */ + public static synchronized void deregister(LifeCycle lifeCycle) + { + _thread._lifeCycles.remove(lifeCycle); + } + + /* ------------------------------------------------------------ */ + public void run() + { + for (LifeCycle lifeCycle : _thread._lifeCycles) + { + try + { + lifeCycle.stop(); + Log.debug("Stopped " + lifeCycle); + } + catch (Exception ex) + { + Log.debug(ex); + } + } + } + + /* ------------------------------------------------------------ */ + protected Object readResolve() + throws ObjectStreamException + { + return _thread; + } +} \ No newline at end of file