Author: pferraro
Date: 2008-09-12 18:18:06 -0400 (Fri, 12 Sep 2008)
New Revision: 1819
Added:
trunk/mod_cluster/src/main/java/org/jboss/modcluster/ClusterListener.java
trunk/mod_cluster/src/test/java/org/jboss/modcluster/ClusterListenerTestCase.java
Log:
Stand-alone jbossweb lifecycle listener
Added: trunk/mod_cluster/src/main/java/org/jboss/modcluster/ClusterListener.java
===================================================================
--- trunk/mod_cluster/src/main/java/org/jboss/modcluster/ClusterListener.java
(rev 0)
+++ trunk/mod_cluster/src/main/java/org/jboss/modcluster/ClusterListener.java 2008-09-12
22:18:06 UTC (rev 1819)
@@ -0,0 +1,230 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.jboss.modcluster;
+
+import java.util.List;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.ContainerListener;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.Service;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.modeler.Registry;
+import org.jboss.logging.Logger;
+import org.jboss.modcluster.config.ModClusterConfig;
+import org.jboss.modcluster.mcmp.MCMPHandler;
+import org.jboss.modcluster.mcmp.MCMPRequest;
+import org.jboss.modcluster.mcmp.MCMPUtils;
+import org.jboss.modcluster.mcmp.ResetRequestSource;
+import org.jboss.modcluster.mcmp.impl.DefaultMCMPHandler;
+
+/**
+ * This listener communicates with a front end mod_cluster enabled proxy to
+ * automatically maintain the node configuration according to what is
+ * deployed.
+ */
+public class ClusterListener extends ModClusterConfig
+ implements LifecycleListener, ContainerListener, ResetRequestSource
+{
+ private static final Logger log = Logger.getLogger(ClusterListener.class);
+
+ /** The string manager for this package. */
+ private final StringManager sm = StringManager.getManager(Constants.Package);
+
+ // ----------------------------------------------------------------- Fields
+
+ private final LifecycleListener lifecycleListener;
+ private final ContainerListener containerListener;
+ private final MCMPHandler mcmpHandler;
+
+ // ----------------------------------------------------------- Constructors
+
+ public ClusterListener()
+ {
+ this.mcmpHandler = new DefaultMCMPHandler(this, this);
+
+ JBossWebEventHandler eventHandler = new DefaultJBossWebEventHandler(this, this,
this, this.mcmpHandler, this);
+
+ BasicClusterListener listener = new BasicClusterListener(eventHandler);
+
+ this.lifecycleListener = listener;
+ this.containerListener = listener;
+ }
+
+ protected ClusterListener(MCMPHandler mcmpHandler, LifecycleListener
lifecycleListener, ContainerListener containerListener)
+ {
+ this.mcmpHandler = mcmpHandler;
+ this.lifecycleListener = lifecycleListener;
+ this.containerListener = containerListener;
+ }
+
+ // ---------------------------------------------------- ResetRequestSource
+
+ /**
+ * Reset configuration for a particular proxy following an error.
+ */
+ public List<MCMPRequest> getResetRequests()
+ {
+ return MCMPUtils.getResetRequests(ServerFactory.getServer(), this, this);
+ }
+
+ //----------------------------------------------------------------- Public
+
+ /**
+ * Retrieves the full proxy configuration. To be used through JMX or similar.
+ *
+ * response: HTTP/1.1 200 OK
+ * response:
+ * node: [1:1] JVMRoute: node1 Domain: [bla] Host: 127.0.0.1 Port: 8009 Type: ajp
+ * host: 1 [] vhost: 1 node: 1
+ * context: 1 [/] vhost: 1 node: 1 status: 1
+ * context: 2 [/myapp] vhost: 1 node: 1 status: 1
+ * context: 3 [/host-manager] vhost: 1 node: 1 status: 1
+ * context: 4 [/docs] vhost: 1 node: 1 status: 1
+ * context: 5 [/manager] vhost: 1 node: 1 status: 1
+ *
+ * @return the proxy confguration
+ */
+ public String getProxyConfiguration()
+ {
+ return this.mcmpHandler.getProxyConfiguration();
+ }
+
+ /**
+ * Reset a DOWN connection to the proxy up to ERROR, where the configuration will
+ * be refreshed. To be used through JMX or similar.
+ */
+ public void reset()
+ {
+ this.mcmpHandler.reset();
+ }
+
+ /**
+ * Refresh configuration. To be used through JMX or similar.
+ */
+ public void refresh()
+ {
+ // Set as error, and the periodic event will refresh the configuration
+ this.mcmpHandler.markProxiesInError();
+ }
+
+ /**
+ * Disable all webapps for all engines. To be used through JMX or similar.
+ */
+ public boolean disable()
+ {
+ Service[] services = ServerFactory.getServer().findServices();
+ for (Service service: services)
+ {
+ Engine engine = (Engine) service.getContainer();
+ // Send DISABLE-APP * request
+ MCMPRequest request = MCMPUtils.createDisableEngineRequest(engine);
+ this.mcmpHandler.sendRequest(request);
+ }
+ return this.mcmpHandler.isProxyHealthOK();
+ }
+
+ /**
+ * Enable all webapps for all engines. To be used through JMX or similar.
+ */
+ public boolean enable()
+ {
+ Service[] services = ServerFactory.getServer().findServices();
+ for (Service service: services)
+ {
+ Engine engine = (Engine) service.getContainer();
+ // Send ENABLE-APP * request
+ MCMPRequest request = MCMPUtils.createEnableEngineRequest(engine);
+ this.mcmpHandler.sendRequest(request);
+ }
+ return this.mcmpHandler.isProxyHealthOK();
+ }
+
+ /**
+ * @{inheritDoc}
+ * @see
org.apache.catalina.ContainerListener#containerEvent(org.apache.catalina.ContainerEvent)
+ */
+ public void containerEvent(ContainerEvent event)
+ {
+ this.containerListener.containerEvent(event);
+ }
+
+ /**
+ * @{inheritDoc}
+ * @see
org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)
+ */
+ public void lifecycleEvent(LifecycleEvent event)
+ {
+ this.lifecycleListener.lifecycleEvent(event);
+
+ Lifecycle source = event.getLifecycle();
+
+ if (source instanceof Server)
+ {
+ Server server = (Server) source;
+
+ // Register/unregister ClusterListener mbean on server start/stop
+ if (Lifecycle.AFTER_START_EVENT.equals(event.getType()))
+ {
+ try
+ {
+ ObjectName name = this.getObjectName(server);
+
+ Registry.getRegistry(null, null).registerComponent(this, name, null);
+ }
+ catch (Exception e)
+ {
+ log.error(this.sm.getString("modcluster.error.jmxRregister"),
e);
+ }
+ }
+ else if (Lifecycle.STOP_EVENT.equals(event.getType()))
+ {
+ try
+ {
+ ObjectName name = this.getObjectName(server);
+
+ Registry.getRegistry(null, null).unregisterComponent(name);
+ }
+ catch (Exception e)
+ {
+ log.error(this.sm.getString("modcluster.error.jmxUnregister"),
e);
+ }
+ }
+ }
+ }
+
+ private ObjectName getObjectName(Server server) throws MalformedObjectNameException
+ {
+ String domain = (String) IntrospectionUtils.getProperty(server,
"domain");
+ return ObjectName.getInstance(domain , "type",
"ClusterListener");
+ }
+}
\ No newline at end of file
Added: trunk/mod_cluster/src/test/java/org/jboss/modcluster/ClusterListenerTestCase.java
===================================================================
--- trunk/mod_cluster/src/test/java/org/jboss/modcluster/ClusterListenerTestCase.java
(rev 0)
+++
trunk/mod_cluster/src/test/java/org/jboss/modcluster/ClusterListenerTestCase.java 2008-09-12
22:18:06 UTC (rev 1819)
@@ -0,0 +1,346 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.modcluster;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import junit.framework.TestCase;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerEvent;
+import org.apache.catalina.ContainerListener;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+import org.apache.catalina.ServerFactory;
+import org.apache.catalina.Service;
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.modeler.Registry;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.jboss.modcluster.mcmp.MCMPHandler;
+import org.jboss.modcluster.mcmp.MCMPRequest;
+import org.jboss.modcluster.mcmp.MCMPRequestType;
+
+/**
+ * @author Paul Ferraro
+ *
+ */
+public class ClusterListenerTestCase extends TestCase
+{
+ private static final LifecycleServer server =
EasyMock.createStrictMock(LifecycleServer.class);
+ {
+ ServerFactory.setServer(server);
+ }
+
+ private final MCMPHandler mcmpHandler = EasyMock.createStrictMock(MCMPHandler.class);
+ private final LifecycleListener lifecycleListener =
EasyMock.createStrictMock(LifecycleListener.class);
+ private final ContainerListener containerListener =
EasyMock.createStrictMock(ContainerListener.class);
+
+ private final ClusterListener listener = new ClusterListener(this.mcmpHandler,
this.lifecycleListener, this.containerListener);
+
+ public void testGetResetRequests() throws Exception
+ {
+ Service service = EasyMock.createStrictMock(Service.class);
+ Engine engine = EasyMock.createStrictMock(Engine.class);
+ Container container = EasyMock.createStrictMock(Container.class);
+ Context context = EasyMock.createStrictMock(Context.class);
+ Host host = EasyMock.createStrictMock(Host.class);
+ Connector connector = new Connector("AJP/1.3");
+
+ this.listener.setDomain("domain");
+ this.listener.setFlushPackets(true);
+ this.listener.setFlushWait(1);
+ this.listener.setPing(2);
+ this.listener.setSmax(3);
+ this.listener.setTtl(4);
+ this.listener.setNodeTimeout(5);
+ this.listener.setBalancer("S");
+ this.listener.setStickySession(false);
+ this.listener.setStickySessionRemove(true);
+ this.listener.setStickySessionForce(false);
+ this.listener.setWorkerTimeout(6);
+ this.listener.setMaxAttempts(7);
+
+ EasyMock.expect(server.findServices()).andReturn(new Service[] { service });
+ EasyMock.expect(service.getContainer()).andReturn(engine);
+
+ // create remove-all request
+ EasyMock.expect(engine.getJvmRoute()).andReturn("host1").times(2);
+
+ // create config request
+ EasyMock.expect(engine.getService()).andReturn(service);
+ EasyMock.expect(service.findConnectors()).andReturn(new Connector[] { connector
});
+ EasyMock.expect(engine.getJvmRoute()).andReturn("host1");
+
+ EasyMock.expect(engine.findChildren()).andReturn(new Container[] { container });
+ EasyMock.expect(container.findChildren()).andReturn(new Container[] { context });
+ EasyMock.expect(context.isStarted()).andReturn(true);
+
+ // create enable-app request
+ EasyMock.expect(context.getParent()).andReturn(container);
+ EasyMock.expect(container.getParent()).andReturn(engine);
+ EasyMock.expect(engine.getJvmRoute()).andReturn("host1");
+ EasyMock.expect(context.getPath()).andReturn("/context");
+ EasyMock.expect(context.getParent()).andReturn(host);
+ EasyMock.expect(host.getName()).andReturn("host");
+ EasyMock.expect(host.findAliases()).andReturn(new String[] { "alias1",
"alias2" });
+
+ EasyMock.replay(server, service, engine, container, context, host);
+
+ List<MCMPRequest> requests = this.listener.getResetRequests();
+
+ EasyMock.verify(server, service, engine, container, context, host);
+
+ MCMPRequest request = requests.get(0);
+ Map<String, String> parameters = request.getParameters();
+
+ assertSame(MCMPRequestType.REMOVE_APP, request.getRequestType());
+ assertTrue(request.isWildcard());
+ assertEquals(1, parameters.size());
+ assertEquals("host1", parameters.get("JVMRoute"));
+
+ request = requests.get(1);
+ parameters = request.getParameters();
+
+ assertSame(MCMPRequestType.CONFIG, request.getRequestType());
+ assertFalse(request.isWildcard());
+ assertEquals(17, parameters.size());
+ assertEquals("host1", parameters.get("JVMRoute"));
+ assertEquals("172.0.0.1", parameters.get("Host"));
+ assertEquals("0", parameters.get("Port"));
+ assertEquals("ajp", parameters.get("Type"));
+ assertEquals("domain", parameters.get("Domain"));
+ assertEquals("On", parameters.get("flushpackets"));
+ assertEquals("1", parameters.get("flushwait"));
+ assertEquals("2", parameters.get("ping"));
+ assertEquals("3", parameters.get("smax"));
+ assertEquals("4", parameters.get("ttl"));
+ assertEquals("5", parameters.get("Timeout"));
+ assertEquals("S", parameters.get("Balancer"));
+ assertEquals("No", parameters.get("StickySession"));
+ assertEquals("Yes", parameters.get("StickySessionRemove"));
+ assertEquals("No", parameters.get("StickySessionForce"));
+ assertEquals("6", parameters.get("WaitWorker"));
+ assertEquals("7", parameters.get("Maxattempts"));
+
+ request = requests.get(2);
+ parameters = request.getParameters();
+
+ assertSame(MCMPRequestType.ENABLE_APP, request.getRequestType());
+ assertFalse(request.isWildcard());
+ assertEquals(3, parameters.size());
+ assertEquals("host1", parameters.get("JVMRoute"));
+ assertEquals("/context", parameters.get("Context"));
+ assertEquals("host,alias1,alias2", parameters.get("Alias"));
+
+ EasyMock.reset(server, service, engine, container, context, host);
+ }
+
+ public void testGetProxyConfiguration()
+ {
+
EasyMock.expect(this.mcmpHandler.getProxyConfiguration()).andReturn("config");
+
+ EasyMock.replay(this.mcmpHandler);
+
+ String result = this.listener.getProxyConfiguration();
+
+ EasyMock.verify(this.mcmpHandler);
+
+ assertEquals("config", result);
+
+ EasyMock.reset(this.mcmpHandler);
+ }
+
+ public void testReset()
+ {
+ this.mcmpHandler.reset();
+
+ EasyMock.replay(this.mcmpHandler);
+
+ this.listener.reset();
+
+ EasyMock.verify(this.mcmpHandler);
+ EasyMock.reset(this.mcmpHandler);
+ }
+
+ public void testRefresh()
+ {
+ this.mcmpHandler.markProxiesInError();
+
+ EasyMock.replay(this.mcmpHandler);
+
+ this.listener.refresh();
+
+ EasyMock.verify(this.mcmpHandler);
+ EasyMock.reset(this.mcmpHandler);
+ }
+
+ public void testEnable()
+ {
+ Service service = EasyMock.createStrictMock(Service.class);
+ Engine engine = EasyMock.createStrictMock(Engine.class);
+ Capture<MCMPRequest> capturedRequest = new Capture<MCMPRequest>();
+
+ EasyMock.expect(server.findServices()).andReturn(new Service[] { service });
+ EasyMock.expect(service.getContainer()).andReturn(engine);
+ EasyMock.expect(engine.getJvmRoute()).andReturn("host1");
+
+ this.mcmpHandler.sendRequest(EasyMock.capture(capturedRequest));
+ EasyMock.expect(this.mcmpHandler.isProxyHealthOK()).andReturn(true);
+
+ EasyMock.replay(this.mcmpHandler, server, service, engine);
+
+ boolean result = this.listener.enable();
+
+ EasyMock.verify(this.mcmpHandler, server, service, engine);
+
+ assertTrue(result);
+
+ MCMPRequest request = capturedRequest.getValue();
+ Map<String, String> parameters = request.getParameters();
+
+ assertSame(MCMPRequestType.ENABLE_APP, request.getRequestType());
+ assertTrue(request.isWildcard());
+ assertEquals(1, parameters.size());
+ assertEquals("host1", parameters.get("JVMRoute"));
+
+ EasyMock.reset(this.mcmpHandler, server, service, engine);
+ }
+
+ public void testDisable()
+ {
+ Service service = EasyMock.createStrictMock(Service.class);
+ Engine engine = EasyMock.createStrictMock(Engine.class);
+ Capture<MCMPRequest> capturedRequest = new Capture<MCMPRequest>();
+
+ EasyMock.expect(server.findServices()).andReturn(new Service[] { service });
+ EasyMock.expect(service.getContainer()).andReturn(engine);
+ EasyMock.expect(engine.getJvmRoute()).andReturn("host1");
+
+ this.mcmpHandler.sendRequest(EasyMock.capture(capturedRequest));
+ EasyMock.expect(this.mcmpHandler.isProxyHealthOK()).andReturn(true);
+
+ EasyMock.replay(this.mcmpHandler, server, service, engine);
+
+ boolean result = this.listener.disable();
+
+ EasyMock.verify(this.mcmpHandler, server, service, engine);
+
+ assertTrue(result);
+
+ MCMPRequest request = capturedRequest.getValue();
+ Map<String, String> parameters = request.getParameters();
+
+ assertSame(MCMPRequestType.DISABLE_APP, request.getRequestType());
+ assertTrue(request.isWildcard());
+ assertEquals(1, parameters.size());
+ assertEquals("host1", parameters.get("JVMRoute"));
+
+ EasyMock.reset(this.mcmpHandler, server, service, engine);
+ }
+
+ public void testContainerEvent()
+ {
+ ContainerEvent event = new ContainerEvent(EasyMock.createMock(Container.class),
Container.ADD_CHILD_EVENT, null);
+
+ this.containerListener.containerEvent(event);
+
+ EasyMock.replay(this.containerListener);
+
+ this.listener.containerEvent(event);
+
+ EasyMock.verify(this.containerListener);
+ EasyMock.reset(this.containerListener);
+ }
+
+ public void testStartServerLifecycleEvent() throws MalformedObjectNameException
+ {
+ LifecycleEvent event = new LifecycleEvent(server, Lifecycle.AFTER_START_EVENT);
+
+ this.lifecycleListener.lifecycleEvent(event);
+
+ EasyMock.expect(server.getDomain()).andReturn("domain");
+
+ EasyMock.replay(this.lifecycleListener, server);
+
+ this.listener.lifecycleEvent(event);
+
+ EasyMock.verify(this.lifecycleListener, server);
+
+ Registry registry = Registry.getRegistry(null, null);
+ ObjectName name = ObjectName.getInstance("domain:type=ClusterListener");
+
+ assertTrue(registry.getMBeanServer().isRegistered(name));
+
+ EasyMock.reset(this.lifecycleListener, server);
+ }
+
+ public void testStopServerLifecycleEvent() throws MalformedObjectNameException
+ {
+ LifecycleEvent event = new LifecycleEvent(server, Lifecycle.STOP_EVENT);
+
+ this.lifecycleListener.lifecycleEvent(event);
+
+ EasyMock.expect(server.getDomain()).andReturn("domain");
+
+ EasyMock.replay(this.lifecycleListener, server);
+
+ this.listener.lifecycleEvent(event);
+
+ EasyMock.verify(this.lifecycleListener, server);
+
+ Registry registry = Registry.getRegistry(null, null);
+ ObjectName name = ObjectName.getInstance("domain:type=ClusterListener");
+
+ assertFalse(registry.getMBeanServer().isRegistered(name));
+
+ EasyMock.reset(this.lifecycleListener, server);
+ }
+
+ public void testOtherLifecycleEvent()
+ {
+ LifecycleEvent event = new LifecycleEvent(EasyMock.createMock(Lifecycle.class),
Lifecycle.START_EVENT);
+
+ this.lifecycleListener.lifecycleEvent(event);
+
+ EasyMock.replay(this.lifecycleListener);
+
+ this.listener.lifecycleEvent(event);
+
+ EasyMock.verify(this.lifecycleListener);
+ EasyMock.reset(this.lifecycleListener);
+ }
+
+ interface LifecycleServer extends Server, Lifecycle
+ {
+ String getDomain();
+ }
+}