[jboss-cvs] JBossAS SVN: r76052 - in trunk/tomcat: src/main/org/jboss/web/tomcat/service and 9 other directories.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Mon Jul 21 01:54:56 EDT 2008
Author: bstansberry at jboss.com
Date: 2008-07-21 01:54:56 -0400 (Mon, 21 Jul 2008)
New Revision: 76052
Added:
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/BasicClusterListener.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Constants.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/DefaultJBossWebEventHandler.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/JBossWebEventHandler.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/LocalStrings.properties
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ModClusterService.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Utils.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertiseEventType.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertiseListener.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertisedServer.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/BalancerConfiguration.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/MCMPHandlerConfiguration.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/ModClusterConfig.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/NodeConfiguration.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/SSLConfiguration.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ClusteredMCMPHandler.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ClusteredMCMPHandlerImpl.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/HASingletonAwareResetRequestSource.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ModClusterServiceDRMEntry.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ModClusterServiceHASingletonElectionPolicy.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/BooleanGroupRpcResponse.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/GroupRpcResponse.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/GroupRpcResponseFilter.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/InetAddressGroupRpcResponse.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/IntegerGroupRpcResponse.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/MCMPServerDiscoveryEvent.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ModClusterServiceStateGroupRpcResponse.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/PeerMCMPDiscoveryStatus.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ResetRequestGroupRpcResponse.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/StringGroupRpcResponse.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ThrowableGroupRpcResponse.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/LoadBalanceFactorProvider.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/impl/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/impl/StaticLoadBalanceFactorProvider.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mbeans-descriptors.xml
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/AddressPort.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPHandler.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPRequest.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPRequestType.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPServer.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPServerState.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPUtils.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/ResetRequestSource.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/DefaultMCMPHandler.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/JSSEKeyManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/JSSESocketFactory.java
Modified:
trunk/tomcat/build.xml
Log:
[JBAS-5659] Intra-clustering communication component for mod_cluster
Modified: trunk/tomcat/build.xml
===================================================================
--- trunk/tomcat/build.xml 2008-07-21 05:49:57 UTC (rev 76051)
+++ trunk/tomcat/build.xml 2008-07-21 05:54:56 UTC (rev 76052)
@@ -67,7 +67,8 @@
<path refid="jboss.metadata.classpath"/>
<path refid="jboss.jboss.cl.classpath"/>
<path refid="jboss.jboss.deployers.classpath"/>
- <path refid="jboss.jboss.man.classpath"/>
+ <path refid="jboss.jboss.man.classpath"/>
+ <path refid="jboss.jboss.mdr.classpath"/>
<path refid="jboss.microcontainer.classpath"/>
<path refid="jboss.jboss.vfs.classpath"/>
<path refid="jboss.integration.classpath"/>
@@ -76,6 +77,7 @@
<path refid="jboss.jboss.ha.server.api.classpath"/>
<path refid="jboss.jboss.jpa.deployers.classpath" />
<path refid="jboss.jboss.reflect.classpath" />
+ <path refid="net.jcip.classpath" />
</path>
<!-- ======= -->
@@ -102,6 +104,7 @@
<path refid="jboss.jca.classpath"/>
<path refid="jboss.test.classpath" />
<path refid="jboss.iiop.classpath"/>
+ <path refid="jboss.cluster.classpath"/>
</path>
<!-- The combined thirdparty classpath -->
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/BasicClusterListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/BasicClusterListener.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/BasicClusterListener.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,231 @@
+/*
+ * 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.web.tomcat.service.modcluster;
+
+
+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.Service;
+import org.apache.catalina.util.StringManager;
+import org.jboss.logging.Logger;
+
+
+
+/**
+ * This listener communicates with a front end mod_cluster enabled proxy to
+ * automatically maintain the node configuration according to what is
+ * deployed.
+ */
+public class BasicClusterListener
+ implements LifecycleListener, ContainerListener
+{
+ protected static Logger log = Logger.getLogger(BasicClusterListener.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+
+ // -------------------------------------------------------------- Constants
+
+
+ // ----------------------------------------------------------------- Fields
+
+ /**
+ * Initialization flag.
+ */
+ protected boolean init = false;
+
+ protected JBossWebEventHandler eventHandler;
+
+ // ----------------------------------------------------------- Constructors
+
+ public BasicClusterListener(JBossWebEventHandler eventHandler)
+ {
+ this.eventHandler = eventHandler;
+ }
+
+ protected BasicClusterListener()
+ {
+ }
+
+ // ------------------------------------------------------------- Properties
+
+
+
+ // ---------------------------------------------- LifecycleListener Methods
+
+
+ /**
+ * Acknowledge the occurrence of the specified event.
+ * Note: Will never be called when the listener is associated to a Server,
+ * since it is not a Container.
+ *
+ * @param event ContainerEvent that has occurred
+ */
+ public void containerEvent(ContainerEvent event) {
+
+ Container container = event.getContainer();
+ Object child = event.getData();
+ String type = event.getType();
+
+ if (type.equals(Container.ADD_CHILD_EVENT)) {
+ if (container instanceof Host) {
+ // Deploying a webapp
+ ((Lifecycle) child).addLifecycleListener(this);
+ eventHandler.addContext((Context) child);
+ } else if (container instanceof Engine) {
+ // Deploying a host
+ container.addContainerListener(this);
+ }
+ } else if (type.equals(Container.REMOVE_CHILD_EVENT)) {
+ if (container instanceof Host) {
+ // Undeploying a webapp
+ ((Lifecycle) child).removeLifecycleListener(this);
+ eventHandler.removeContext((Context) child);
+ } else if (container instanceof Engine) {
+ // Undeploying a host
+ container.removeContainerListener(this);
+ }
+ }
+
+ }
+
+
+ /**
+ * Primary entry point for startup and shutdown events.
+ *
+ * @param event The event that has occurred
+ */
+ public void lifecycleEvent(LifecycleEvent event) {
+
+ Object source = event.getLifecycle();
+
+ if (Lifecycle.START_EVENT.equals(event.getType())) {
+ if (source instanceof Context) {
+ // Start a webapp
+ eventHandler.startContext((Context) source);
+ } else {
+ return;
+ }
+ } else if (Lifecycle.AFTER_START_EVENT.equals(event.getType())) {
+ if (source instanceof Server) {
+
+ eventHandler.init();
+
+ startServer((Server) source);
+
+ init = true;
+ } else {
+ return;
+ }
+ } else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
+ if (source instanceof Context) {
+ // Stop a webapp
+ eventHandler.stopContext((Context) source);
+ } else if (source instanceof Server) {
+
+ stopServer((Server) source);
+
+ eventHandler.shutdown();
+
+ init = false;
+ } else {
+ return;
+ }
+ } else if (Lifecycle.PERIODIC_EVENT.equals(event.getType())) {
+ if (init && source instanceof Engine) {
+ eventHandler.status((Engine) source);
+ }
+ }
+
+ }
+
+
+ /**
+ * Send commands to the front end server associated with the startup of the
+ * node.
+ */
+ protected void startServer(Server server) {
+
+ // Register ourself as a listener for child services
+ Service[] services = server.findServices();
+ for (int i = 0; i < services.length; i++) {
+ services[i].getContainer().addContainerListener(this);
+
+ Engine engine = (Engine) services[i].getContainer();
+ ((Lifecycle) engine).addLifecycleListener(this);
+
+ Container[] children = engine.findChildren();
+ for (int j = 0; j < children.length; j++) {
+ children[j].addContainerListener(this);
+ Container[] children2 = children[j].findChildren();
+ for (int k = 0; k < children2.length; k++) {
+ ((Lifecycle) children2[k]).addLifecycleListener(this);
+ }
+ }
+ }
+
+ eventHandler.startServer(server);
+ }
+
+
+ /**
+ * Send commands to the front end server associated with the shutdown of the
+ * node.
+ */
+ protected void stopServer(Server server) {
+
+ // Register ourself as a listener to child components
+ Service[] services = server.findServices();
+ for (int i = 0; i < services.length; i++) {
+ services[i].getContainer().removeContainerListener(this);
+ ((Lifecycle) services[i].getContainer()).removeLifecycleListener(this);
+ Container[] children = services[i].getContainer().findChildren();
+ for (int j = 0; j < children.length; j++) {
+ children[j].removeContainerListener(this);
+ Container[] children2 = children[j].findChildren();
+ for (int k = 0; k < children2.length; k++) {
+ ((Lifecycle) children2[k]).removeLifecycleListener(this);
+ }
+ }
+ }
+
+ eventHandler.stopServer(server);
+ }
+
+ protected void setEventHandler(JBossWebEventHandler handler)
+ {
+ this.eventHandler = handler;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Constants.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Constants.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Constants.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,31 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, 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.web.tomcat.service.modcluster;
+
+
+public class Constants {
+
+ public static final String Package = "org.jboss.web.tomcat.service.cluster";
+
+}
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Constants.java
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/DefaultJBossWebEventHandler.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/DefaultJBossWebEventHandler.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/DefaultJBossWebEventHandler.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,268 @@
+/*
+ * 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.web.tomcat.service.modcluster;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.util.StringManager;
+import org.jboss.logging.Logger;
+import org.jboss.web.tomcat.service.modcluster.config.BalancerConfiguration;
+import org.jboss.web.tomcat.service.modcluster.config.NodeConfiguration;
+import org.jboss.web.tomcat.service.modcluster.load.LoadBalanceFactorProvider;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPHandler;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPRequest;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPUtils;
+
+/**
+ * Default implementation of {@link JBossWebEventHandler}.
+ *
+ * @author Brian Stansberry
+ */
+public class DefaultJBossWebEventHandler implements JBossWebEventHandler
+{
+ protected static Logger log = Logger.getLogger(DefaultJBossWebEventHandler.class);
+
+ // ----------------------------------------------------------------- Fields
+
+ /**
+ * The string manager for this package.
+ */
+ private StringManager sm = StringManager.getManager(Constants.Package);
+
+ private final NodeConfiguration nodeConfiguration;
+ private final BalancerConfiguration balancerConfiguration;
+ private final MCMPHandler mcmpHandler;
+ private final LoadBalanceFactorProvider loadBalanceFactorProvider;
+
+ private boolean init;
+
+ // ----------------------------------------------------------- Constructors
+
+ public DefaultJBossWebEventHandler(NodeConfiguration nodeConfiguration,
+ BalancerConfiguration balancerConfiguration,
+ MCMPHandler mcmpHandler,
+ LoadBalanceFactorProvider loadBalanceFactorProvider)
+ {
+ this.nodeConfiguration = nodeConfiguration;
+ this.balancerConfiguration = balancerConfiguration;
+ this.mcmpHandler = mcmpHandler;
+ this.loadBalanceFactorProvider = loadBalanceFactorProvider;
+ }
+
+ // ---------------------------------------------------- JBossWebEventHandler
+
+ public synchronized void init()
+ {
+ mcmpHandler.init();
+ init = true;
+ }
+
+ public synchronized void shutdown()
+ {
+ mcmpHandler.shutdown();
+ init = false;
+ }
+
+ /**
+ * Send commands to the front end server associated with the startup of the
+ * node.
+ */
+ public void startServer(Server server) {
+
+ Service[] services = server.findServices();
+ for (int i = 0; i < services.length; i++) {
+
+ Engine engine = (Engine) services[i].getContainer();
+
+ config(engine);
+ Container[] children = engine.findChildren();
+ for (int j = 0; j < children.length; j++) {
+ Container[] children2 = children[j].findChildren();
+ for (int k = 0; k < children2.length; k++) {
+ addContext((Context) children2[k]);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Send commands to the front end server associated with the shutdown of the
+ * node.
+ */
+ public void stopServer(Server server) {
+
+ Service[] services = server.findServices();
+ for (int i = 0; i < services.length; i++) {
+ removeAll((Engine) services[i].getContainer());
+ Container[] children = services[i].getContainer().findChildren();
+ for (int j = 0; j < children.length; j++) {
+ Container[] children2 = children[j].findChildren();
+ for (int k = 0; k < children2.length; k++) {
+ removeContext((Context) children2[k]);
+ }
+ }
+ }
+ }
+
+ public void config(Engine engine)
+ {
+ checkInit();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("clusterListener.config", engine.getName()));
+ }
+
+ // If needed, create automagical JVM route (address + port + engineName)
+ try {
+ Utils.establishJvmRouteAndConnectorAddress(engine, this.mcmpHandler);
+ } catch (Exception e) {
+ mcmpHandler.markProxiesInError();
+ log.info(sm.getString("clusterListener.error.addressJvmRoute"), e);
+ return;
+ }
+
+ MCMPRequest request = MCMPUtils.createConfigRequest(engine, getNodeConfiguration(), getBalancerConfiguration());
+
+ // Send CONFIG request
+ mcmpHandler.sendRequest(request);
+
+ }
+
+ public void addContext(Context context)
+ {
+ checkInit();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("clusterListener.context.enable", context.getPath(), context.getParent().getName(), ((StandardContext) context).getState()));
+ }
+
+ // Send ENABLE-APP if state is started
+ if (context.isStarted()) {
+ MCMPRequest request = MCMPUtils.createEnableAppRequest(context);
+ mcmpHandler.sendRequest(request);
+ }
+ }
+
+ public void startContext(Context context)
+ {
+ checkInit();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("clusterListener.context.start", context.getPath(), context.getParent().getName()));
+ }
+
+ // Send ENABLE-APP
+ MCMPRequest request = MCMPUtils.createEnableAppRequest(context);
+ mcmpHandler.sendRequest(request);
+ }
+
+ public void stopContext(Context context)
+ {
+ checkInit();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("clusterListener.context.stop", context.getPath(), context.getParent().getName()));
+ }
+
+ // Send STOP-APP
+ MCMPRequest request = MCMPUtils.createStopAppRequest(context);
+ mcmpHandler.sendRequest(request);
+ }
+
+ public void removeContext(Context context)
+ {
+ checkInit();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("clusterListener.context.disable", context.getPath(), context.getParent().getName(), ((StandardContext) context).getState()));
+ }
+
+ // JVMRoute can be null here if nothing was ever initialized
+ if (Utils.getJvmRoute(context) != null) {
+ MCMPRequest request = MCMPUtils.createRemoveAppRequest(context);
+ mcmpHandler.sendRequest(request);
+ }
+ }
+
+ public void removeAll(Engine engine)
+ {
+ checkInit();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("clusterListener.stop", engine.getName()));
+ }
+
+ // JVMRoute can be null here if nothing was ever initialized
+ if (engine.getJvmRoute() != null) {
+ // Send REMOVE-APP * request
+ MCMPRequest request = MCMPUtils.createRemoveAllRequest(engine);
+ mcmpHandler.sendRequest(request);
+ }
+ }
+
+ public void status(Engine engine)
+ {
+ checkInit();
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("clusterListener.status", engine.getName()));
+ }
+
+ mcmpHandler.status();
+
+ // Send STATUS request
+ int lbf = getLoadBalanceFactorProvider().getLoadBalanceFactor();
+ MCMPRequest request = MCMPUtils.createStatusRequest(engine, lbf);
+ mcmpHandler.sendRequest(request);
+ }
+
+ // ----------------------------------------------------------------- Public
+
+ public BalancerConfiguration getBalancerConfiguration()
+ {
+ return this.balancerConfiguration;
+ }
+
+ public NodeConfiguration getNodeConfiguration()
+ {
+ return this.nodeConfiguration;
+ }
+
+ public LoadBalanceFactorProvider getLoadBalanceFactorProvider()
+ {
+ return this.loadBalanceFactorProvider;
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ private void checkInit()
+ {
+ if (!init)
+ throw new IllegalStateException("Not initialized; call init() first");
+ }
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/JBossWebEventHandler.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/JBossWebEventHandler.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/JBossWebEventHandler.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,55 @@
+/*
+ * 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.web.tomcat.service.modcluster;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Server;
+
+/**
+ * Handles events notifications coming from JBoss Web via a cluster listener.
+ *
+ * @author Brian Stansberry
+ */
+public interface JBossWebEventHandler
+{
+ void init();
+ void shutdown();
+
+ void addContext(Context context);
+
+ void startContext(Context context);
+
+ void stopContext(Context context);
+
+ void removeContext(Context context);
+
+ void config(Engine engine);
+
+ void removeAll(Engine engine);
+
+ void status(Engine engine);
+
+ void startServer(Server server);
+
+ void stopServer(Server server);
+}
\ No newline at end of file
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/LocalStrings.properties
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/LocalStrings.properties (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/LocalStrings.properties 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,24 @@
+# Regular messages
+clusterListener.address=Detected local address [{0}]
+clusterListener.config=Sending configuration for engine [{0}]
+clusterListener.context.disable=Undeploy context [{0}] with state [{2}] to Host [{1}]
+clusterListener.context.enable=Deploy context [{0}] with state [{2}] to Host [{1}]
+clusterListener.context.start=Start context [{0}] in Host [{1}]
+clusterListener.context.stop=Stop context [{0}] in Host [{1}]
+clusterListener.jvmRoute=Engine [{0}] will use jvmRoute [{1}]
+clusterListener.request=Sending command [{0}] wildcard [{1}] to proxy [{2}]
+clusterListener.status=Check status for engine [{0}]
+clusterListener.stop=Stop all web applications for engine [{0}]
+
+# Error messages
+clusterListener.error.addressJvmRoute=Error connecting to proxy to determine Engine.JVMRoute or Connector.address
+clusterListener.error.invalidHost=Invalid host specified: {0}
+clusterListener.error.io=IO error sending command {0} to proxy {1}
+clusterListener.error.jmxRegister=Error during JMX registration
+clusterListener.error.jmxUnregister=Error during JMX unregistration
+clusterListener.error.nullAttribute=Value for attribute {0} cannot be null
+clusterListener.error.other=Error [{2}: {3}] sending command {0} to proxy {1}, configuration will be reset
+clusterListener.error.parse=Error parsing response header for command {0}
+clusterListener.error.startListener=Error starting advertise listener
+clusterListener.error.stopListener=Error stopping advertise listener
+clusterListener.error.syntax=Unrecoverable syntax error [{2}: {3}] sending command {0} to proxy {1}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ModClusterService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ModClusterService.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ModClusterService.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,799 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, 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.web.tomcat.service.modcluster;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Server;
+import org.apache.catalina.util.StringManager;
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.framework.interfaces.DistributedReplicantManager;
+import org.jboss.ha.framework.interfaces.HAPartition;
+import org.jboss.ha.singleton.HASingletonElectionPolicySimple;
+import org.jboss.ha.singleton.HASingletonElector;
+import org.jboss.ha.singleton.HASingletonSupport;
+import org.jboss.web.tomcat.service.modcluster.config.BalancerConfiguration;
+import org.jboss.web.tomcat.service.modcluster.config.ModClusterConfig;
+import org.jboss.web.tomcat.service.modcluster.config.NodeConfiguration;
+import org.jboss.web.tomcat.service.modcluster.ha.ModClusterServiceDRMEntry;
+import org.jboss.web.tomcat.service.modcluster.ha.ModClusterServiceHASingletonElectionPolicy;
+import org.jboss.web.tomcat.service.modcluster.ha.ClusteredMCMPHandler;
+import org.jboss.web.tomcat.service.modcluster.ha.ClusteredMCMPHandlerImpl;
+import org.jboss.web.tomcat.service.modcluster.ha.HASingletonAwareResetRequestSource;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.BooleanGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.ModClusterServiceStateGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.GroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.InetAddressGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.MCMPServerDiscoveryEvent;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.PeerMCMPDiscoveryStatus;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.StringGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.ThrowableGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.load.LoadBalanceFactorProvider;
+import org.jboss.web.tomcat.service.modcluster.mcmp.AddressPort;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPHandler;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPRequest;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServer;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServerState;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPUtils;
+import org.jboss.web.tomcat.service.modcluster.mcmp.impl.DefaultMCMPHandler;
+
+/**
+ * A ModClusterService.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class ModClusterService extends HASingletonSupport
+ implements JBossWebEventHandler
+{
+ // ----------------------------------------------------------------- Fields
+
+ /**
+ * The string manager for this package.
+ */
+ private StringManager sm = StringManager.getManager(Constants.Package);
+
+ private final HASingletonAwareResetRequestSource resetRequestSource;
+ private final MCMPHandler localHandler;
+ private final ClusteredMCMPHandler clusteredHandler;
+ private final LoadBalanceFactorProvider loadManager;
+ private final RpcHandler rpcHandler = new RpcHandler();
+ private final JBossWebEventHandler eventHandlerDelegate;
+ private final ModClusterServiceHASingletonElectionPolicy electionPolicy;
+ private final String domain;
+ private final Map<ClusterNode, MCMPServerDiscoveryEvent> proxyChangeDigest =
+ new HashMap<ClusterNode, MCMPServerDiscoveryEvent>();
+
+
+ private volatile int statusCount = 0;
+ private volatile int processStatusFrequency = 1;
+ private volatile int latestLoad;
+ private ModClusterServiceDRMEntry drmEntry;
+
+
+ /**
+ * Create a new ClusterCoordinator.
+ *
+ * @param partition the partition of which we are a member
+ * @param config our configuration
+ * @param loadFactorProvider source for local load balance statistics
+ * @param singletonElector chooses the singleton master
+ */
+ public ModClusterService(HAPartition partition,
+ ModClusterConfig config,
+ LoadBalanceFactorProvider loadFactorProvider,
+ HASingletonElector singletonElector)
+ {
+ assert partition != null : "partition is null";
+ assert loadFactorProvider != null : "loadFactorProvider is null";
+ assert config != null : "config is null";
+
+ setHAPartition(partition);
+
+ this.resetRequestSource = new HASingletonAwareResetRequestSource(config, config, partition, ClusteredMCMPHandler.HA_SERVICE_NAME);
+ this.localHandler = new DefaultMCMPHandler(config, this.resetRequestSource, false);
+ this.clusteredHandler = new ClusteredMCMPHandlerImpl(this.localHandler, partition, ClusteredMCMPHandler.HA_SERVICE_NAME);
+ this.loadManager = loadFactorProvider;
+ this.eventHandlerDelegate = new DefaultJBossWebEventHandler(config, config, clusteredHandler, loadFactorProvider);
+ this.domain = config.getDomain();
+ if (singletonElector == null)
+ singletonElector = new HASingletonElectionPolicySimple();
+ this.electionPolicy = new ModClusterServiceHASingletonElectionPolicy(singletonElector);
+ setElectionPolicy(this.electionPolicy);
+
+ this.drmEntry = new ModClusterServiceDRMEntry(partition.getClusterNode(), null);
+ }
+
+ /**
+ * Create a new ClusterCoordinator using the given component parts.
+ * Only intended for use by test suites that may wish to inject
+ * mock components.
+ *
+ * @param partition
+ * @param nodeConfig
+ * @param balancerConfig
+ * @param localHandler
+ * @param resetRequestSource
+ * @param clusteredHandler
+ * @param loadManager
+ * @param singletonElector
+ */
+ protected ModClusterService(HAPartition partition,
+ NodeConfiguration nodeConfig,
+ BalancerConfiguration balancerConfig,
+ MCMPHandler localHandler,
+ HASingletonAwareResetRequestSource resetRequestSource,
+ ClusteredMCMPHandler clusteredHandler,
+ LoadBalanceFactorProvider loadManager,
+ HASingletonElector singletonElector)
+ {
+ assert partition != null : "partition is null";
+ assert localHandler != null : "mcmpHandler is null";
+ assert loadManager != null : "loadManager is null";
+ assert resetRequestSource != null : "resetRequestSource is null";
+ assert nodeConfig != null : "nodeConfig is null";
+ assert balancerConfig != null : "balancerConfig is null";
+ assert clusteredHandler != null : "clusteredHandler is null";
+
+ setHAPartition(partition);
+
+ this.localHandler = localHandler;
+ this.resetRequestSource = resetRequestSource;
+ this.clusteredHandler = clusteredHandler;
+ this.loadManager = loadManager;
+ this.eventHandlerDelegate = new DefaultJBossWebEventHandler(nodeConfig, balancerConfig, clusteredHandler, loadManager);
+ this.domain = nodeConfig.getDomain();
+
+ if (singletonElector == null)
+ singletonElector = new HASingletonElectionPolicySimple();
+ this.electionPolicy = new ModClusterServiceHASingletonElectionPolicy(singletonElector);
+ setElectionPolicy(this.electionPolicy);
+
+ this.drmEntry = new ModClusterServiceDRMEntry(partition.getClusterNode(), null);
+ }
+
+ // ---------------------------------------------------- JBossWebEventHandler
+
+ public void init()
+ {
+ // Use the standard logic
+ this.eventHandlerDelegate.init();
+ }
+
+ public void shutdown()
+ {
+ // Use the standard logic
+ this.eventHandlerDelegate.shutdown();
+ }
+
+
+ public void startServer(Server server)
+ {
+ // Pass on ref to our server
+ this.resetRequestSource.setJbossWebServer(server);
+
+ // Use the standard logic
+ this.eventHandlerDelegate.startServer(server);
+ }
+
+ public void stopServer(Server server)
+ {
+ // Use the standard logic
+ this.eventHandlerDelegate.stopServer(server);
+ }
+
+ public void config(Engine engine)
+ {
+ // If needed, create automagical JVM route (address + port + engineName)
+ try {
+ Utils.establishJvmRouteAndConnectorAddress(engine, this.clusteredHandler);
+ } catch (Exception e) {
+ this.clusteredHandler.markProxiesInError();
+ log.info(sm.getString("clusterListener.error.addressJvmRoute"), e);
+ return;
+ }
+
+ this.drmEntry.addJvmRoute(engine.getJvmRoute());
+ updateLocalDRM(drmEntry);
+
+ // Use the standard logic
+ this.eventHandlerDelegate.config(engine);
+ }
+
+ public void removeAll(Engine engine)
+ {
+ // Use the standard logic
+ this.eventHandlerDelegate.removeAll(engine);
+
+ this.drmEntry.removeJvmRoute(engine.getJvmRoute());
+ updateLocalDRM(drmEntry);
+ }
+
+ public void addContext(Context context)
+ {
+ // Use the standard logic
+ this.eventHandlerDelegate.addContext(context);
+ }
+
+ public void startContext(Context context)
+ {
+ // Use the standard logic
+ this.eventHandlerDelegate.startContext(context);
+ }
+
+ public void stopContext(Context context)
+ {
+ // Use the standard logic
+ this.eventHandlerDelegate.stopContext(context);
+ }
+
+ public void removeContext(Context context)
+ {
+ // Use the standard logic
+ this.eventHandlerDelegate.removeContext(context);
+ }
+
+ public void status(Engine engine)
+ {
+ this.latestLoad = loadManager.getLoadBalanceFactor();
+
+ if (this.isMasterNode())
+ {
+ statusCount = (statusCount + 1) % processStatusFrequency;
+ if (statusCount == 0)
+ {
+ // 1) Poll everybody for status
+ // 2) Deal with any dropped mcmp request situations
+ // 3) Update the LBFs on mod_cluster
+ // 4) Get the mod_cluster proxy lists in sync
+ updateClusterStatus();
+ }
+ }
+ }
+
+
+ // ------------------------------------------------------------- Properties
+
+ public String getDomain()
+ {
+ return this.domain;
+ }
+
+ public int getProcessStatusFrequency()
+ {
+ return this.processStatusFrequency;
+ }
+
+ public void setProcessStatusFrequency(int processStatusFrequency)
+ {
+ this.processStatusFrequency = processStatusFrequency;
+ }
+
+
+ // ------------------------------------------------------- Public Overrides
+
+ @Override
+ public void _stopOldMaster()
+ {
+ this.stopOldMaster(this.getDomain());
+ }
+
+ @Override
+ public void startSingleton()
+ {
+ super.startSingleton();
+
+ this.clusteredHandler.setMasterNode(true);
+ this.resetRequestSource.setMasterNode(true);
+
+ // Ensure we do a full status on the next event
+ this.statusCount = processStatusFrequency - 1;
+ }
+
+ @Override
+ public void stopSingleton()
+ {
+ super.stopSingleton();
+
+ this.clusteredHandler.setMasterNode(false);
+ this.resetRequestSource.setMasterNode(false);
+ }
+
+ // -------------------------------------------------------------- Protected
+
+ /**
+ * {@inheritDoc}
+ *
+ * We override the superclass to pass our domain as a param to the
+ * "_stopOldMaster" call.
+ */
+ @Override
+ protected void makeThisNodeMaster()
+ {
+ try
+ {
+ // stop the old master (if there is one) before starting the new one
+
+ // ovidiu 09/02/04 - temporary solution for Case 1843, use an asynchronous
+ // distributed call.
+ this.callAsyncMethodOnPartition("stopOldMaster", new Object[]{ getDomain() }, new Class[]{ String.class });
+
+ this.startNewMaster();
+ }
+ catch (Exception ex)
+ {
+ this.log.error("_stopOldMaster failed. New master singleton will not start.", ex);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return an inner class that allows us to avoid exposing RPC methods as
+ * public methods of this class
+ */
+ @Override
+ protected Object getRPCHandler()
+ {
+ return rpcHandler;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return a {@link ModClusterServiceDRMEntry}
+ */
+ @Override
+ protected Serializable getReplicant()
+ {
+ return drmEntry;
+ }
+
+ // ----------------------------------------------------------------- Private
+
+ private void stopOldMaster(String domain)
+ {
+ if (safeEquals(domain, this.getDomain()))
+ {
+ super._stopOldMaster();
+ }
+ else
+ {
+ log.debug("ignoring call to _stopOldMaster for domain " + domain +
+ " as we are in domain " + this.getDomain());
+ }
+ }
+
+ private boolean safeEquals(Object a, Object b)
+ {
+ return (a == b || (a != null && a.equals(b)));
+ }
+
+ private void updateClusterStatus()
+ {
+ this.localHandler.status();
+ Set<MCMPServerState> masterList = null;
+ Map<ClusterNode, MCMPServerDiscoveryEvent> latestEvents = null;
+ synchronized (proxyChangeDigest)
+ {
+ masterList = this.localHandler.getProxyStates();
+ latestEvents = new HashMap<ClusterNode, MCMPServerDiscoveryEvent>(proxyChangeDigest);
+ }
+ HAPartition partition = getHAPartition();
+ List<ModClusterServiceDRMEntry> replicants = partition.getDistributedReplicantManager().lookupReplicants(getServiceHAName());
+ Map<ClusterNode, ModClusterServiceDRMEntry> nonresponsive = new HashMap<ClusterNode, ModClusterServiceDRMEntry>();
+ for (ModClusterServiceDRMEntry replicant : replicants)
+ {
+ nonresponsive.put(replicant.getPeer(), replicant);
+ }
+ nonresponsive.remove(partition.getClusterNode());
+
+ // FIXME -- what about our own dropped discovery events if we just became master?
+ List responses = null;
+ try
+ {
+ responses = partition.callMethodOnCluster(getServiceHAName(), "getClusterCoordinatorState", new Object[]{ masterList }, new Class[]{ Set.class }, true);
+ }
+ catch (Exception e)
+ {
+ throw Utils.convertToUnchecked(e);
+ }
+
+ // Gather up all the reset requests in one list
+ // FIXME -- what about our own dropped requests if we just became master?
+ List<MCMPRequest> resetRequests = new ArrayList<MCMPRequest>();
+
+ // Gather all the load balance factors
+ Map<String, Integer> loadBalanceFactors = new HashMap<String, Integer>();
+
+ // Gather the info on who knows about what proxies
+ Map<ClusterNode, PeerMCMPDiscoveryStatus> statuses = new HashMap<ClusterNode, PeerMCMPDiscoveryStatus>();
+
+ boolean resync = false;
+ for (Object response : responses)
+ {
+ if (response instanceof ModClusterServiceStateGroupRpcResponse)
+ {
+ ModClusterServiceStateGroupRpcResponse mcssgrr = (ModClusterServiceStateGroupRpcResponse) response;
+ ClusterNode cn = mcssgrr.getSender();
+
+ // Check for discovery events we haven't processed
+ MCMPServerDiscoveryEvent latestEvent = latestEvents.get(cn);
+ for (MCMPServerDiscoveryEvent toCheck : mcssgrr.getUnacknowledgedEvents())
+ {
+ if (latestEvent != null && latestEvent.getEventIndex() <= toCheck.getEventIndex())
+ continue; // already processed it
+
+ AddressPort ap = toCheck.getMCMPServer();
+ if (toCheck.isAddition())
+ {
+ this.localHandler.addProxy(ap.address, ap.port);
+ }
+ else
+ {
+ this.localHandler.removeProxy(ap.address, ap.port);
+ }
+ resync = true;
+ }
+
+ if (!resync) // don't bother if we are going to start over
+ {
+ statuses.put(cn, new PeerMCMPDiscoveryStatus(cn, mcssgrr.getStates(), latestEvent));
+
+ resetRequests.addAll(mcssgrr.getResetRequests());
+
+ ModClusterServiceDRMEntry removed = nonresponsive.remove(cn);
+ if (removed != null)
+ {
+ Integer lbf = new Integer(mcssgrr.getLoadBalanceFactor());
+ for (String jvmRoute : removed.getJvmRoutes())
+ {
+ loadBalanceFactors.put(jvmRoute, lbf);
+ }
+ }
+ }
+ }
+ else if (response instanceof ThrowableGroupRpcResponse)
+ {
+ ThrowableGroupRpcResponse tgrr = (ThrowableGroupRpcResponse) response;
+ ClusterNode cn = tgrr.getSender();
+
+ log.warn("Call to getClusterCoordinatorState received throwable from " + cn, tgrr.getValue());
+
+ // Don't remove from nonresponsive list and we'll pass back an error
+ // status (null server list) to this peer
+ }
+ else if (response instanceof Throwable)
+ {
+ log.warn("Call to getClusterCoordinatorState received throwable from unknown server", (Throwable) response);
+ }
+ else
+ {
+ log.error("Call to getClusterCoordinatorState received unexpected response : " + response);
+ }
+ }
+
+ if (resync)
+ {
+ // We picked up previously unknown discovery events; start over
+ updateClusterStatus();
+ return;
+ }
+
+ // Add error-state objects for non-responsive peers
+ Integer lbf = new Integer(0);
+ for (Map.Entry<ClusterNode, ModClusterServiceDRMEntry> entry : nonresponsive.entrySet())
+ {
+ ClusterNode cn = entry.getKey();
+ statuses.put(entry.getKey(), new PeerMCMPDiscoveryStatus(cn, null, latestEvents.get(cn)));
+
+ for (String jvmRoute : entry.getValue().getJvmRoutes())
+ {
+ loadBalanceFactors.put(jvmRoute, lbf);
+ }
+ }
+
+ // FIXME handle crashed members, gone from DRM
+
+ // Advise the proxies of any reset requests
+ for (MCMPRequest resetRequest : resetRequests)
+ {
+ this.localHandler.sendRequest(resetRequest);
+ }
+
+ // Pass along the LBF values
+ for (Map.Entry<String, Integer> entry : loadBalanceFactors.entrySet())
+ {
+ MCMPRequest req = MCMPUtils.createStatusRequest(entry.getKey(), entry.getValue().intValue());
+ this.localHandler.sendRequest(req);
+ }
+
+ // Advise the members the process is done and that they should update DRM
+ notifyClusterStatusComplete(masterList, statuses);
+ }
+
+ private void notifyClusterStatusComplete(Set<MCMPServerState> masterList,
+ Map<ClusterNode, PeerMCMPDiscoveryStatus> statuses)
+ {
+ HAPartition partition = getHAPartition();
+
+ // Determine who should update DRM first -- us or the rest of the nodes
+ Set<ModClusterServiceDRMEntry> allStatuses = new HashSet<ModClusterServiceDRMEntry>(statuses.values());
+ ModClusterServiceDRMEntry ourCurrentStatus = (ModClusterServiceDRMEntry) partition.getDistributedReplicantManager().lookupLocalReplicant(getServiceHAName());
+ allStatuses.add(ourCurrentStatus);
+
+ boolean othersFirst = this.electionPolicy.narrowCandidateList(allStatuses).contains(partition.getClusterNode());
+ ModClusterServiceDRMEntry ourNewStatus = new ModClusterServiceDRMEntry(partition.getClusterNode(), masterList);
+ boolean updated = (ourNewStatus.equals(ourCurrentStatus) == false);
+
+ if (othersFirst)
+ {
+ updateRemoteDRMs(statuses);
+
+ if (updated)
+ {
+ updateLocalDRM(ourNewStatus);
+ }
+ }
+ else
+ {
+ if (updated)
+ {
+ updateLocalDRM(ourNewStatus);
+ }
+
+ updateRemoteDRMs(statuses);
+ }
+ }
+
+ private void updateRemoteDRMs(Map<ClusterNode, PeerMCMPDiscoveryStatus> statuses)
+ {
+ try
+ {
+ getHAPartition().callMethodOnCluster(getServiceHAName(), "updateDRM", new Object[]{ statuses }, new Class[]{ Map.class }, true);
+ }
+ catch (Exception e)
+ {
+ log.error("Caught exception advising cluster to update DRM", e);
+ }
+ }
+
+ private void updateLocalDRM(ModClusterServiceDRMEntry ourNewStatus) throws Error
+ {
+ try
+ {
+ getHAPartition().getDistributedReplicantManager().add(getServiceHAName(), ourNewStatus);
+ }
+ catch (Exception e)
+ {
+ throw Utils.convertToUnchecked(e);
+ }
+ }
+
+
+ // ---------------------------------------------------------- Inner classes
+
+ /**
+ * This is the object that gets invoked on via reflection by HAPartition.
+ */
+ private class RpcHandler
+ {
+ private final ModClusterService coord = ModClusterService.this;
+ private final GroupRpcResponse SUCCESS =
+ new GroupRpcResponse(this.coord.getHAPartition().getClusterNode());
+
+
+ public void stopOldMaster(String domain)
+ {
+ this.coord.stopOldMaster(domain);
+ }
+
+ public InetAddressGroupRpcResponse getLocalAddress() throws IOException
+ {
+ if (this.coord.isMasterNode())
+ {
+ return new InetAddressGroupRpcResponse(this.coord.getHAPartition().getClusterNode(),
+ this.coord.localHandler.getLocalAddress());
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public GroupRpcResponse mcmpServerDiscoveryEvent(MCMPServerDiscoveryEvent event)
+ {
+ if (this.coord.isMasterNode())
+ {
+ synchronized (proxyChangeDigest)
+ {
+ AddressPort ap = event.getMCMPServer();
+ if (event.isAddition())
+ {
+ this.coord.localHandler.addProxy(ap.address, ap.port);
+ }
+ else
+ {
+ this.coord.localHandler.removeProxy(ap.address, ap.port);
+ }
+ proxyChangeDigest.put(event.getSender(), event);
+ return new GroupRpcResponse(getHAPartition().getClusterNode());
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public ModClusterServiceStateGroupRpcResponse getClusterCoordinatorState(Set<MCMPServer> masterList)
+ {
+ if (this.coord.isMasterNode() == false)
+ {
+ Set<MCMPServerState> ourStates = this.coord.clusteredHandler.updateServersFromMasterNode(masterList);
+
+ List<MCMPRequest> resetRequests = null;
+ boolean needReset = this.coord.clusteredHandler.getNeedsResetTransmission();
+ if (needReset)
+ {
+ resetRequests = this.coord.resetRequestSource.getLocalResetRequests();
+ }
+
+ ModClusterServiceStateGroupRpcResponse response =
+ new ModClusterServiceStateGroupRpcResponse(getHAPartition().getClusterNode(),
+ this.coord.latestLoad,
+ ourStates,
+ this.coord.clusteredHandler.getPendingDiscoveryEvents(),
+ resetRequests);
+
+ if (needReset)
+ {
+ this.coord.clusteredHandler.recordResetTransmission();
+ }
+
+ return response;
+ }
+ else
+ {
+ // TODO is this the correct response here?
+ return null;
+ }
+
+ }
+
+ public void updateDRM(Map<ClusterNode, PeerMCMPDiscoveryStatus> statuses)
+ {
+ HAPartition partition = this.coord.getHAPartition();
+ ClusterNode cn = partition.getClusterNode();
+ PeerMCMPDiscoveryStatus newStatus = statuses.get(cn);
+ if (newStatus != null)
+ {
+ // Notify our handler that discovery events have been processed
+ this.coord.clusteredHandler.discoveryEventsReceived(newStatus.getLatestDiscoveryEvent());
+
+ // Notify our handler that any reset requests have been processed
+ this.coord.clusteredHandler.recordResetSuccess();
+
+ DistributedReplicantManager drm = partition.getDistributedReplicantManager();
+ String haName = this.coord.getServiceHAName();
+ ModClusterServiceDRMEntry oldStatus =
+ (ModClusterServiceDRMEntry) drm.lookupLocalReplicant(haName);
+ if (newStatus.equals(oldStatus) == false)
+ {
+ try
+ {
+ drm.add(haName, new ModClusterServiceDRMEntry(cn, newStatus.getMCMPServerStates()));
+ }
+ catch (Exception e)
+ {
+ this.coord.log.error("Error updating DRM", e);
+ }
+ }
+ }
+ }
+
+ public StringGroupRpcResponse getProxyConfiguration()
+ {
+ if (this.coord.isMasterNode())
+ {
+ return new StringGroupRpcResponse(getHAPartition().getClusterNode(), this.coord.localHandler.getProxyConfiguration());
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public BooleanGroupRpcResponse isProxyHealthOK()
+ {
+ if (this.coord.isMasterNode())
+ {
+ return new BooleanGroupRpcResponse(getHAPartition().getClusterNode(), this.coord.localHandler.isProxyHealthOK());
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public GroupRpcResponse markProxiesInError()
+ {
+ if (this.coord.isMasterNode())
+ {
+ throw new UnsupportedOperationException("TODO implement me");
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public GroupRpcResponse refresh()
+ {
+ if (this.coord.isMasterNode())
+ {
+ this.coord.localHandler.refresh();
+ return SUCCESS;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public GroupRpcResponse reset()
+ {
+ if (this.coord.isMasterNode())
+ {
+ this.coord.localHandler.reset();
+ return SUCCESS;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public GroupRpcResponse sendRequest(MCMPRequest request)
+ {
+ if (this.coord.isMasterNode())
+ {
+ this.coord.localHandler.sendRequest(request);
+ return SUCCESS;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Utils.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Utils.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/Utils.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,152 @@
+package org.jboss.web.tomcat.service.modcluster;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.jboss.logging.Logger;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPHandler;
+
+public class Utils
+{
+ private static final Logger log = Logger.getLogger(Utils.class);
+ private static final StringManager sm = StringManager.getManager(Constants.Package);
+
+ /**
+ * Find the most likely connector the proxy server should connect to, or
+ * accept connections from.
+ *
+ * @param connectors
+ * @return
+ */
+ public static Connector findProxyConnector(Connector[] connectors)
+ {
+ int pos = 0;
+ int maxThreads = 0;
+ for (int i = 0; i < connectors.length; i++) {
+ if (connectors[i].getProtocol().startsWith("AJP")) {
+ // Return any AJP connector found
+ return connectors[i];
+ }
+ if (Boolean.TRUE.equals(IntrospectionUtils.getProperty(connectors[i].getProtocolHandler(), "reverseConnection"))) {
+ return connectors[i];
+ }
+ Integer mt = (Integer) IntrospectionUtils.getProperty(connectors[i].getProtocolHandler(), "maxThreads");
+ if (mt.intValue() > maxThreads) {
+ maxThreads = mt.intValue();
+ pos = i;
+ }
+ }
+ // If no AJP connector and no reverse, return the connector with the most threads
+ return connectors[pos];
+ }
+
+ /**
+ * Return the address on which the connector is bound.
+ *
+ * @param connector
+ * @return
+ */
+ public static String getAddress(Connector connector)
+ {
+ InetAddress inetAddress =
+ (InetAddress) IntrospectionUtils.getProperty(connector.getProtocolHandler(), "address");
+ if (inetAddress == null) {
+ // Should not happen
+ return "127.0.0.1";
+ } else {
+ return inetAddress.getHostAddress();
+ }
+ }
+
+ /**
+ * Return the JvmRoute for the specified context.
+ *
+ * @param context
+ * @return
+ */
+ public static String getJvmRoute(Context context)
+ {
+ return ((Engine) context.getParent().getParent()).getJvmRoute();
+ }
+
+ /**
+ * Return the host and its alias list with which the context is associated.
+ *
+ * @param context
+ * @return
+ */
+ public static String getHost(Context context)
+ {
+ StringBuffer result = new StringBuffer();
+ Host host = (Host) context.getParent();
+ result.append(host.getName());
+ String[] aliases = host.findAliases();
+ for (int i = 0; i < aliases.length; i++) {
+ result.append(',');
+ result.append(aliases[i]);
+ }
+ return result.toString();
+ }
+
+ public static void establishJvmRouteAndConnectorAddress(Engine engine, MCMPHandler mcmpHandler) throws IOException
+ {
+ Connector connector = findProxyConnector(engine.getService().findConnectors());
+ InetAddress localAddress =
+ (InetAddress) IntrospectionUtils.getProperty(connector.getProtocolHandler(), "address");
+ if ((engine.getJvmRoute() == null || localAddress == null) && mcmpHandler.getProxyStates().size() > 0) {
+ // Automagical JVM route (address + port + engineName)
+ if (localAddress == null) {
+ localAddress = mcmpHandler.getLocalAddress();
+ if (localAddress != null) {
+ IntrospectionUtils.setProperty(connector.getProtocolHandler(), "address", localAddress.getHostAddress());
+ } else {
+ // Should not happen
+ IntrospectionUtils.setProperty(connector.getProtocolHandler(), "address", "127.0.0.1");
+ }
+ log.info(sm.getString("clusterListener.address", localAddress.getHostAddress()));
+ }
+ if (engine.getJvmRoute() == null) {
+ String hostName = null;
+ if (localAddress != null) {
+ hostName = localAddress.getHostName();
+ } else {
+ // Fallback
+ hostName = "127.0.0.1";
+ }
+ String jvmRoute = hostName + ":" + connector.getPort() + ":" + engine.getName();
+ engine.setJvmRoute(jvmRoute);
+ log.info(sm.getString("clusterListener.jvmRoute", engine.getName(), jvmRoute));
+ }
+ }
+ }
+
+ /**
+ * Analyzes the type of the given Throwable, handing it back if it is a
+ * RuntimeException, wrapping it in a RuntimeException if it is a checked
+ * exception, or throwing it if it is an Error
+ *
+ * @param t the throwable
+ * @return a RuntimeException based on t
+ * @throws Error if t is an Error
+ */
+ public static RuntimeException convertToUnchecked(Throwable t) throws Error
+ {
+ if (t instanceof Error)
+ throw (Error) t;
+ else if (t instanceof RuntimeException)
+ return (RuntimeException) t;
+ else
+ return new RuntimeException(t.getMessage(), t);
+ }
+
+ private Utils()
+ {
+ }
+
+}
\ No newline at end of file
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertiseEventType.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertiseEventType.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertiseEventType.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright(c) 2008 Red Hat Middleware, LLC,
+ * and individual contributors as indicated by the @authors tag.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ */
+
+package org.jboss.web.tomcat.service.modcluster.advertise;
+
+/**
+ * Set what type of event the AdvertiseEvent signals.
+ * @param type The type of event. One of:
+ * <PRE>
+ * ON_NEW_SERVER -- New AdvertisedServer detected
+ * ON_STATUS_CHANGE -- AdvertisedServer server changed status
+ * </PRE>
+ */
+public enum AdvertiseEventType
+{
+ /** New AdvertisedServer detected */
+ ON_NEW_SERVER( 0),
+ /** AdvertisedServer server changed status */
+ ON_STATUS_CHANGE( 1);
+
+ private int value;
+ private AdvertiseEventType(int v)
+ {
+ value = v;
+ }
+
+ public int valueOf()
+ {
+ return value;
+ }
+
+ public static AdvertiseEventType valueOf(int value)
+ {
+ for (AdvertiseEventType e : values()) {
+ if (e.value == value)
+ return e;
+ }
+ throw new IllegalArgumentException("Invalid initializer: " + value);
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertiseListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertiseListener.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertiseListener.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,448 @@
+/*
+ *
+ * Copyright(c) 2008 Red Hat Middleware, LLC,
+ * and individual contributors as indicated by the @authors tag.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ */
+
+package org.jboss.web.tomcat.service.modcluster.advertise;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPHandler;
+
+
+/** AdvertiseListener
+ * Listens for Advertise messages from mod_cluster
+ *
+ * @author Mladen Turk
+ *
+ */
+public class AdvertiseListener
+{
+ /** Default port for listening Advertise messages.
+ */
+ public static int DEFAULT_PORT = 23364;
+ /** Default Multicast group address for listening Advertise messages.
+ */
+ public static String DEFAULT_GROUP = "224.0.1.105";
+
+ private static String RFC_822_FMT = "EEE, d MMM yyyy HH:mm:ss Z";
+ private int advertisePort = DEFAULT_PORT;
+ private String groupAddress = DEFAULT_GROUP;
+ private MulticastSocket ms;
+ private SimpleDateFormat df;
+ private boolean listening = true;
+ private boolean initialized = false;
+ private boolean running = false;
+ private boolean paused = false;
+ private boolean daemon = true;
+ private byte [] secure = new byte[16];
+ private String securityKey = null;
+ private MessageDigest md = null;
+
+ private HashMap<String, AdvertisedServer> servers;
+
+ private MCMPHandler commHandler;
+ private Thread workerThread;
+
+
+ private static void digestString(MessageDigest md, String s)
+ {
+ int len = s.length();
+ byte [] b = new byte[len];
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ if (c < 127)
+ b[i] = (byte)c;
+ else
+ b[i] = '?';
+ }
+ md.update(b);
+ }
+
+ /** Create AdvertiseListener instance
+ * @param eventHandler The event handler that will be used for
+ * status and new server notifications.
+ */
+ public AdvertiseListener(MCMPHandler commHandler)
+ {
+ df = new SimpleDateFormat(RFC_822_FMT, Locale.US);
+ servers = new HashMap<String, AdvertisedServer>();
+ this.commHandler = commHandler;
+ }
+
+ /**
+ * The default is true - the control thread will be
+ * in daemon mode. If set to false, the control thread
+ * will not be daemon - and will keep the process alive.
+ */
+ public void setDaemon(boolean b)
+ {
+ daemon = b;
+ }
+
+ public boolean getDaemon()
+ {
+ return daemon;
+ }
+
+ /** Set Advertise security key
+ * @param key The key to use.<br/>
+ * Security key must match the AdvertiseKey
+ * on the advertised server.
+ */
+ public void setSecurityKey(String key)
+ throws NoSuchAlgorithmException
+ {
+ securityKey = key;
+ if (md == null)
+ md = MessageDigest.getInstance("MD5");
+ }
+
+ /** Set Advertise port
+ * @param port The UDP port to use.
+ */
+ public void setAdvertisePort(int port)
+ {
+ advertisePort = port;
+ }
+
+ public int getAdvertisePort()
+ {
+ return advertisePort;
+ }
+
+ /** Set Advertise Multicaset group address
+ * @param address The IP or host address to use.
+ */
+ public void setGroupAddress(String address)
+ {
+ groupAddress = address;
+ }
+
+ /** Get Advertise Multicaset group address
+ */
+ public String getGroupAddress()
+ {
+ return groupAddress;
+ }
+
+ /** Get Collection of all AdvertisedServer instances.
+ */
+ public Collection<AdvertisedServer> getServers()
+ {
+ return servers.values();
+ }
+
+ /** Get AdvertiseServer server.
+ * @param name Server name to get.
+ */
+ public AdvertisedServer getServer(String name)
+ {
+ return servers.get(name);
+ }
+
+ /** Remove the AdvertisedServer from the collection.
+ * @param server Server to remove.
+ */
+ public void removeServer(AdvertisedServer server)
+ {
+ servers.remove(server);
+ }
+
+ private void init()
+ throws IOException
+ {
+ ms = new MulticastSocket(advertisePort);
+ ms.setTimeToLive(16);
+ ms.joinGroup(InetAddress.getByName(groupAddress));
+ initialized = true;
+ }
+
+ private void interruptDatagramReader()
+ {
+ if (!initialized)
+ return;
+ try {
+ // Restrict to localhost.
+ ms.setTimeToLive(0);
+ DatagramPacket dp = new DatagramPacket(secure, secure.length,
+ InetAddress.getByName(groupAddress),
+ advertisePort);
+ ms.send(dp);
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ /** Start the Listener, creating listener thread.
+ */
+ public void start()
+ throws IOException
+ {
+ if (!initialized) {
+ init();
+ }
+ if (!running) {
+ SecureRandom random = new SecureRandom();
+ random.nextBytes(secure);
+ secure[0] = 0;
+ running = true;
+ paused = false;
+ listening = true;
+ AdvertiseListenerWorker aw = new AdvertiseListenerWorker();
+ workerThread = new Thread(aw);
+ workerThread.setDaemon(daemon);
+ workerThread.start();
+ }
+ }
+
+ /**
+ * Pause the listener, which will make it stop accepting new advertise
+ * messages.
+ */
+ public void pause()
+ {
+ if (running && !paused) {
+ paused = true;
+ interruptDatagramReader();
+ }
+ }
+
+
+ /**
+ * Resume the listener, which will make it start accepting new advertise
+ * messages again.
+ */
+ public void resume()
+ {
+ if (running && paused) {
+ // Genererate new private secure
+ SecureRandom random = new SecureRandom();
+ random.nextBytes(secure);
+ secure[0] = 0;
+ paused = false;
+ }
+ }
+
+
+ /**
+ * Stop the endpoint. This will cause all processing threads to stop.
+ */
+ public void stop()
+ {
+ if (running) {
+ running = false;
+ interruptDatagramReader();
+ workerThread = null;
+ }
+ }
+
+
+ /**
+ * Deallocate listener and close sockets.
+ */
+ public void destroy()
+ throws IOException
+ {
+ if (running) {
+ stop();
+ }
+ if (initialized) {
+ ms.leaveGroup(InetAddress.getByName(groupAddress));
+ ms.close();
+ initialized = false;
+ ms = null;
+ }
+ }
+
+ private boolean verifyDigest(String digest, String server, String date)
+ {
+ if (md == null)
+ return true;
+ md.reset();
+ digestString(md, securityKey);
+ digestString(md, date);
+ digestString(md, server);
+ byte [] our = md.digest();
+ byte [] dst = new byte[digest.length() * 2];
+ for (int i = 0, j = 0; i < digest.length(); i++) {
+ char ch = digest.charAt(i);
+ dst[j++] = (byte)((ch >= 'A') ? ((ch & 0xdf) - 'A') + 10 : (ch - '0'));
+ }
+ return true;
+ }
+
+ /**
+ * True if listener is accepting the advetise messages.<br/>
+ * If false it means that listener is experiencing some
+ * network problems if running.
+ */
+ public boolean isListening()
+ {
+ return listening;
+ }
+
+
+ // ------------------------------------ AdvertiseListenerWorker Inner Class
+ private class AdvertiseListenerWorker implements Runnable
+ {
+
+ protected AdvertiseListenerWorker()
+ {
+ // Nothing
+ }
+ /**
+ * The background thread that listens for incoming Advertise packets
+ * and hands them off to an appropriate AdvertiseEvent handler.
+ */
+ public void run() {
+ byte[] buffer = new byte[512];
+ // Loop until we receive a shutdown command
+ while (running) {
+ // Loop if endpoint is paused
+ while (paused) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ try {
+ DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
+ ms.receive(dp);
+ if (!running)
+ break;
+ byte [] data = dp.getData();
+ boolean intr = false;
+ if (dp.getLength() == secure.length) {
+ int i;
+ for (i = 0; i < secure.length; i++) {
+ if (data[i] != secure[i])
+ break;
+ }
+ if (i == secure.length)
+ intr = true;
+ }
+ if (intr)
+ continue;
+ String s = new String(data, 0, dp.getLength(), "8859_1");
+ if (!s.startsWith("HTTP/1."))
+ continue;
+
+ String [] headers = s.split("\r\n");
+ String date_str = null;
+ Date date = null;
+ int status = 0;
+ String status_desc = null;
+ String digest = null;
+ String server_name = null;
+ AdvertisedServer server = null;
+ boolean added = false;
+ for (int i = 0; i < headers.length; i++) {
+ if (i == 0) {
+ String [] sline = headers[i].split(" ", 3);
+ if (sline == null || sline.length != 3)
+ break;
+ status = Integer.parseInt(sline[1]);
+ if (status < 100)
+ break;
+ status_desc = sline[2];
+ }
+ else {
+ String [] hdrv = headers[i].split(": ", 2);
+ if (hdrv == null || hdrv.length != 2)
+ break;
+ if (hdrv[0].equals("Date")) {
+ date_str = hdrv[1];
+ try {
+ date = df.parse(date_str);
+ } catch (ParseException e) {
+ date = new Date();
+ }
+ }
+ else if (hdrv[0].equals("Digest")) {
+ digest = hdrv[1];
+ }
+ else if (hdrv[0].equals("Server")) {
+ server_name = hdrv[1];
+ server = servers.get(server_name);
+ if (server == null) {
+ server = new AdvertisedServer(server_name);
+ added = true;
+ }
+ }
+ else if (server != null) {
+ server.setParameter(hdrv[0], hdrv[1]);
+ }
+ }
+ }
+ if (server != null && status > 0) {
+ if (md != null) {
+ /* We need a digest to match */
+ if (!verifyDigest(digest, server_name, date_str)) {
+ System.out.println("Digest mismatch");
+ continue;
+ }
+ }
+ server.setDate(date);
+ boolean rc = server.setStatus(status, status_desc);
+ if (added) {
+ servers.put(server_name, server);
+ // Call the new server callback
+ //eventHandler.onEvent(AdvertiseEventType.ON_NEW_SERVER, server);
+ String proxy = server.getParameter(AdvertisedServer.MANAGER_ADDRESS);
+ if (proxy != null) {
+ commHandler.addProxy(proxy);
+ }
+ }
+ else if (rc) {
+ // Call the status change callback
+ //eventHandler.onEvent(AdvertiseEventType.ON_STATUS_CHANGE, server);
+ }
+ }
+ listening = true;
+ } catch (IOException e) {
+ // Do not blow the CPU in case of communication error
+ listening = false;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException x) {
+ // Ignore
+ }
+ }
+ }
+ }
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertisedServer.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertisedServer.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/advertise/AdvertisedServer.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright(c) 2008 Red Hat Middleware, LLC,
+ * and individual contributors as indicated by the @authors tag.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ */
+
+package org.jboss.web.tomcat.service.modcluster.advertise;
+
+import java.util.Date;
+import java.util.HashMap;
+
+/**
+ * Advertised server instance
+ *
+ * @author Mladen Turk
+ *
+ */
+public class AdvertisedServer
+{
+ private String server;
+ private Date date;
+ private int status;
+ private String status_desc;
+ private HashMap<String, String> headers;
+
+ /** Manager-Address header */
+ public static String MANAGER_ADDRESS = "X-Manager-Address";
+ /** Manager-Url header */
+ public static String MANAGER_URL = "X-Manager-Url";
+ /** Manager-Protocol header */
+ public static String MANAGER_PROTOCOL = "X-Manager-Protocol";
+ /** Manager-Version header */
+ public static String MANAGER_VERSION = "X-Manager-Version";
+ /** Manager-Host header */
+ public static String MANAGER_HOST = "X-Manager-Host";
+
+ protected AdvertisedServer(String server)
+ {
+ this.server = server;
+ headers = new HashMap<String, String>();
+ }
+
+ protected boolean setStatus(int status, String desc)
+ {
+ boolean rv = false;
+ status_desc = desc;
+ if (this.status == 0 ) {
+ // First time
+ this.status = status;
+ }
+ else if (this.status != status) {
+ this.status = status;
+ rv = true;
+ }
+ return rv;
+ }
+
+ /** Set the Date of the last Advertise message
+ */
+ protected void setDate(Date date)
+ {
+ this.date = date;
+ }
+
+ /** Set the Header
+ */
+ protected void setParameter(String name, String value)
+ {
+ headers.put(name, value);
+ }
+
+ /** Get Date of the last Advertise message
+ */
+ public Date getDate()
+ {
+ return date;
+ }
+
+ /** Get Status code of the last Advertise message
+ */
+ public int getStatusCode()
+ {
+ return status;
+ }
+
+ /** Get Status description of the last Advertise message
+ */
+ public String getStatusDescription()
+ {
+ return status_desc;
+ }
+
+ /** Get Advertise parameter
+ */
+ public String getParameter(String name)
+ {
+ return headers.get(name);
+ }
+
+ public String toString()
+ {
+ return server;
+ }
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/BalancerConfiguration.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/BalancerConfiguration.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/BalancerConfiguration.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,40 @@
+/*
+ * 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.web.tomcat.service.modcluster.config;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public interface BalancerConfiguration
+{
+ boolean getStickySession();
+
+ boolean getStickySessionRemove();
+
+ boolean getStickySessionForce();
+
+ int getWorkerTimeout();
+
+ int getMaxAttempts();
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/MCMPHandlerConfiguration.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/MCMPHandlerConfiguration.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/MCMPHandlerConfiguration.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,49 @@
+/*
+ * 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.web.tomcat.service.modcluster.config;
+
+/**
+ * Configuration object for an {@link MCMPHandler}.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public interface MCMPHandlerConfiguration extends SSLConfiguration
+{
+ String getProxyList();
+
+ String getProxyURL();
+
+ int getSocketTimeout();
+
+ boolean isSsl();
+
+ Boolean getAdvertise();
+
+ String getAdvertiseGroupAddress();
+
+ int getAdvertisePort();
+
+ String getAdvertiseSecurityKey();
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/ModClusterConfig.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/ModClusterConfig.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/ModClusterConfig.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,349 @@
+/*
+ * 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.web.tomcat.service.modcluster.config;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+/**
+ * Java bean implementing the various configuration interfaces.
+ *
+ * @author Brian Stansberry
+ */
+public class ModClusterConfig
+ implements
+ BalancerConfiguration,
+ MCMPHandlerConfiguration,
+ NodeConfiguration,
+ SSLConfiguration
+{
+
+ // ----------------------------------------------- MCMPHandlerConfiguration
+
+ /**
+ * Receive advertisements from httpd proxies (default is to use advertisements
+ * if the proxyList is not set).
+ */
+ private Boolean advertise;
+ public Boolean getAdvertise() { return advertise; }
+ public void setAdvertise(Boolean advertise) { this.advertise = advertise; }
+
+
+ /**
+ * Advertise group.
+ */
+ private String advertiseGroupAddress = null;
+ public String getAdvertiseGroupAddress() { return advertiseGroupAddress; }
+ public void setAdvertiseGroupAddress(String advertiseGroupAddress) { this.advertiseGroupAddress = advertiseGroupAddress; }
+
+
+ /**
+ * Advertise port.
+ */
+ private int advertisePort = -1;
+ public int getAdvertisePort() { return advertisePort; }
+ public void setAdvertisePort(int advertisePort) { this.advertisePort = advertisePort; }
+
+
+ /**
+ * Advertise security key.
+ */
+ private String advertiseSecurityKey = null;
+ public String getAdvertiseSecurityKey() { return advertiseSecurityKey; }
+ public void setAdvertiseSecurityKey(String advertiseSecurityKey) { this.advertiseSecurityKey = advertiseSecurityKey; }
+
+
+ /**
+ * Proxy list, format "address:port,address:port".
+ */
+ private String proxyList = null;
+ public String getProxyList() { return proxyList; }
+ public void setProxyList(String proxyList) { this.proxyList = proxyList; }
+
+
+ /**
+ * URL prefix.
+ */
+ private String proxyURL = null;
+ public String getProxyURL() { return proxyURL; }
+ public void setProxyURL(String proxyURL) { this.proxyURL = proxyURL; }
+
+
+ /**
+ * Connection timeout for communication with the proxy.
+ */
+ private int socketTimeout = 20000;
+ public int getSocketTimeout() { return socketTimeout; }
+ public void setSocketTimeout(int socketTimeout) { this.socketTimeout = socketTimeout; }
+
+
+ // ----------------------------------------------------- SSLConfiguration
+
+ /**
+ * SSL client cert usage to connect to the proxy.
+ */
+ private boolean ssl = false;
+ public boolean isSsl() { return ssl; }
+ public void setSsl(boolean ssl) { this.ssl = ssl; }
+
+
+ /**
+ * SSL ciphers.
+ */
+ private String sslCiphers = null;
+ public String getSslCiphers() { return sslCiphers; }
+ public void setSslCiphers(String sslCiphers) { this.sslCiphers = sslCiphers; }
+
+
+ /**
+ * SSL protocol.
+ */
+ private String sslProtocol = "TLS";
+ public String getSslProtocol() { return sslProtocol; }
+ public void setSslProtocol(String sslProtocol) { this.sslProtocol = sslProtocol; }
+
+
+ /**
+ * Certificate encoding algorithm.
+ */
+ private String sslCertificateEncodingAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+ public String getSslCertificateEncodingAlgorithm() { return sslCertificateEncodingAlgorithm; }
+ public void setSslCertificateEncodingAlgorithm(String sslCertificateEncodingAlgorithm) { this.sslCertificateEncodingAlgorithm = sslCertificateEncodingAlgorithm; }
+
+
+ /**
+ * SSL keystore.
+ */
+ private String sslKeyStore = System.getProperty("user.home") + "/.keystore";
+ public String getSslKeyStore() { return sslKeyStore; }
+ public void setSslKeyStore(String sslKeyStore) { this.sslKeyStore = sslKeyStore; }
+
+
+ /**
+ * SSL keystore password.
+ */
+ private String sslKeyStorePass = "changeit";
+ public String getSslKeyStorePass() { return sslKeyStorePass; }
+ public void setSslKeyStorePass(String sslKeyStorePass) { this.sslKeyStorePass = sslKeyStorePass; }
+
+
+ /**
+ * Keystore type.
+ */
+ private String sslKeyStoreType = "JKS";
+ public String getSslKeyStoreType() { return sslKeyStoreType; }
+ public void setSslKeyStoreType(String sslKeyStoreType) { this.sslKeyStoreType = sslKeyStoreType; }
+
+
+ /**
+ * Keystore provider.
+ */
+ private String sslKeyStoreProvider = null;
+ public String getSslKeyStoreProvider() { return sslKeyStoreProvider; }
+ public void setSslKeyStoreProvider(String sslKeyStoreProvider) { this.sslKeyStoreProvider = sslKeyStoreProvider; }
+
+
+ /**
+ * Truststore algorithm.
+ */
+ private String sslTrustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ public String getSslTrustAlgorithm() { return sslTrustAlgorithm; }
+ public void setSslTrustAlgorithm(String sslTrustAlgorithm) { this.sslTrustAlgorithm = sslTrustAlgorithm; }
+
+
+ /**
+ * Key alias.
+ */
+ private String sslKeyAlias = null;
+ public String getSslKeyAlias() { return sslKeyAlias; }
+ public void setSslKeyAlias(String sslKeyAlias) { this.sslKeyAlias = sslKeyAlias; }
+
+
+ /**
+ * Certificate revocation list.
+ */
+ private String sslCrlFile = null;
+ public String getSslCrlFile() { return sslCrlFile; }
+ public void setSslCrlFile(String sslCrlFile) { this.sslCrlFile = sslCrlFile; }
+
+
+ /**
+ * Trust max certificate length.
+ */
+ private int sslTrustMaxCertLength = 5;
+ public int getSslTrustMaxCertLength() { return sslTrustMaxCertLength; }
+ public void setSslTrustMaxCertLength(int sslTrustMaxCertLength) { this.sslTrustMaxCertLength = sslTrustMaxCertLength; }
+
+
+ /**
+ * Trust store file.
+ */
+ private String sslTrustStore = System.getProperty("javax.net.ssl.trustStore");
+ public String getSslTrustStore() { return sslTrustStore; }
+ public void setSslTrustStore(String sslTrustStore) { this.sslTrustStore = sslTrustStore; }
+
+
+ /**
+ * Trust store password.
+ */
+ private String sslTrustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+ public String getSslTrustStorePassword() { return sslTrustStorePassword; }
+ public void setSslTrustStorePassword(String sslTrustStorePassword) { this.sslTrustStorePassword = sslTrustStorePassword; }
+
+
+ /**
+ * Trust store type.
+ */
+ private String sslTrustStoreType = System.getProperty("javax.net.ssl.trustStoreType");
+ public String getSslTrustStoreType() { return sslTrustStoreType; }
+ public void setSslTrustStoreType(String sslTrustStoreType) { this.sslTrustStoreType = sslTrustStoreType; }
+
+
+ /**
+ * Trust store provider.
+ */
+ private String sslTrustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider");
+ public String getSslTrustStoreProvider() { return sslTrustStoreProvider; }
+ public void setSslTrustStoreProvider(String sslTrustStoreProvider) { this.sslTrustStoreProvider = sslTrustStoreProvider; }
+
+
+ // ----------------------------------------------------- NodeConfiguration
+
+
+ /**
+ * Domain parameter, which allows tying a jvmRoute to a particular domain.
+ */
+ private String domain = null;
+ public String getDomain() { return domain; }
+ public void setDomain(String domain) { this.domain = domain; }
+
+
+ /**
+ * Allows controlling flushing of packets.
+ */
+ private boolean flushPackets = false;
+ public boolean getFlushPackets() { return flushPackets; }
+ public void setFlushPackets(boolean flushPackets) { this.flushPackets = flushPackets; }
+
+
+ /**
+ * Time to wait before flushing packets.
+ */
+ private int flushWait = -1;
+ public int getFlushWait() { return flushWait; }
+ public void setFlushWait(int flushWait) { this.flushWait = flushWait; }
+
+
+ /**
+ * Time to wait for a pong answer to a ping.
+ */
+ private int ping = -1;
+ public int getPing() { return ping; }
+ public void setPing(int ping) { this.ping = ping; }
+
+
+ /**
+ * Soft maximum inactive connection count.
+ */
+ private int smax = -1;
+ public int getSmax() { return smax; }
+ public void setSmax(int smax) { this.smax = smax; }
+
+
+ /**
+ * Maximum time on seconds for idle connections above smax.
+ */
+ private int ttl = -1;
+ public int getTtl() { return ttl; }
+ public void setTtl(int ttl) { this.ttl = ttl; }
+
+
+ /**
+ * Maximum time on seconds for idle connections the proxy will wait to connect to the node.
+ */
+ private int nodeTimeout = -1;
+ public int getNodeTimeout() { return nodeTimeout; }
+ public void setNodeTimeout(int nodeTimeout) { this.nodeTimeout = nodeTimeout; }
+
+
+ /**
+ * Name of the balancer.
+ */
+ private String balancer = null;
+ public String getBalancer() { return balancer; }
+ public void setBalancer(String balancer) { this.balancer = balancer; }
+
+ /**
+ * Our load balance factor
+ */
+ private int loadBalanceFactor = 1;
+ public int getLoadBalanceFactor() { return loadBalanceFactor; }
+ public void setLoadBalanceFactor(int loadBalanceFactor)
+ {
+ assert loadBalanceFactor >= 0 : "loadBalanceFactor is negative";
+ this.loadBalanceFactor = loadBalanceFactor;
+ }
+
+
+ // ------------------------------------------------- BalancerConfiguration
+
+ /**
+ * Enables sticky sessions.
+ */
+ private boolean stickySession = true;
+ public boolean getStickySession() { return stickySession; }
+ public void setStickySession(boolean stickySession) { this.stickySession = stickySession; }
+
+
+ /**
+ * Remove session when the request cannot be routed to the right node.
+ */
+ private boolean stickySessionRemove = false;
+ public boolean getStickySessionRemove() { return stickySessionRemove; }
+ public void setStickySessionRemove(boolean stickySessionRemove) { this.stickySessionRemove = stickySessionRemove; }
+
+
+ /**
+ * Return an error when the request cannot be routed to the right node.
+ */
+ private boolean stickySessionForce = true;
+ public boolean getStickySessionForce() { return stickySessionForce; }
+ public void setStickySessionForce(boolean stickySessionForce) { this.stickySessionForce = stickySessionForce; }
+
+
+ /**
+ * Timeout to wait for an available worker (default is no wait).
+ */
+ private int workerTimeout = -1;
+ public int getWorkerTimeout() { return workerTimeout; }
+ public void setWorkerTimeout(int workerTimeout) { this.workerTimeout = workerTimeout; }
+
+
+ /**
+ * Maximum number of attempts to send the request to the backend server.
+ */
+ private int maxAttempts = -1;
+ public int getMaxAttempts() { return maxAttempts; }
+ public void setMaxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/NodeConfiguration.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/NodeConfiguration.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/NodeConfiguration.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,47 @@
+/*
+ * 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.web.tomcat.service.modcluster.config;
+
+public interface NodeConfiguration
+{
+ int getSocketTimeout();
+
+ String getDomain();
+
+ boolean getFlushPackets();
+
+ int getFlushWait();
+
+ int getPing();
+
+ int getSmax();
+
+ int getTtl();
+
+ int getNodeTimeout();
+
+ String getBalancer();
+
+ int getLoadBalanceFactor();
+
+}
\ No newline at end of file
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/SSLConfiguration.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/SSLConfiguration.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/config/SSLConfiguration.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,49 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, 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.web.tomcat.service.modcluster.config;
+
+/**
+ * A SSLConfiguration.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public interface SSLConfiguration
+{
+ String getSslCiphers();
+ String getSslProtocol();
+ String getSslCertificateEncodingAlgorithm();
+ String getSslKeyStore();
+ String getSslKeyStorePass();
+ String getSslKeyStoreType();
+ String getSslKeyStoreProvider();
+ String getSslTrustAlgorithm();
+ String getSslKeyAlias();
+ String getSslCrlFile();
+ int getSslTrustMaxCertLength();
+ String getSslTrustStore();
+ String getSslTrustStorePassword();
+ String getSslTrustStoreType();
+ String getSslTrustStoreProvider();
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ClusteredMCMPHandler.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ClusteredMCMPHandler.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ClusteredMCMPHandler.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,56 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha;
+
+import java.util.List;
+import java.util.Set;
+
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.MCMPServerDiscoveryEvent;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPHandler;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServer;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServerState;
+
+/**
+ * @author Brian Stansberry
+ */
+public interface ClusteredMCMPHandler extends MCMPHandler
+{
+ public static final String HA_SERVICE_NAME = "ModCluster";
+
+ List<MCMPServerDiscoveryEvent> getPendingDiscoveryEvents();
+ void discoveryEventsReceived(MCMPServerDiscoveryEvent lastReceived);
+
+ Set<MCMPServerState> updateServersFromMasterNode(Set<MCMPServer> masterList);
+
+ boolean getNeedsResetTransmission();
+ void recordResetTransmission();
+ void recordResetSuccess();
+
+ boolean isMasterNode();
+ void setMasterNode(boolean master);
+
+ String getHAServiceName();
+ void setHAServiceName(String serviceName);
+
+ String getPartitionName();
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ClusteredMCMPHandlerImpl.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ClusteredMCMPHandlerImpl.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ClusteredMCMPHandlerImpl.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,537 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import net.jcip.annotations.GuardedBy;
+
+import org.apache.catalina.util.StringManager;
+import org.jboss.ha.framework.interfaces.HAPartition;
+import org.jboss.logging.Logger;
+import org.jboss.web.tomcat.service.modcluster.Constants;
+import org.jboss.web.tomcat.service.modcluster.Utils;
+import org.jboss.web.tomcat.service.modcluster.advertise.AdvertiseListener;
+import org.jboss.web.tomcat.service.modcluster.config.MCMPHandlerConfiguration;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.BooleanGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.GroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.GroupRpcResponseFilter;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.MCMPServerDiscoveryEvent;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.StringGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.ThrowableGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.mcmp.AddressPort;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPHandler;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPRequest;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServer;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServerState;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPUtils;
+
+public class ClusteredMCMPHandlerImpl implements ClusteredMCMPHandler
+{
+ private static final Object[] NULL_ARGS = new Object[0];
+ private static final Class[] NULL_TYPES = new Class[0];
+ private static final Class[] MCMPREQ_TYPES = new Class[] { MCMPRequest.class };
+ private static final Class[] DISC_EVENT_TYPES = new Class[] { MCMPServerDiscoveryEvent.class, int.class };
+
+ private static final Logger log = Logger.getLogger(ClusteredMCMPHandlerImpl.class);
+
+ private final MCMPHandler localHandler;
+ private final HAPartition partition;
+ private String haServiceName;
+ private AdvertiseListener advertiseListener;
+
+ private volatile boolean masterNode = false;
+
+ @GuardedBy("errorState")
+ private final List<Boolean> errorState = new ArrayList<Boolean>();
+
+ @GuardedBy("this")
+ private List<MCMPServerDiscoveryEvent> pendingDiscoveryEvents = new ArrayList<MCMPServerDiscoveryEvent>();
+
+ private AtomicInteger discoveryEventIndex = new AtomicInteger();
+
+ /**
+ * The string manager for this package.
+ */
+ private final StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+ public ClusteredMCMPHandlerImpl(MCMPHandler localHandler, HAPartition partition, String haServiceName)
+ {
+ this.localHandler = localHandler;
+ this.partition = partition;
+ this.haServiceName = haServiceName;
+ }
+
+ // --------------------------------------------------- ClusteredMCMPHandler
+
+ public boolean isMasterNode()
+ {
+ return this.masterNode;
+ }
+
+ public void setMasterNode(boolean masterNode)
+ {
+ this.masterNode = masterNode;
+ }
+
+ public String getHAServiceName()
+ {
+ return this.haServiceName;
+ }
+
+ public void setHAServiceName(String haServiceName)
+ {
+ this.haServiceName = haServiceName;
+ }
+
+ public String getPartitionName()
+ {
+ return this.partition.getPartitionName();
+ }
+
+ public synchronized List<MCMPServerDiscoveryEvent> getPendingDiscoveryEvents()
+ {
+ return new ArrayList<MCMPServerDiscoveryEvent>(pendingDiscoveryEvents);
+ }
+
+ public synchronized void discoveryEventsReceived(MCMPServerDiscoveryEvent lastReceived)
+ {
+ if (lastReceived != null)
+ {
+ for (Iterator<MCMPServerDiscoveryEvent> it = pendingDiscoveryEvents.iterator(); it.hasNext();)
+ {
+ MCMPServerDiscoveryEvent event = it.next();
+ if (event.getEventIndex() <= lastReceived.getEventIndex())
+ {
+ it.remove();
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ public synchronized Set<MCMPServerState> updateServersFromMasterNode(Set<MCMPServer> masterList)
+ {
+ for (MCMPServer server : masterList)
+ {
+ this.localHandler.addProxy(server.getAddress(), server.getPort());
+ }
+
+ for (MCMPServer server : this.localHandler.getProxyStates())
+ {
+ if (!masterList.contains(server))
+ {
+ this.localHandler.removeProxy(server.getAddress(), server.getPort());
+ }
+ }
+
+ this.localHandler.status();
+
+ return this.localHandler.getProxyStates();
+ }
+
+ public boolean getNeedsResetTransmission()
+ {
+ synchronized (errorState)
+ {
+ return errorState.size() > 0 && (errorState.get(errorState.size() -1).booleanValue() == false);
+ }
+ }
+
+ public void recordResetTransmission()
+ {
+ synchronized (errorState)
+ {
+ if (errorState.size() > 0)
+ {
+ errorState.set(0, Boolean.TRUE);
+ }
+ }
+ }
+
+ public void recordResetSuccess()
+ {
+ synchronized (errorState)
+ {
+ if (errorState.size() > 0 || errorState.get(errorState.size() -1).booleanValue())
+ {
+ errorState.remove(0);
+ }
+ }
+ }
+
+ // ------------------------------------------------------------ MCMPHandler
+
+ public MCMPHandlerConfiguration getConfiguration()
+ {
+ return this.localHandler.getConfiguration();
+ }
+
+ public void addProxy(String address)
+ {
+ AddressPort ap = MCMPUtils.parseAddressPort(address);
+ addProxy(ap.address, ap.port);
+ }
+
+ public void addProxy(String host, int port)
+ {
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByName(host);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ addProxy(address, port);
+ }
+
+ public synchronized void addProxy(InetAddress address, int port)
+ {
+ if (isMasterNode())
+ {
+ this.localHandler.addProxy(address, port);
+ }
+ else
+ {
+ sendDiscoveryEventToPartition(address, port, true);
+ }
+ }
+
+ public void establishProxy(MCMPServer server)
+ {
+ throw new UnsupportedOperationException("Should not be invoked on ClusteredMCMPHandler");
+ }
+
+
+ /**
+ * Remove proxy.
+ */
+ public synchronized void removeProxy(String host, int port)
+ {
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByName(host);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ removeProxy(address, port);
+ }
+
+ /**
+ * Remove proxy.
+ */
+ public synchronized void removeProxy(InetAddress address, int port)
+ {
+ if (isMasterNode())
+ {
+ this.localHandler.removeProxy(address, port);
+ }
+ else
+ {
+ sendDiscoveryEventToPartition(address, port, false);
+ }
+ }
+
+ public Set<MCMPServerState> getProxyStates()
+ {
+ return this.localHandler.getProxyStates();
+ }
+
+ public InetAddress getLocalAddress() throws IOException
+ {
+ return localHandler.getLocalAddress();
+ }
+
+ public String getProxyConfiguration()
+ {
+ if (isMasterNode())
+ {
+ return this.localHandler.getProxyConfiguration();
+ }
+ else
+ {
+ GroupRpcResponse result = invokeNoArgGroupRpc("getProxyConfiguration");
+ if (result instanceof StringGroupRpcResponse)
+ {
+ return ((StringGroupRpcResponse) result).getValue();
+ }
+ else
+ {
+ throw ((ThrowableGroupRpcResponse) result).getValueAsRuntimeException();
+ }
+ }
+ }
+
+ public void init()
+ {
+ startListener();
+ }
+
+ public boolean isProxyHealthOK()
+ {
+ if (isMasterNode())
+ {
+ return this.localHandler.isProxyHealthOK();
+ }
+ else
+ {
+ GroupRpcResponse result = invokeNoArgGroupRpc("isProxyHealthOK");
+ if (result instanceof BooleanGroupRpcResponse)
+ {
+ return ((BooleanGroupRpcResponse) result).getValue();
+ }
+ else
+ {
+ throw ((ThrowableGroupRpcResponse) result).getValueAsRuntimeException();
+ }
+ }
+ }
+
+ public void markProxiesInError()
+ {
+ recordRequestFailure();
+ }
+
+ public void refresh()
+ {
+ if (isMasterNode())
+ {
+ this.localHandler.refresh();
+ }
+ else
+ {
+ GroupRpcResponse result = invokeNoArgGroupRpc("refresh");
+ if (result instanceof ThrowableGroupRpcResponse)
+ {
+ throw ((ThrowableGroupRpcResponse) result).getValueAsRuntimeException();
+ }
+ }
+ }
+
+ public void reset()
+ {
+ if (isMasterNode())
+ {
+ this.localHandler.reset();
+ }
+ else
+ {
+ GroupRpcResponse result = invokeNoArgGroupRpc("reset");
+ if (result instanceof ThrowableGroupRpcResponse)
+ {
+ throw ((ThrowableGroupRpcResponse) result).getValueAsRuntimeException();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void sendRequest(MCMPRequest request)
+ {
+ if (isMasterNode())
+ {
+ this.localHandler.sendRequest(request);
+ }
+ else
+ {
+ GroupRpcResponse response = null;
+ try
+ {
+ List<Object> rsps = this.partition.callMethodOnCluster(this.haServiceName, "sendRequest", new Object[] { request }, MCMPREQ_TYPES, false, new GroupRpcResponseFilter());
+ response = extractGroupRpcResponse(rsps, "sendRequest");
+ }
+ catch (Exception e)
+ {
+ recordRequestFailure();
+ throw Utils.convertToUnchecked(e);
+ }
+
+ if (response instanceof ThrowableGroupRpcResponse)
+ {
+ recordRequestFailure();
+ throw ((ThrowableGroupRpcResponse) response).getValueAsRuntimeException();
+ }
+ }
+ }
+
+ public void shutdown()
+ {
+ stopListener();
+ }
+
+ public void status()
+ {
+ if (isMasterNode())
+ {
+ this.localHandler.status();
+ }
+ else
+ {
+ throw new UnsupportedOperationException("TODO implement me");
+ }
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ @SuppressWarnings("unchecked")
+ private GroupRpcResponse invokeNoArgGroupRpc(String methodName)
+ {
+ try
+ {
+ List<Object> rsps = this.partition.callMethodOnCluster(this.haServiceName, methodName, NULL_ARGS, NULL_TYPES, false, new GroupRpcResponseFilter());
+ return extractGroupRpcResponse(rsps, methodName);
+ }
+ catch (Exception e)
+ {
+ throw Utils.convertToUnchecked(e);
+ }
+ }
+
+ private GroupRpcResponse extractGroupRpcResponse(List<Object> responses, String methodName)
+ {
+ Throwable thrown = null;
+ for (Object obj : responses)
+ {
+ if (obj instanceof GroupRpcResponse)
+ {
+ return (GroupRpcResponse) obj;
+ }
+ else if (obj instanceof Throwable)
+ {
+ if (thrown == null)
+ {
+ thrown = (Throwable) obj;
+ }
+ }
+ else
+ {
+ log.warn("Unexpected response " + obj + "(" + obj.getClass() + ") to RPC " + methodName);
+ }
+ }
+
+ if (thrown != null)
+ throw Utils.convertToUnchecked(thrown);
+
+ throw new IllegalStateException("No valid response to RPC " + methodName);
+ }
+
+ /**
+ * Start the advertise listener.
+ */
+ private void startListener()
+ {
+ MCMPHandlerConfiguration handlerConfig = getConfiguration();
+ Boolean advertise = handlerConfig.getAdvertise();
+ if ((handlerConfig.getProxyList() == null && advertise == null)
+ || Boolean.TRUE.equals(advertise))
+ {
+ this.advertiseListener = new AdvertiseListener(this);
+ if (handlerConfig.getAdvertiseGroupAddress() != null) {
+ this.advertiseListener.setGroupAddress(handlerConfig.getAdvertiseGroupAddress());
+ }
+ if (handlerConfig.getAdvertisePort() > 0) {
+ this.advertiseListener.setAdvertisePort(handlerConfig.getAdvertisePort());
+ }
+ try {
+ if (handlerConfig.getAdvertiseSecurityKey() != null) {
+ this.advertiseListener.setSecurityKey(handlerConfig.getAdvertiseSecurityKey());
+ }
+ this.advertiseListener.start();
+ } catch (IOException e) {
+ log.error(sm.getString("clusterListener.error.startListener"), e);
+ } catch (NoSuchAlgorithmException e) {
+ // Should never happen
+ log.error(sm.getString("clusterListener.error.startListener"), e);
+ }
+ }
+ }
+
+
+ /**
+ * Stop the advertise listener.
+ */
+ private void stopListener()
+ {
+ if (this.advertiseListener != null) {
+ try {
+ this.advertiseListener.destroy();
+ } catch (IOException e) {
+ log.error(sm.getString("clusterListener.error.stopListener"), e);
+ }
+ this.advertiseListener = null;
+ }
+ }
+
+ private synchronized void sendDiscoveryEventToPartition(InetAddress address, int port, boolean addition)
+ {
+ AddressPort ap = new AddressPort(address, port);
+ MCMPServerDiscoveryEvent event = new MCMPServerDiscoveryEvent(this.partition.getClusterNode(), ap, addition, discoveryEventIndex.incrementAndGet());
+ pendingDiscoveryEvents.add(event);
+
+ GroupRpcResponse response = null;
+ Throwable throwable = null;;
+ try
+ {
+ List<Object> rsps = this.partition.callMethodOnCluster(this.haServiceName, "mcmpServerDiscoveryEvent", new Object[] { event }, DISC_EVENT_TYPES, false, new GroupRpcResponseFilter());
+ response = extractGroupRpcResponse(rsps, "mcmpServerDiscoveryEvent");
+ }
+ catch (Exception e)
+ {
+ throwable = e;
+ }
+
+ if (response instanceof ThrowableGroupRpcResponse)
+ {
+ throwable = ((ThrowableGroupRpcResponse) response).getValue();
+ }
+
+ if (throwable != null)
+ {
+ // Just log it; we'll retry later
+ log.error("Failure notifying master of " + (addition ? "added" : "removed") +
+ " proxy " + address.toString() + ":" + port, throwable);
+
+ }
+ }
+
+ private void recordRequestFailure()
+ {
+ synchronized (errorState)
+ {
+ if (errorState.size() == 0 || errorState.get(errorState.size() -1).booleanValue())
+ {
+ errorState.add(Boolean.FALSE);
+ }
+ }
+ }
+}
\ No newline at end of file
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/HASingletonAwareResetRequestSource.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/HASingletonAwareResetRequestSource.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/HASingletonAwareResetRequestSource.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,143 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.catalina.Server;
+import org.jboss.ha.framework.interfaces.HAPartition;
+import org.jboss.logging.Logger;
+import org.jboss.web.tomcat.service.modcluster.Utils;
+import org.jboss.web.tomcat.service.modcluster.config.BalancerConfiguration;
+import org.jboss.web.tomcat.service.modcluster.config.NodeConfiguration;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.ResetRequestGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.ha.rpc.ThrowableGroupRpcResponse;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPRequest;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPUtils;
+import org.jboss.web.tomcat.service.modcluster.mcmp.ResetRequestSource;
+
+/**
+ * {@link ResetRequestSource} that provides different reset requests
+ * depending on whether or not it believes it is running on the singleton
+ * master.
+ *
+ * @author Brian Stansberry
+ */
+public class HASingletonAwareResetRequestSource implements ResetRequestSource
+{
+ private static final Logger log = Logger.getLogger(HASingletonAwareResetRequestSource.class);
+
+ private final NodeConfiguration nodeConfig;
+ private final BalancerConfiguration balancerConfig;
+ private final HAPartition partition;
+ private final String haServiceName;
+ private volatile boolean master;
+ private volatile Server jbossWebServer;
+
+ public HASingletonAwareResetRequestSource(NodeConfiguration nodeConfig, BalancerConfiguration balancerConfig, HAPartition partition, String haServiceName)
+ {
+ this.nodeConfig = nodeConfig;
+ this.balancerConfig = balancerConfig;
+ this.partition = partition;
+ this.haServiceName = haServiceName;
+ }
+
+ public List<MCMPRequest> getResetRequests()
+ {
+ if (this.master)
+ {
+ List<MCMPRequest> resets = getLocalResetRequests();
+ addRemoteRequests(resets);
+ return resets;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public List<MCMPRequest> getLocalResetRequests()
+ {
+ if (this.jbossWebServer == null)
+ {
+ return new ArrayList<MCMPRequest>();
+ }
+
+ return MCMPUtils.getResetRequests(this.jbossWebServer, this.nodeConfig, this.balancerConfig);
+ }
+
+ public boolean isMasterNode()
+ {
+ return this.master;
+ }
+
+ public void setMasterNode(boolean master)
+ {
+ this.master = master;
+ }
+
+ public void setJbossWebServer(Server jbossWebServer)
+ {
+ this.jbossWebServer = jbossWebServer;
+ }
+
+ private void addRemoteRequests(List<MCMPRequest> resets)
+ {
+ List responses = null;
+ try
+ {
+ responses = this.partition.callMethodOnCluster(this.haServiceName, "getResetRequests", new Object[]{}, new Class[]{}, true);
+ }
+ catch (Exception e)
+ {
+ //FIXME what to do?
+ throw Utils.convertToUnchecked(e);
+ }
+
+ for (Object response : responses)
+ {
+ if (response instanceof ResetRequestGroupRpcResponse)
+ {
+ resets.addAll(((ResetRequestGroupRpcResponse) response).getValue());
+ }
+ else if (response instanceof ThrowableGroupRpcResponse)
+ {
+ ThrowableGroupRpcResponse tgrr = (ThrowableGroupRpcResponse) response;
+ //FIXME what to do?
+ log.warn("Call to getResetRequests received throwable from " + tgrr.getSender(), tgrr.getValue());
+ }
+ else if (response instanceof Throwable)
+ {
+ log.warn("Call to getResetRequests received throwable from unknown server", (Throwable) response);
+ }
+ else
+ {
+ log.error("Call to getResetRequests received unexpected response : " + response);
+ }
+
+ }
+
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ModClusterServiceDRMEntry.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ModClusterServiceDRMEntry.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ModClusterServiceDRMEntry.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,145 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServerState;
+
+/**
+ * Represents the status of a given MCMP client's ability to communicate
+ * with MCMP servers.
+ *
+ * @author Brian Stansberry
+ */
+public class ModClusterServiceDRMEntry implements Serializable, Comparable<ModClusterServiceDRMEntry>
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8275232749243297786L;
+
+ private final ClusterNode peer;
+ private final Set<MCMPServerState> mcmpServerStates;
+ private final Integer healthyEstablishedCount;
+ private final Integer establishedCount;
+ private final Integer healthyCount;
+ private final Integer knownCount;
+ private final Set<String> jvmRoutes = new HashSet<String>();
+
+ public ModClusterServiceDRMEntry(ClusterNode peer, Set<MCMPServerState> mcmpServerStates)
+ {
+ assert peer != null : "peer is null";
+
+ this.peer = peer;
+ this.mcmpServerStates = mcmpServerStates;
+
+ int healthyEstablished = 0;
+ int knownEstablished = 0;
+ int healthy = 0;
+ int known = 0;
+
+ if (this.mcmpServerStates != null)
+ {
+ for (MCMPServerState state : this.mcmpServerStates)
+ {
+ known++;
+ if (state.getState() == MCMPServerState.State.OK)
+ {
+ healthy++;
+ if (state.isEstablished())
+ {
+ knownEstablished++;
+ healthyEstablished++;
+ }
+ }
+ else if (state.isEstablished())
+ {
+ knownEstablished++;
+ }
+ }
+ }
+
+ this.establishedCount = new Integer(knownEstablished);
+ this.healthyCount = new Integer(healthy);
+ this.healthyEstablishedCount = new Integer(healthyEstablished);
+ this.knownCount = new Integer(known);
+ }
+
+ public ClusterNode getPeer()
+ {
+ return this.peer;
+ }
+
+ public Set<MCMPServerState> getMCMPServerStates()
+ {
+ return this.mcmpServerStates;
+ }
+
+ public Set<String> getJvmRoutes()
+ {
+ synchronized (jvmRoutes)
+ {
+ return new HashSet<String>(jvmRoutes);
+ }
+ }
+
+ public void addJvmRoute(String jvmRoute)
+ {
+ synchronized (jvmRoutes)
+ {
+ jvmRoutes.add(jvmRoute);
+ }
+ }
+
+ public void removeJvmRoute(String jvmRoute)
+ {
+ synchronized (jvmRoutes)
+ {
+ jvmRoutes.remove(jvmRoute);
+ }
+ }
+
+ public int compareTo(ModClusterServiceDRMEntry other)
+ {
+ int result = other.healthyEstablishedCount.compareTo(this.healthyEstablishedCount);
+ if (result == 0)
+ {
+ result = other.establishedCount.compareTo(this.establishedCount);
+ if (result == 0)
+ {
+ result = other.healthyCount.compareTo(this.healthyCount);
+ if (result == 0)
+ {
+ result = other.knownCount.compareTo(this.knownCount);
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+
+}
\ No newline at end of file
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ModClusterServiceHASingletonElectionPolicy.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ModClusterServiceHASingletonElectionPolicy.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/ModClusterServiceHASingletonElectionPolicy.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,97 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.singleton.HASingletonElectionPolicyBase;
+import org.jboss.ha.singleton.HASingletonElector;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class ModClusterServiceHASingletonElectionPolicy extends HASingletonElectionPolicyBase
+{
+ private final HASingletonElector delegate;
+
+
+ public ModClusterServiceHASingletonElectionPolicy(HASingletonElector delegate)
+ {
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected ClusterNode elect(List<ClusterNode> candidates)
+ {
+ if (candidates.size() == 1)
+ return candidates.get(0);
+ return delegate.elect(candidates);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected List<ClusterNode> getCandidates()
+ {
+ List<ModClusterServiceDRMEntry> candidates = getHAPartition().getDistributedReplicantManager().lookupReplicants(getSingletonName());
+ return narrowCandidateList(candidates);
+ }
+
+ public List<ClusterNode> narrowCandidateList(Collection<ModClusterServiceDRMEntry> candidates)
+ {
+ List<ClusterNode> narrowed = new ArrayList<ClusterNode>();
+ ModClusterServiceDRMEntry champion = null;
+
+ for (ModClusterServiceDRMEntry candidate : candidates)
+ {
+ if (champion == null)
+ {
+ champion = candidate;
+ narrowed.add(candidate.getPeer());
+ }
+ else
+ {
+ int compFactor = candidate.compareTo(champion);
+ if (compFactor < 0)
+ {
+ // New champ
+ narrowed.clear();
+ champion = candidate;
+ narrowed.add(candidate.getPeer());
+ }
+ else if (compFactor == 0)
+ {
+ // As good as our champ
+ narrowed.add(candidate.getPeer());
+ }
+ // else candidate didn't make the cut; continue
+ }
+ }
+
+ return narrowed;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/BooleanGroupRpcResponse.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/BooleanGroupRpcResponse.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/BooleanGroupRpcResponse.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,50 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+
+/**
+ * A {@link GroupRpcResponse} that wraps a boolean return value.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class BooleanGroupRpcResponse extends GroupRpcResponse
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8932443264578153750L;
+
+ private final boolean value;
+
+ public BooleanGroupRpcResponse(ClusterNode sender, boolean value)
+ {
+ super(sender);
+ this.value = value;
+ }
+
+ public boolean getValue()
+ {
+ return this.value;
+ }
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/GroupRpcResponse.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/GroupRpcResponse.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/GroupRpcResponse.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,52 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import java.io.Serializable;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.web.tomcat.service.modcluster.ModClusterService;
+
+/**
+ * A response to a group RPC call made by {@link ModClusterService}.
+ *
+ * @author Brian Stansberry
+ */
+public class GroupRpcResponse implements Serializable
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 7427355308214918459L;
+
+ private final ClusterNode sender;
+
+ public GroupRpcResponse(ClusterNode sender)
+ {
+ this.sender = sender;
+ }
+
+ public ClusterNode getSender()
+ {
+ return this.sender;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/GroupRpcResponseFilter.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/GroupRpcResponseFilter.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/GroupRpcResponseFilter.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,53 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.framework.interfaces.ResponseFilter;
+
+/**
+ * A {@link ResponseFilter} that accepts any GroupRpcResponse and doesn't
+ * need any further responses after receiving the first.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class GroupRpcResponseFilter implements ResponseFilter
+{
+ private boolean stillNeed = true;
+
+ public boolean isAcceptable(Object response, ClusterNode responder)
+ {
+ boolean acceptable = (response instanceof GroupRpcResponse);
+ if (acceptable)
+ this.stillNeed = false;
+
+ return acceptable;
+ }
+
+ public boolean needMoreResponses()
+ {
+ return this.stillNeed;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/InetAddressGroupRpcResponse.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/InetAddressGroupRpcResponse.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/InetAddressGroupRpcResponse.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,53 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import java.net.InetAddress;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+
+/**
+ * A {@link GroupRpcResponse} that wraps an InetAddress return value.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class InetAddressGroupRpcResponse extends GroupRpcResponse
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8932443264578153750L;
+
+ private final InetAddress value;
+
+ public InetAddressGroupRpcResponse(ClusterNode sender, InetAddress value)
+ {
+ super(sender);
+ this.value = value;
+ }
+
+ public InetAddress getValue()
+ {
+ return this.value;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/IntegerGroupRpcResponse.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/IntegerGroupRpcResponse.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/IntegerGroupRpcResponse.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,51 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+
+/**
+ * A {@link GroupRpcResponse} that wraps an int return value.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class IntegerGroupRpcResponse extends GroupRpcResponse
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8932443264578153750L;
+
+ private final int value;
+
+ public IntegerGroupRpcResponse(ClusterNode sender, int value)
+ {
+ super(sender);
+ this.value = value;
+ }
+
+ public int getValue()
+ {
+ return this.value;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/MCMPServerDiscoveryEvent.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/MCMPServerDiscoveryEvent.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/MCMPServerDiscoveryEvent.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,94 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import java.io.Serializable;
+
+import net.jcip.annotations.Immutable;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.web.tomcat.service.modcluster.mcmp.AddressPort;
+
+/**
+ * Event object indicating the discovery or requested removal of an
+ * {@link MCMPServer}.
+ *
+ * @author Brian Stansberry
+ */
+ at Immutable
+public class MCMPServerDiscoveryEvent implements Serializable
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -4615651826967237065L;
+
+ private final ClusterNode sender;
+ private final AddressPort mcmpServer;
+ private final boolean addition;
+ private final int eventIndex;
+
+ public MCMPServerDiscoveryEvent(ClusterNode sender, AddressPort mcmpServer, boolean addition, int eventIndex)
+ {
+ assert sender != null : "sender is null";
+ assert mcmpServer != null : "mcmpServer is null";
+
+ this.sender = sender;
+ this.mcmpServer = mcmpServer;
+ this.addition = addition;
+ this.eventIndex = eventIndex;
+ }
+
+ /**
+ * Creates a new MCMPServerDiscoveryEvent with the same values as an existing
+ * one, but a new event index. Used in resending events.
+ *
+ * @param toRecreate
+ * @param newEventIndex
+ */
+ public MCMPServerDiscoveryEvent(MCMPServerDiscoveryEvent toRecreate, int newEventIndex)
+ {
+ this(toRecreate.getSender(), toRecreate.getMCMPServer(), toRecreate.isAddition(), newEventIndex);
+ }
+
+
+ public ClusterNode getSender()
+ {
+ return this.sender;
+ }
+
+ public AddressPort getMCMPServer()
+ {
+ return this.mcmpServer;
+ }
+
+ public boolean isAddition()
+ {
+ return this.addition;
+ }
+
+ public int getEventIndex()
+ {
+ return this.eventIndex;
+ }
+
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ModClusterServiceStateGroupRpcResponse.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ModClusterServiceStateGroupRpcResponse.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ModClusterServiceStateGroupRpcResponse.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,81 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import java.util.List;
+import java.util.Set;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPRequest;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServerState;
+
+/**
+ * GroupRpcResponse that provides the overall status picture for a
+ * ModClusterService instance.
+ *
+ * @author Brian Stansberry
+ */
+public class ModClusterServiceStateGroupRpcResponse extends GroupRpcResponse
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -6591593007825931165L;
+
+ private final Set<MCMPServerState> states;
+ private final List<MCMPServerDiscoveryEvent> unacknowledgedEvents;
+ private final List<MCMPRequest> resetRequests;
+ private final int loadBalanceFactor;
+
+ public ModClusterServiceStateGroupRpcResponse(ClusterNode sender,
+ int loadBalanceFactor,
+ Set<MCMPServerState> states,
+ List<MCMPServerDiscoveryEvent> unacknowledgedEvents,
+ List<MCMPRequest> resetRequests)
+ {
+ super(sender);
+ this.loadBalanceFactor = loadBalanceFactor;
+ this.states = states;
+ this.unacknowledgedEvents = unacknowledgedEvents;
+ this.resetRequests = resetRequests;
+ }
+
+ public Set<MCMPServerState> getStates()
+ {
+ return this.states;
+ }
+
+ public List<MCMPServerDiscoveryEvent> getUnacknowledgedEvents()
+ {
+ return this.unacknowledgedEvents;
+ }
+
+ public List<MCMPRequest> getResetRequests()
+ {
+ return this.resetRequests;
+ }
+
+ public int getLoadBalanceFactor()
+ {
+ return this.loadBalanceFactor;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/PeerMCMPDiscoveryStatus.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/PeerMCMPDiscoveryStatus.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/PeerMCMPDiscoveryStatus.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,66 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import java.util.Set;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.web.tomcat.service.modcluster.ha.ModClusterServiceDRMEntry;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServerState;
+
+/**
+ * Extends {@link ModClusterServiceDRMEntry} to include information on
+ * the most recent discovery event from the peer that the current
+ * singleton master has included in the group-wide set of MCMP configurations.
+ *
+ * @author Brian Stansberry
+ */
+public class PeerMCMPDiscoveryStatus extends ModClusterServiceDRMEntry
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 3497115763128334162L;
+
+ private final MCMPServerDiscoveryEvent latestDiscoveryEvent;
+
+ /**
+ * Create a new PeerMCMPCommStatus.
+ *
+ * @param peer the id of the peer
+ * @param mcmpServerStates unmodifiable Set of MCMPServerState objects, or
+ * <code>null</code> if such a set of states could
+ * not be obtained for the peer.
+ * @param latestDiscoveryEvent most recent discovery event received from the peer
+ */
+ public PeerMCMPDiscoveryStatus(ClusterNode peer, Set<MCMPServerState> mcmpServerStates,
+ MCMPServerDiscoveryEvent latestDiscoveryEvent)
+ {
+ super(peer, mcmpServerStates);
+ this.latestDiscoveryEvent = latestDiscoveryEvent;
+ }
+
+ public MCMPServerDiscoveryEvent getLatestDiscoveryEvent()
+ {
+ return this.latestDiscoveryEvent;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ResetRequestGroupRpcResponse.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ResetRequestGroupRpcResponse.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ResetRequestGroupRpcResponse.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,54 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import java.util.List;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPRequest;
+
+/**
+ * A {@link GroupRpcResponse} that wraps a List<MCMPRequest> return value.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class ResetRequestGroupRpcResponse extends GroupRpcResponse
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8932443264578153750L;
+
+ private final List<MCMPRequest> value;
+
+ public ResetRequestGroupRpcResponse(ClusterNode sender, List<MCMPRequest> value)
+ {
+ super(sender);
+ this.value = value;
+ }
+
+ public List<MCMPRequest> getValue()
+ {
+ return this.value;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/StringGroupRpcResponse.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/StringGroupRpcResponse.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/StringGroupRpcResponse.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,51 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+
+/**
+ * A {@link GroupRpcResponse} that wraps a String return value.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class StringGroupRpcResponse extends GroupRpcResponse
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8932443264578153750L;
+
+ private final String value;
+
+ public StringGroupRpcResponse(ClusterNode sender, String value)
+ {
+ super(sender);
+ this.value = value;
+ }
+
+ public String getValue()
+ {
+ return this.value;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ThrowableGroupRpcResponse.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ThrowableGroupRpcResponse.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/ha/rpc/ThrowableGroupRpcResponse.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,60 @@
+/*
+ * 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.web.tomcat.service.modcluster.ha.rpc;
+
+import org.jboss.ha.framework.interfaces.ClusterNode;
+
+/**
+ * A {@link GroupRpcResponse} that wraps a String return value.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class ThrowableGroupRpcResponse extends GroupRpcResponse
+{
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 8932443264578153750L;
+
+ private final Throwable value;
+
+ public ThrowableGroupRpcResponse(ClusterNode sender, Throwable value)
+ {
+ super(sender);
+ this.value = value;
+ }
+
+ public Throwable getValue()
+ {
+ return this.value;
+ }
+
+ public RuntimeException getValueAsRuntimeException()
+ {
+ if (value instanceof RuntimeException)
+ return (RuntimeException) value;
+ else
+ {
+ return new RuntimeException("Group RPC returned exception", value);
+ }
+ }
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/LoadBalanceFactorProvider.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/LoadBalanceFactorProvider.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/LoadBalanceFactorProvider.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, 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.web.tomcat.service.modcluster.load;
+
+/**
+ * Provides the load balance factor for a node.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public interface LoadBalanceFactorProvider
+{
+ int getLoadBalanceFactor();
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/impl/StaticLoadBalanceFactorProvider.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/impl/StaticLoadBalanceFactorProvider.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/load/impl/StaticLoadBalanceFactorProvider.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,51 @@
+/*
+ * 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.web.tomcat.service.modcluster.load.impl;
+
+import org.jboss.web.tomcat.service.modcluster.load.LoadBalanceFactorProvider;
+
+/**
+ * A {@link LoadManagerImpl} that returns a static value.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class StaticLoadBalanceFactorProvider implements LoadBalanceFactorProvider
+{
+ private int loadBalanceFactor = 1;
+
+ // ------------------------------------------------------------- LoadManager
+
+ public int getLoadBalanceFactor()
+ {
+ return this.loadBalanceFactor;
+ }
+
+ // -------------------------------------------------------------- Properties
+
+ public void setLoadBalanceFactor(int loadBalanceFactor)
+ {
+ this.loadBalanceFactor = loadBalanceFactor;
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mbeans-descriptors.xml
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mbeans-descriptors.xml (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mbeans-descriptors.xml 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,138 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+ <mbean name="ClusterListener"
+ description="Cluster listener for mod_cluster"
+ domain="Catalina"
+ group="Listener"
+ type="org.jboss.web.cluster.ClusterListener">
+
+ <attribute name="className"
+ description="Fully qualified class name of the managed object"
+ type="java.lang.String"
+ writeable="false"/>
+
+ <attribute name="containerName"
+ description="Object name of the container"
+ type="javax.management.ObjectName"/>
+
+ <attribute name="proxyConfiguration"
+ description="Get the mod_cluster configuration"
+ type="java.lang.String"
+ writeable="false"/>
+
+ <attribute name="proxyList"
+ description="Comma delimited list of proxy servers"
+ type="java.lang.String"/>
+
+ <attribute name="socketTimeout"
+ description="Connection timeout for communication with the proxy"
+ type="int"/>
+
+ <attribute name="advertise"
+ description="Enable autodiscovery of httpd servers"
+ type="boolean"/>
+
+ <attribute name="advertiseGroupAddress"
+ description="Multicast address for discovery"
+ type="java.lang.String"/>
+
+ <attribute name="advertisePort"
+ description="Multicast port for discovery"
+ type="int"/>
+
+ <attribute name="advertiseSecurityKey"
+ description="Security key for discovery"
+ type="java.lang.String"/>
+
+ <attribute name="domain"
+ description="Domain parameter, which allows tying a jvmRoute to a particular domain"
+ type="java.lang.String"/>
+
+ <attribute name="flushPackets"
+ description="Allows controlling flushing of packets"
+ type="boolean"/>
+
+ <attribute name="flushWait"
+ description="Time in ms to wait before flushing packets"
+ type="int"/>
+
+ <attribute name="ping"
+ description="Time in s to wait for a pong answer to a ping"
+ type="int"/>
+
+ <attribute name="smax"
+ description="Maximum time on seconds for idle connections above smax"
+ type="int"/>
+
+ <attribute name="balancer"
+ description="Name of the balancer"
+ type="java.lang.String"/>
+
+ <attribute name="stickySession"
+ description="Enables sticky sessions"
+ type="boolean"/>
+
+ <attribute name="stickySessionRemove"
+ description="Remove session when the request cannot be routed to the right node"
+ type="boolean"/>
+
+ <attribute name="stickySessionForce"
+ description="Return an error when the request cannot be routed to the right node"
+ type="boolean"/>
+
+ <attribute name="workerTimeout"
+ description="Timeout to wait for an available worker (default is no wait)"
+ type="int"/>
+
+ <attribute name="maxAttempts"
+ description="Maximum number of attempts to send the request to the backend server"
+ type="int"/>
+
+ <operation name="refresh"
+ description="Refresh configuration"
+ impact="ACTION"
+ returnType="void"/>
+
+ <operation name="reset"
+ description="Move the node out of an error state"
+ impact="ACTION"
+ returnType="void"/>
+
+ <operation name="disable"
+ description="Disable all webapps for all engines"
+ impact="ACTION"
+ returnType="boolean"/>
+
+ <operation name="enable"
+ description="Enable all webapps for all engines"
+ impact="ACTION"
+ returnType="boolean"/>
+
+ <operation name="addProxy"
+ description="Add a proxy"
+ impact="ACTION"
+ returnType="void">
+ <parameter name="host"
+ description="Proxy address"
+ type="java.lang.String"/>
+ <parameter name="port"
+ description="Proxy port"
+ type="int"/>
+ </operation>
+
+ <operation name="removeProxy"
+ description="Remove a proxy"
+ impact="ACTION"
+ returnType="void">
+ <parameter name="host"
+ description="Proxy address"
+ type="java.lang.String"/>
+ <parameter name="port"
+ description="Proxy port"
+ type="int"/>
+ </operation>
+
+ </mbean>
+
+</mbeans-descriptors>
Property changes on: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mbeans-descriptors.xml
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/AddressPort.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/AddressPort.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/AddressPort.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,81 @@
+/*
+ * 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.web.tomcat.service.modcluster.mcmp;
+
+import java.net.InetAddress;
+
+/**
+ * Simple data object encapsulating an InetAddress and a port.
+ *
+ * @author Brian Stansberry
+ */
+public class AddressPort
+{
+ public final InetAddress address;
+ public final int port;
+
+ public AddressPort(InetAddress address, int port)
+ {
+ this.address = address;
+ this.port = port;
+ }
+
+ public boolean equals(InetAddress address, int port)
+ {
+ if (this.port != port) {
+ return false;
+ }
+ if (address == null) {
+ return (this.address == null);
+ }
+ else
+ {
+ return (address.equals(this.address));
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof AddressPort) {
+ AddressPort compare = (AddressPort) o;
+ return equals(compare.address, compare.port);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = 17;
+ result += 23 * (this.address == null ? 0 : this.address.hashCode());
+ result += 23 * this.port;
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "AddressPort{" + address + ":" + port + "}";
+ }
+
+}
\ No newline at end of file
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPHandler.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPHandler.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPHandler.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, 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.web.tomcat.service.modcluster.mcmp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.Set;
+
+import org.jboss.web.tomcat.service.modcluster.config.MCMPHandlerConfiguration;
+
+/**
+ * Handles communication via MCMP with the httpd side.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public interface MCMPHandler
+{
+ MCMPHandlerConfiguration getConfiguration();
+
+ void init();
+ void shutdown();
+
+ void sendRequest(MCMPRequest request);
+
+ void addProxy(String address);
+ void addProxy(String host, int port);
+ void addProxy(InetAddress address, int port);
+ void removeProxy(String host, int port);
+ void removeProxy(InetAddress address, int port);
+ void establishProxy(MCMPServer server);
+ Set<MCMPServerState> getProxyStates();
+ void reset();
+ void refresh();
+ void markProxiesInError();
+ boolean isProxyHealthOK();
+
+ InetAddress getLocalAddress() throws IOException;
+ String getProxyConfiguration();
+
+ void status();
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPRequest.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPRequest.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPRequest.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,83 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, 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.web.tomcat.service.modcluster.mcmp;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Encapsulates the parameters for a request over MCMP.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class MCMPRequest
+{
+ private final MCMPRequestType requestType;
+ private final boolean wildcard;
+ private final Map<String, String> parameters;
+
+ /**
+ * Create a new ModClusterRequest.
+ */
+ public MCMPRequest(MCMPRequestType requestType, boolean wildcard, Map<String, String> parameters)
+ {
+ this.requestType = requestType;
+ this.wildcard = wildcard;
+ this.parameters = Collections.unmodifiableMap(new HashMap<String, String>(parameters));
+ }
+
+ public MCMPRequestType getRequestType()
+ {
+ return requestType;
+ }
+
+ public boolean isWildcard()
+ {
+ return wildcard;
+ }
+
+ public Map<String, String> getParameters()
+ {
+ return parameters;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(getClass().getName());
+ sb.append("{requestType=");
+ sb.append(requestType);
+ sb.append(",wildcard=");
+ sb.append(wildcard);
+ sb.append(",parameters=");
+ synchronized (parameters)
+ {
+ sb.append(parameters);
+ }
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPRequestType.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPRequestType.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPRequestType.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, 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.web.tomcat.service.modcluster.mcmp;
+
+/**
+ * Valid types of MCMP requests.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public enum MCMPRequestType
+{
+ CONFIG("CONFIG", true),
+ ENABLE_APP("ENABLE-APP", false),
+ DISABLE_APP("DISABLE-APP", false),
+ STOP_APP("STOP-APP", false),
+ REMOVE_APP("REMOVE-APP", false),
+ STATUS("STATUS", false),
+ INFO("INFO", false),
+ DUMP("DUMP", false);
+
+ private final String command;
+ private final boolean establishesServer;
+
+ private MCMPRequestType(String command, boolean establishesServer)
+ {
+ this.command = command;
+ this.establishesServer = establishesServer;
+ }
+
+ public String getCommand()
+ {
+ return this.command;
+ }
+
+ public boolean getEstablishesServer()
+ {
+ return this.establishesServer;
+ }
+
+ public String toString()
+ {
+ return command;
+ }
+
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPServer.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPServer.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPServer.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,43 @@
+/*
+ * 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.web.tomcat.service.modcluster.mcmp;
+
+import java.net.InetAddress;
+
+/**
+ * Represents a native server that is running the <code>mod_cluster</code>
+ * module and proxying requests to JBoss Web. For example, an Apache httpd
+ * instance. Such an instance represents the server in the Mod Cluster
+ * Management Protocol, with an MCMPHandler acting as the client.
+ *
+ * @author Brian Stansberry
+ */
+public interface MCMPServer
+{
+ InetAddress getAddress();
+
+ int getPort();
+
+ boolean isEstablished();
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPServerState.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPServerState.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPServerState.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,39 @@
+/*
+ * 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.web.tomcat.service.modcluster.mcmp;
+
+
+/**
+ * Extends {@link MCMPServer} to provide information about the current
+ * state of communications with that server.
+ *
+ * @author Brian Stansberry
+ */
+public interface MCMPServerState extends MCMPServer
+{
+ /** Possible communication states vis a vis the server */
+ public enum State { OK, ERROR, DOWN };
+
+ State getState();
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPUtils.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPUtils.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/MCMPUtils.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,254 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, 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.web.tomcat.service.modcluster.mcmp;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.jboss.web.tomcat.service.modcluster.Utils;
+import org.jboss.web.tomcat.service.modcluster.config.BalancerConfiguration;
+import org.jboss.web.tomcat.service.modcluster.config.NodeConfiguration;
+
+/**
+ * Utility methods related to the Mod-Cluster Management Protocol.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class MCMPUtils
+{
+ public static MCMPRequest createConfigRequest(Engine engine, NodeConfiguration nodeConfig, BalancerConfiguration balancerConfig)
+ {
+ Connector connector = Utils.findProxyConnector(engine.getService().findConnectors());
+ Map<String, String> parameters = new HashMap<String, String>();
+ parameters.put("JVMRoute", engine.getJvmRoute());
+ boolean reverseConnection =
+ Boolean.TRUE.equals(IntrospectionUtils.getProperty(connector.getProtocolHandler(), "reverseConnection"));
+ boolean ssl =
+ Boolean.TRUE.equals(IntrospectionUtils.getProperty(connector.getProtocolHandler(), "SSLEnabled"));
+ boolean ajp = ((String) IntrospectionUtils.getProperty(connector.getProtocolHandler(), "name")).startsWith("ajp-");
+
+ if (reverseConnection) {
+ parameters.put("Reversed", "true");
+ }
+ parameters.put("Host", Utils.getAddress(connector));
+ parameters.put("Port", "" + connector.getPort());
+ if (ajp) {
+ parameters.put("Type", "ajp");
+ } else if (ssl) {
+ parameters.put("Type", "https");
+ } else {
+ parameters.put("Type", "http");
+ }
+
+ // Other configuration parameters
+ if (nodeConfig.getDomain() != null) {
+ parameters.put("Domain", nodeConfig.getDomain());
+ }
+ if (nodeConfig.getFlushPackets()) {
+ parameters.put("flushpackets", "On");
+ }
+ if (nodeConfig.getFlushWait() != -1) {
+ parameters.put("flushwait", "" + nodeConfig.getFlushWait());
+ }
+ if (nodeConfig.getPing() != -1) {
+ parameters.put("ping", "" + nodeConfig.getPing());
+ }
+ if (nodeConfig.getSmax() != -1) {
+ parameters.put("smax", "" + nodeConfig.getSmax());
+ }
+ if (nodeConfig.getTtl() != -1) {
+ parameters.put("ttl", "" + nodeConfig.getTtl());
+ }
+ if (nodeConfig.getNodeTimeout() != -1) {
+ parameters.put("Timeout", "" + nodeConfig.getNodeTimeout());
+ }
+ if (nodeConfig.getBalancer() != null) {
+ parameters.put("Balancer", nodeConfig.getBalancer());
+ }
+ if (!balancerConfig.getStickySession()) {
+ parameters.put("StickySession", "No");
+ }
+ if (!org.apache.catalina.Globals.SESSION_COOKIE_NAME.equals("JSESSIONID")) {
+ parameters.put("StickySessionCookie", org.apache.catalina.Globals.SESSION_COOKIE_NAME);
+ }
+ if (!org.apache.catalina.Globals.SESSION_PARAMETER_NAME.equals("jsessionid")) {
+ parameters.put("StickySessionPath", org.apache.catalina.Globals.SESSION_PARAMETER_NAME);
+ }
+ if (balancerConfig.getStickySessionRemove()) {
+ parameters.put("StickSessionRemove", "Yes");
+ }
+ if (!balancerConfig.getStickySessionForce()) {
+ parameters.put("StickySessionForce", "No");
+ }
+ if (balancerConfig.getWorkerTimeout() != -1) {
+ parameters.put("WaitWorker", "" + balancerConfig.getWorkerTimeout());
+ }
+ if (balancerConfig.getMaxAttempts() != -1) {
+ parameters.put("Maxattempts", "" + balancerConfig.getMaxAttempts());
+ }
+
+ return new MCMPRequest(MCMPRequestType.CONFIG, false, parameters);
+ }
+
+ public static MCMPRequest createEnableAppRequest(Context context)
+ {
+ return new MCMPRequest(MCMPRequestType.ENABLE_APP, false, createContextParameters(context));
+ }
+
+ public static MCMPRequest createDisableAppRequest(Context context)
+ {
+ return new MCMPRequest(MCMPRequestType.DISABLE_APP, false, createContextParameters(context));
+ }
+
+ public static MCMPRequest createStopAppRequest(Context context)
+ {
+ return new MCMPRequest(MCMPRequestType.STOP_APP, false, createContextParameters(context));
+ }
+
+ public static MCMPRequest createRemoveAppRequest(Context context)
+ {
+ return new MCMPRequest(MCMPRequestType.REMOVE_APP, false, createContextParameters(context));
+ }
+
+ public static MCMPRequest createStatusRequest(Engine engine, int lbf)
+ {
+ Map<String, String> parameters = new HashMap<String, String>();
+ parameters.put("JVMRoute", engine.getJvmRoute());
+ parameters.put("Load", String.valueOf(lbf));
+ return new MCMPRequest(MCMPRequestType.STATUS, false, parameters);
+ }
+
+ public static MCMPRequest createStatusRequest(String jvmRoute, int lbf)
+ {
+ Map<String, String> parameters = new HashMap<String, String>();
+ parameters.put("JVMRoute", jvmRoute);
+ parameters.put("Load", String.valueOf(lbf));
+ return new MCMPRequest(MCMPRequestType.STATUS, false, parameters);
+ }
+
+ public static MCMPRequest createEnableEngineRequest(Engine engine)
+ {
+ return new MCMPRequest(MCMPRequestType.ENABLE_APP, true, createEngineParameters(engine));
+ }
+
+ public static MCMPRequest createDisableEngineRequest(Engine engine)
+ {
+ return new MCMPRequest(MCMPRequestType.DISABLE_APP, true, createEngineParameters(engine));
+ }
+
+ public static MCMPRequest createRemoveAllRequest(Engine engine)
+ {
+ return new MCMPRequest(MCMPRequestType.REMOVE_APP, true, createEngineParameters(engine));
+ }
+
+ /**
+ * Reset configuration for a particular proxy following an error.
+ */
+ public static List<MCMPRequest> getResetRequests(Server server,
+ NodeConfiguration nodeConfig, BalancerConfiguration balancerConfig) {
+
+ List<MCMPRequest> requests = new ArrayList<MCMPRequest>();
+ Service[] services = server.findServices();
+ for (int i = 0; i < services.length; i++) {
+ Engine engine = (Engine) services[i].getContainer();
+ if (engine.getJvmRoute() != null)
+ {
+ requests.add(MCMPUtils.createRemoveAllRequest(engine));
+ }
+ requests.add(MCMPUtils.createConfigRequest(engine, nodeConfig, balancerConfig));
+ Container[] children = engine.findChildren();
+ for (int j = 0; j < children.length; j++) {
+ Container[] children2 = children[j].findChildren();
+ for (int k = 0; k < children2.length; k++) {
+ Context ctx = (Context) children2[k];
+ if (ctx.isStarted())
+ {
+ requests.add(MCMPUtils.createEnableAppRequest(ctx));
+ }
+ }
+ }
+ }
+
+ return requests;
+ }
+
+ private static Map<String, String> createEngineParameters(Engine engine)
+ {
+ Map<String, String> parameters = new HashMap<String, String>();
+ parameters.put("JVMRoute", engine.getJvmRoute());
+ return parameters;
+ }
+
+ private static Map<String, String> createContextParameters(Context context)
+ {
+ Map<String, String> parameters = new HashMap<String, String>();
+ parameters.put("JVMRoute", Utils.getJvmRoute(context));
+ parameters.put("Context", ("".equals(context.getPath())) ? "/" : context.getPath());
+ parameters.put("Alias", Utils.getHost(context));
+ return parameters;
+ }
+
+ public static AddressPort parseAddressPort(String addressPort)
+ {
+ int pos = addressPort.indexOf(':');
+ String host = null;
+ int port = 0;
+ if (pos < 0) {
+ host = addressPort;
+ } else if (pos == 0) {
+ host = null;
+ port = Integer.parseInt(addressPort.substring(1));
+ } else {
+ host = addressPort.substring(0, pos);
+ port = Integer.parseInt(addressPort.substring(pos + 1));
+ }
+
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByName(host);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ return new AddressPort(address, port);
+ }
+
+ /**
+ * Disable external instantiation.
+ */
+ private MCMPUtils()
+ {
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/ResetRequestSource.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/ResetRequestSource.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/ResetRequestSource.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,45 @@
+/*
+ * 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.web.tomcat.service.modcluster.mcmp;
+
+import java.util.List;
+
+/**
+ * Source for a list of requests that should be sent to an httpd-side
+ * mod_cluster instance when an {@link MCMPHandler} determines that
+ * the httpd-side state needs to be reset.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public interface ResetRequestSource
+{
+ /**
+ * Gets a list of requests that should be sent to an httpd-side
+ * mod_cluster instance when an {@link MCMPHandler} determines that
+ * its state needs to be reset.
+ *
+ * @return a list of requests. Will not return <code>null</code>.
+ */
+ List<MCMPRequest> getResetRequests();
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/DefaultMCMPHandler.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/DefaultMCMPHandler.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/DefaultMCMPHandler.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,990 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, 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.web.tomcat.service.modcluster.mcmp.impl;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.UEncoder;
+import org.jboss.logging.Logger;
+import org.jboss.web.tomcat.service.modcluster.Constants;
+import org.jboss.web.tomcat.service.modcluster.advertise.AdvertiseListener;
+import org.jboss.web.tomcat.service.modcluster.config.MCMPHandlerConfiguration;
+import org.jboss.web.tomcat.service.modcluster.mcmp.AddressPort;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPHandler;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPRequest;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPRequestType;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServer;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPServerState;
+import org.jboss.web.tomcat.service.modcluster.mcmp.MCMPUtils;
+import org.jboss.web.tomcat.service.modcluster.mcmp.ResetRequestSource;
+
+/**
+ * Default implementation of {@link MCMPHandler}.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public class DefaultMCMPHandler implements MCMPHandler
+{
+ protected static Logger log = Logger.getLogger(DefaultMCMPHandler.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+
+ // -------------------------------------------------------------- Constants
+
+
+
+
+
+ // ----------------------------------------------------------------- Fields
+
+ /** Our configuration */
+ private final MCMPHandlerConfiguration config;
+
+
+ /**
+ * Source for reset requests when we need to reset a proxy.
+ */
+ private final ResetRequestSource resetRequestSource;
+
+ /**
+ * URL encoder used to generate requests bodies.
+ */
+ private UEncoder encoder = new UEncoder();
+
+ /**
+ * Proxies.
+ */
+ private volatile Proxy[] proxies = null;
+
+
+ /**
+ * Add proxy list.
+ */
+ private ArrayList<Proxy> addProxies = new ArrayList<Proxy>();
+
+
+ /**
+ * Remove proxy list.
+ */
+ private ArrayList<Proxy> removeProxies = new ArrayList<Proxy>();
+
+
+ /**
+ * Socket factory.
+ */
+ private JSSESocketFactory sslSocketFactory = null;
+
+ private final boolean allowAdvertise;
+
+ /**
+ * Advertise listener.
+ */
+ private AdvertiseListener listener = null;
+
+ /** Initialization completion flag */
+ private boolean init;
+
+ // ----------------------------------------------------------- Constructors
+
+ public DefaultMCMPHandler(MCMPHandlerConfiguration config, ResetRequestSource source)
+ {
+ this(config, source, true);
+ }
+
+ public DefaultMCMPHandler(MCMPHandlerConfiguration config, ResetRequestSource source, boolean allowAdvertise)
+ {
+ this.config = config;
+ this.resetRequestSource = source;
+ this.allowAdvertise = allowAdvertise;
+ }
+
+
+ // -------------------------------------------------------------- Properties
+
+
+
+
+ // ------------------------------------------------------------ MCMPHandler
+
+ public MCMPHandlerConfiguration getConfiguration()
+ {
+ return this.config;
+ }
+
+ public ResetRequestSource getResetRequestSource()
+ {
+ return this.resetRequestSource;
+ }
+
+ public synchronized void init()
+ {
+ if (this.init)
+ return;
+
+ sslInit();
+
+ if (this.config.getProxyList() == null) {
+ if (Boolean.FALSE.equals(this.config.getAdvertise()) == false) {
+ proxies = new Proxy[0];
+ startListener();
+ } else {
+ // Default to a httpd on localhost on the default port
+ proxies = new Proxy[1];
+ try
+ {
+ proxies[0] = new Proxy(this.sslSocketFactory, this.config.getSocketTimeout());
+ }
+ catch (UnknownHostException e)
+ {
+ proxies = new Proxy[0];
+ log.fatal("No proxy list or URL configured, advertise disabled " +
+ "and no localhost available; connect connect to mod_cluster", e);
+ }
+ }
+ } else {
+ ArrayList<Proxy> proxyList = new ArrayList<Proxy>();
+ StringTokenizer tok = new StringTokenizer(this.config.getProxyList(), ",");
+ while (tok.hasMoreTokens()) {
+ String token = tok.nextToken().trim();
+ int pos = token.indexOf(':');
+ String address = null;
+ int port = Proxy.DEFAULT_PORT;
+ if (pos < 0) {
+ address = token;
+ } else if (pos == 0) {
+ address = null;
+ port = Integer.parseInt(token.substring(1));
+ } else {
+ address = token.substring(0, pos);
+ port = Integer.parseInt(token.substring(pos + 1));
+ }
+ InetAddress inetAddress = null;
+ try {
+ if (address != null) {
+ inetAddress = InetAddress.getByName(address);
+ }
+ } catch (Exception e) {
+ log.error(sm.getString("clusterListener.error.invalidHost", address), e);
+ continue;
+ }
+ Proxy proxy = new Proxy(inetAddress, port, this.sslSocketFactory, this.config.getSocketTimeout());
+ proxyList.add(proxy);
+ }
+ proxies = proxyList.toArray(new Proxy[proxyList.size()]);
+
+ if (Boolean.TRUE.equals(this.config.getAdvertise()))
+ {
+ startListener();
+ }
+ }
+
+ this.init = true;
+
+ }
+
+ public synchronized void shutdown()
+ {
+ if (!this.init)
+ return;
+
+ stopListener();
+
+ for (int i = 0; i < proxies.length; i++) {
+ synchronized (proxies[i])
+ {
+ proxies[i].closeConnection();
+ }
+ }
+
+ this.sslSocketFactory = null;
+
+ this.init = false;
+ }
+
+ /**
+ * Add proxy.
+ */
+ public void addProxy(String address) {
+
+ AddressPort ap = MCMPUtils.parseAddressPort(address);
+ addProxy(ap.address, ap.port);
+ }
+
+ /**
+ * Add proxy.
+ */
+ public synchronized void addProxy(String host, int port)
+ {
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByName(host);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ addProxyInternal(address, port);
+ }
+
+ public synchronized void addProxy(InetAddress address, int port)
+ {
+ addProxyInternal(address, port);
+ }
+
+ private synchronized Proxy addProxyInternal(InetAddress address, int port)
+ {
+ for (Proxy candidate : proxies)
+ {
+ if (candidate.getAddress().equals(address) && candidate.getPort() == port)
+ return candidate;
+ }
+ for (Proxy candidate : addProxies)
+ {
+ if (candidate.getAddress().equals(address) && candidate.getPort() == port)
+ return candidate;
+ }
+ for (Proxy candidate : removeProxies)
+ {
+ if (candidate.getAddress().equals(address) && candidate.getPort() == port)
+ return candidate;
+ }
+
+ Proxy proxy = new Proxy(address, port, this.sslSocketFactory, this.config.getSocketTimeout());
+ proxy.setState(Proxy.State.ERROR);
+ addProxies.add(proxy);
+ return proxy;
+ }
+
+ public synchronized void establishProxy(MCMPServer server)
+ {
+ Proxy proxy = addProxyInternal(server.getAddress(), server.getPort());
+ proxy.setEstablished(server.isEstablished());
+ }
+
+
+ /**
+ * Remove proxy.
+ */
+ public synchronized void removeProxy(String host, int port)
+ {
+ InetAddress address = null;
+ try {
+ address = InetAddress.getByName(host);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ removeProxy(address, port);
+ }
+
+ /**
+ * Remove proxy.
+ */
+ public synchronized void removeProxy(InetAddress address, int port)
+ {
+ Proxy proxy = new Proxy(address, port, this.sslSocketFactory, this.config.getSocketTimeout());
+ removeProxies.add(proxy);
+ }
+
+ public synchronized Set<MCMPServerState> getProxyStates()
+ {
+ Set<MCMPServerState> result = new HashSet<MCMPServerState>(proxies.length);
+ for (Proxy proxy : proxies)
+ {
+ result.add(proxy);
+ }
+ return result;
+ }
+
+ public synchronized boolean isProxyHealthOK()
+ {
+ boolean ok = true;
+ Proxy[] local = proxies;
+ for (Proxy proxy : local)
+ {
+ if (proxy.getState() != Proxy.State.OK)
+ {
+ ok = false;
+ break;
+ }
+ }
+ return ok;
+ }
+
+ public synchronized void markProxiesInError()
+ {
+ Proxy[] local = proxies;
+ for (Proxy proxy : local)
+ {
+ synchronized (proxy)
+ {
+ if (proxy.getState() == Proxy.State.OK)
+ {
+ proxy.setState(Proxy.State.ERROR);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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() {
+ HashMap<String, String> parameters = new HashMap<String, String>();
+ // Send DUMP * request
+ Proxy[] local = proxies;
+ StringBuffer result = new StringBuffer();
+ for (int i = 0; i < local.length; i++) {
+
+ result.append("Proxy[").append(i).append("]:[").append(local[i].getAddress())
+ .append(':').append(local[i].getPort()).append("]: \r\n");
+ synchronized (local[i])
+ {
+ result.append(sendRequest(new MCMPRequest(MCMPRequestType.DUMP, true, parameters), local[i]));
+ }
+ result.append("\r\n");
+ }
+ return result.toString();
+ }
+
+ public InetAddress getLocalAddress() throws IOException
+ {
+ IOException firstException = null;
+ Proxy[] local = proxies;
+ for (Proxy proxy : local)
+ {
+ try
+ {
+ return proxy.getConnection().getInetAddress();
+ }
+ catch (IOException e)
+ {
+ // Cache the exception in case no other connection works,
+ // but keep trying
+ if (firstException == null)
+ firstException = e;
+ }
+ }
+
+ if (firstException != null)
+ throw firstException;
+
+ // We get here if there are no proxies
+ return null;
+ }
+
+
+ /**
+ * 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() {
+ Proxy[] local = proxies;
+ for (Proxy proxy : local) {
+ synchronized (proxy)
+ {
+ if (proxy.getState() == Proxy.State.DOWN) {
+ proxy.setState(Proxy.State.ERROR);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Refresh configuration. To be used through JMX or similar.
+ */
+ public void refresh() {
+ // Set as error, and the periodic event will refresh the configuration
+ Proxy[] local = proxies;
+ for (Proxy proxy : local) {
+ synchronized (proxy)
+ {
+ if (proxy.getState() == Proxy.State.OK) {
+ proxy.setState(Proxy.State.ERROR);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Send a periodic status request. If in error state, the listener will attempt to refresh
+ * the configuration on the front end server.
+ *
+ * @param engine
+ */
+ public void status() {
+
+ // Check to add or remove proxies, and rebuild a new list if needed
+ synchronized (this) {
+ if (!addProxies.isEmpty() || !removeProxies.isEmpty()) {
+ ArrayList<Proxy> currentProxies = new ArrayList<Proxy>();
+ for (int i = 0; i < proxies.length; i++) {
+ currentProxies.add(proxies[i]);
+ }
+ for (int i = 0; i < addProxies.size(); i++) {
+ if (!currentProxies.contains(addProxies.get(i))) {
+ currentProxies.add(addProxies.get(i));
+ }
+ }
+ for (int i = 0; i < removeProxies.size(); i++) {
+ if (currentProxies.contains(removeProxies.get(i))) {
+ currentProxies.remove(removeProxies.get(i));
+ }
+ }
+ addProxies.clear();
+ removeProxies.clear();
+ proxies = currentProxies.toArray(new Proxy[0]);
+
+ // Reset all connections
+ for (Proxy proxy : proxies)
+ {
+ proxy.closeConnection();
+ }
+ }
+ }
+
+ // Attempt to reset any proxies in error
+ List<MCMPRequest> resetRequests = null;
+ Proxy[] local = proxies;
+ for (Proxy proxy : local) {
+ synchronized (proxy)
+ {
+ if (proxy.getState() == Proxy.State.ERROR) {
+ proxy.setState(Proxy.State.OK);
+ if (resetRequests == null)
+ {
+ resetRequests = this.resetRequestSource.getResetRequests();
+ }
+
+ for (MCMPRequest request : resetRequests)
+ {
+ sendRequest(request, proxy);
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Send HTTP request, with the specified list of parameters. If an IO error occurs, the error state will
+ * be set. If the front end server reports an error, will mark as error Proxy.State. Other unexpected exceptions
+ * will be thrown and the error state will be set.
+ *
+ * @param command
+ * @param wildcard
+ * @param parameters
+ * @return the response body as a String; null if in error state or a normal error occurs
+ */
+ public void sendRequest(MCMPRequest request) {
+ Proxy[] local = proxies;
+ for (Proxy proxy : local)
+ {
+ sendRequest(request, proxy);
+ }
+ }
+
+ private String sendRequest(MCMPRequest request, Proxy proxy) {
+
+ BufferedReader reader = null;
+ BufferedWriter writer = null;
+ CharChunk body = null;
+
+ String command = request.getRequestType().getCommand();
+ boolean wildcard = request.isWildcard();
+ Map<String, String> parameters = request.getParameters();
+
+ // First, encode the POST body
+ try {
+ body = encoder.encodeURL("", 0, 0);
+ body.recycle();
+ Iterator<String> keys = parameters.keySet().iterator();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ String value = parameters.get(key);
+ if (value == null) {
+ throw new IllegalArgumentException(sm.getString("clusterListener.error.nullAttribute", key));
+ }
+ body = encoder.encodeURL(key, 0, key.length());
+ body.append('=');
+ if (value != null) {
+ body = encoder.encodeURL(value, 0, value.length());
+ }
+ if (keys.hasNext()) {
+ body.append('&');
+ }
+ }
+ } catch (IOException e) {
+ body.recycle();
+ // Error encoding URL, should not happen
+ throw new IllegalArgumentException(e);
+ }
+
+ // If there was an error, do nothing until the next periodic event, where the whole configuration
+ // will be refreshed
+ if (proxy.getState() != Proxy.State.OK) {
+ return null;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("clusterListener.request", command, wildcard, proxy));
+ }
+
+ synchronized (proxy)
+ {
+
+ try {
+
+ // Then, connect to the proxy
+ proxy.getConnection();
+ writer = proxy.getConnectionWriter();
+ // Check connection to see if it is still alive (not really allowed,
+ // but httpd deals with the extra CRLF)
+ try {
+ writer.write("\r\n");
+ writer.flush();
+ } catch (IOException e) {
+ // Get a new connection; if it fails this second time, it is an error
+ proxy.closeConnection();
+ proxy.getConnection();
+ writer = proxy.getConnectionWriter();
+ }
+
+ // Generate and write request
+ String url = this.config.getProxyURL();
+ if (url == null) {
+ url = (wildcard) ? "/*" : "/";
+ } else {
+ if (url.endsWith("/") && wildcard) {
+ url = url + "*";
+ } else if (wildcard) {
+ url = url + "/*";
+ }
+ }
+ String requestLine = command + " " + url + " HTTP/1.0";
+ writer.write(requestLine);
+ writer.write("\r\n");
+ writer.write("Content-Length: " + body.getLength() + "\r\n");
+ writer.write("User-Agent: ClusterListener/1.0\r\n");
+ writer.write("Connection: Keep-Alive\r\n");
+ writer.write("\r\n");
+ writer.write(body.getBuffer(), body.getStart(), body.getLength());
+ writer.write("\r\n");
+ writer.flush();
+
+ // Read the response to a string
+ reader = proxy.getConnectionReader();
+ // Read the first response line and skip the rest of the HTTP header
+ String responseStatus = reader.readLine();
+ // Parse the line, which is formed like HTTP/1.x YYY Message
+ int status = 500;
+ String version = "0";
+ String message = null;
+ String errorType = null;
+ int contentLength = 0;
+ if (responseStatus != null) {
+ try {
+ responseStatus = responseStatus.substring(responseStatus.indexOf(' ') + 1, responseStatus.indexOf(' ', responseStatus.indexOf(' ') + 1));
+ status = Integer.parseInt(responseStatus);
+ String header = reader.readLine();
+ while (!"".equals(header)) {
+ int colon = header.indexOf(':');
+ String headerName = header.substring(0, colon).trim();
+ String headerValue = header.substring(colon + 1).trim();
+ if ("version".equalsIgnoreCase(headerName)) {
+ version = headerValue;
+ } else if ("type".equalsIgnoreCase(headerName)) {
+ errorType = headerValue;
+ } else if ("mess".equalsIgnoreCase(headerName)) {
+ message = headerValue;
+ } else if ("content-length".equalsIgnoreCase(headerName)) {
+ contentLength = Integer.parseInt(headerValue);
+ }
+ header = reader.readLine();
+ }
+ } catch (Exception e) {
+ log.info(sm.getString("clusterListener.error.parse", command), e);
+ }
+ }
+
+ // Mark as error if the front end server did not return 200; the configuration will
+ // be refreshed during the next periodic event
+ if (status == 200) {
+
+ // We know the request succeeded, so if appropriate
+ // mark the proxy as established before any possible
+ // later exception happens
+ if (request.getRequestType().getEstablishesServer()) {
+ proxy.setEstablished(true);
+ }
+
+ // Read the request body
+ StringBuffer result = new StringBuffer();
+ if (contentLength > 0) {
+ int thisTime = contentLength;
+ char[] buf = new char[512];
+ while (contentLength > 0) {
+ thisTime = (contentLength > buf.length) ? buf.length : contentLength;
+ int n = reader.read(buf, 0, thisTime);
+ if (n <= 0) {
+ break;
+ } else {
+ result.append(buf, 0, n);
+ contentLength -= n;
+ }
+ }
+ }
+
+ return result.toString();
+
+ } else {
+ if ("SYNTAX".equals(errorType)) {
+ // Syntax error means the protocol is incorrect, which cannot be automatically fixed
+ proxy.setState(Proxy.State.DOWN);
+ log.error(sm.getString("clusterListener.error.syntax", command, proxy, errorType, message));
+ } else {
+ proxy.setState(Proxy.State.ERROR);
+ log.error(sm.getString("clusterListener.error.other", command, proxy, errorType, message));
+ }
+ }
+
+ } catch (IOException e) {
+ // Most likely this is a connection error with the proxy
+ proxy.setState(Proxy.State.ERROR);
+ log.info(sm.getString("clusterListener.error.io", command, proxy), e);
+ } finally {
+ // If there's an error of any sort, or if the proxy did not return 200, it is an error
+ if (proxy.getState() != Proxy.State.OK) {
+ proxy.closeConnection();
+ }
+ }
+ }
+
+ return null;
+
+ }
+
+
+ /**
+ * SSL init.
+ */
+ protected void sslInit() {
+ if (this.config.isSsl()) {
+ sslSocketFactory = new JSSESocketFactory(this.config);
+ }
+ }
+
+ /**
+ * Start the advertise listener.
+ */
+ protected void startListener()
+ {
+ if (this.allowAdvertise)
+ {
+ listener = new AdvertiseListener(this);
+ if (this.config.getAdvertiseGroupAddress() != null) {
+ listener.setGroupAddress(this.config.getAdvertiseGroupAddress());
+ }
+ if (this.config.getAdvertisePort() > 0) {
+ listener.setAdvertisePort(this.config.getAdvertisePort());
+ }
+ try {
+ if (this.config.getAdvertiseSecurityKey() != null) {
+ listener.setSecurityKey(this.config.getAdvertiseSecurityKey());
+ }
+ listener.start();
+ } catch (IOException e) {
+ log.error(sm.getString("clusterListener.error.startListener"), e);
+ } catch (NoSuchAlgorithmException e) {
+ // Should never happen
+ log.error(sm.getString("clusterListener.error.startListener"), e);
+ }
+ }
+ }
+
+
+ /**
+ * Stop the advertise listener.
+ */
+ protected void stopListener() {
+ if (listener != null) {
+ try {
+ listener.destroy();
+ } catch (IOException e) {
+ log.error(sm.getString("clusterListener.error.stopListener"), e);
+ }
+ listener = null;
+ }
+ }
+
+ /**
+ * This class represents a front-end httpd server.
+ */
+ private static class Proxy implements MCMPServerState
+ {
+ public static final int DEFAULT_PORT = 8000;
+
+ private final InetAddress address;
+ private final int port;
+ private volatile State state = State.OK;
+ private volatile boolean established;
+ private final JSSESocketFactory sslSocketFactory;
+ private final int socketTimeout;
+ private Socket connection;
+ private BufferedReader connectionReader;
+ private BufferedWriter connectionWriter;
+
+ /**
+ * The string manager for this package.
+ */
+ private final StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+ private Proxy(int socketTimeout) throws UnknownHostException
+ {
+ this(InetAddress.getLocalHost(), DEFAULT_PORT, null, socketTimeout);
+ }
+
+ private Proxy(JSSESocketFactory sslSocketFactory, int socketTimeout) throws UnknownHostException
+ {
+ this(InetAddress.getLocalHost(), DEFAULT_PORT, sslSocketFactory, socketTimeout);
+ }
+
+ private Proxy(InetAddress address, int socketTimeout)
+ {
+ this(address, DEFAULT_PORT, null, socketTimeout);
+ }
+
+ private Proxy(InetAddress address, JSSESocketFactory sslSocketFactory, int socketTimeout)
+ {
+ this(address, DEFAULT_PORT, sslSocketFactory, socketTimeout);
+ }
+
+ private Proxy(InetAddress address, int port, int socketTimeout)
+ {
+ this(address, port, null, socketTimeout);
+ }
+
+ private Proxy(InetAddress address, int port, JSSESocketFactory sslSocketFactory, int socketTimeout)
+ {
+ if (address == null)
+ {
+ throw new IllegalArgumentException("address is null");
+ }
+ if (port <= 0)
+ {
+ throw new IllegalArgumentException("invalid port");
+ }
+
+ this.address = address;
+ this.port = port;
+ this.sslSocketFactory = sslSocketFactory;
+ this.socketTimeout = socketTimeout;
+ }
+
+ // -------------------------------------------- MCMPServerConnectionState
+
+ public synchronized State getState()
+ {
+ return this.state;
+ }
+
+ // ----------------------------------------------------------- MCMPServer
+
+ public InetAddress getAddress()
+ {
+ return this.address;
+ }
+
+ public int getPort()
+ {
+ return this.port;
+ }
+
+ public boolean isEstablished()
+ {
+ return this.established;
+ }
+
+ // ------------------------------------------------------------ Overrides
+
+ public String toString() {
+ if (this.address == null) {
+ return ":" + this.port;
+ } else {
+ return this.address.getHostAddress() + ":" + this.port;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Proxy) {
+ Proxy compare = (Proxy) o;
+ if (this.port != compare.port) {
+ return false;
+ }
+ if (compare.address == null) {
+ if (this.address == null) {
+ return true;
+ }
+ } else if ((compare.address.equals(this.address)) && this.port == compare.port) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = 17;
+ result += 23 * (this.address == null ? 0 : this.address.hashCode());
+ result += 23 * this.port;
+ return result;
+ }
+
+ // -------------------------------------------------------------- Private
+
+ private synchronized void setState(State state)
+ {
+ if (state == null)
+ {
+ throw new IllegalArgumentException("state is null");
+ }
+ this.state = state;
+ }
+
+ private synchronized void setEstablished(boolean established)
+ {
+ this.established = established;
+ }
+
+ /**
+ * Return a reader to the proxy.
+ */
+ private synchronized Socket getConnection()
+ throws IOException {
+ if (connection == null || connection.isClosed()) {
+ if (this.sslSocketFactory != null) {
+ connection = this.sslSocketFactory.createSocket(this.address, this.port);
+ } else {
+ connection = new Socket(this.address, this.port);
+ }
+ connection.setSoTimeout(this.socketTimeout);
+ }
+ return connection;
+ }
+
+
+ /**
+ * Return a reader to the proxy.
+ */
+ private synchronized BufferedReader getConnectionReader()
+ throws IOException {
+ if (this.connectionReader == null) {
+ this.connectionReader = new BufferedReader(new InputStreamReader(this.connection.getInputStream()));
+ }
+ return this.connectionReader;
+ }
+
+
+ /**
+ * Return a writer to the proxy.
+ */
+ private synchronized BufferedWriter getConnectionWriter()
+ throws IOException {
+ if (connectionWriter == null) {
+ connectionWriter = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream()));
+ }
+ return connectionWriter;
+ }
+
+
+ /**
+ * Close connection.
+ */
+ private synchronized void closeConnection() {
+ try {
+ if (connectionReader != null) {
+ connectionReader.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ connectionReader = null;
+ try {
+ if (connectionWriter != null) {
+ connectionWriter.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ connectionWriter = null;
+ try {
+ if (connection != null) {
+ connection.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+ connection = null;
+ }
+ }
+
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/JSSEKeyManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/JSSEKeyManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/JSSEKeyManager.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jboss.web.tomcat.service.modcluster.mcmp.impl;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.X509KeyManager;
+
+/**
+ * X509KeyManager which allows selection of a specific keypair and certificate
+ * chain (identified by their keystore alias name) to be used by the server to
+ * authenticate itself to SSL clients.
+ *
+ * @author Jan Luehe
+ */
+public final class JSSEKeyManager implements X509KeyManager {
+
+ private X509KeyManager delegate;
+ private String serverKeyAlias;
+
+ /**
+ * Constructor.
+ *
+ * @param mgr The X509KeyManager used as a delegate
+ * @param serverKeyAlias The alias name of the server's keypair and
+ * supporting certificate chain
+ */
+ public JSSEKeyManager(X509KeyManager mgr, String serverKeyAlias) {
+ this.delegate = mgr;
+ this.serverKeyAlias = serverKeyAlias;
+ }
+
+ /**
+ * Choose an alias to authenticate the client side of a secure socket,
+ * given the public key type and the list of certificate issuer authorities
+ * recognized by the peer (if any).
+ *
+ * @param keyType The key algorithm type name(s), ordered with the
+ * most-preferred key type first
+ * @param issuers The list of acceptable CA issuer subject names, or null
+ * if it does not matter which issuers are used
+ * @param socket The socket to be used for this connection. This parameter
+ * can be null, in which case this method will return the most generic
+ * alias to use
+ *
+ * @return The alias name for the desired key, or null if there are no
+ * matches
+ */
+ public String chooseClientAlias(String[] keyType, Principal[] issuers,
+ Socket socket) {
+ return delegate.chooseClientAlias(keyType, issuers, socket);
+ }
+
+ /**
+ * Returns this key manager's server key alias that was provided in the
+ * constructor.
+ *
+ * @param keyType The key algorithm type name (ignored)
+ * @param issuers The list of acceptable CA issuer subject names, or null
+ * if it does not matter which issuers are used (ignored)
+ * @param socket The socket to be used for this connection. This parameter
+ * can be null, in which case this method will return the most generic
+ * alias to use (ignored)
+ *
+ * @return Alias name for the desired key
+ */
+ public String chooseServerAlias(String keyType, Principal[] issuers,
+ Socket socket) {
+ return serverKeyAlias;
+ }
+
+ /**
+ * Returns the certificate chain associated with the given alias.
+ *
+ * @param alias The alias name
+ *
+ * @return Certificate chain (ordered with the user's certificate first
+ * and the root certificate authority last), or null if the alias can't be
+ * found
+ */
+ public X509Certificate[] getCertificateChain(String alias) {
+ return delegate.getCertificateChain(alias);
+ }
+
+ /**
+ * Get the matching aliases for authenticating the client side of a secure
+ * socket, given the public key type and the list of certificate issuer
+ * authorities recognized by the peer (if any).
+ *
+ * @param keyType The key algorithm type name
+ * @param issuers The list of acceptable CA issuer subject names, or null
+ * if it does not matter which issuers are used
+ *
+ * @return Array of the matching alias names, or null if there were no
+ * matches
+ */
+ public String[] getClientAliases(String keyType, Principal[] issuers) {
+ return delegate.getClientAliases(keyType, issuers);
+ }
+
+ /**
+ * Get the matching aliases for authenticating the server side of a secure
+ * socket, given the public key type and the list of certificate issuer
+ * authorities recognized by the peer (if any).
+ *
+ * @param keyType The key algorithm type name
+ * @param issuers The list of acceptable CA issuer subject names, or null
+ * if it does not matter which issuers are used
+ *
+ * @return Array of the matching alias names, or null if there were no
+ * matches
+ */
+ public String[] getServerAliases(String keyType, Principal[] issuers) {
+ return delegate.getServerAliases(keyType, issuers);
+ }
+
+ /**
+ * Returns the key associated with the given alias.
+ *
+ * @param alias The alias name
+ *
+ * @return The requested key, or null if the alias can't be found
+ */
+ public PrivateKey getPrivateKey(String alias) {
+ return delegate.getPrivateKey(alias);
+ }
+}
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/JSSESocketFactory.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/JSSESocketFactory.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/modcluster/mcmp/impl/JSSESocketFactory.java 2008-07-21 05:54:56 UTC (rev 76052)
@@ -0,0 +1,552 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jboss.web.tomcat.service.modcluster.mcmp.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertStore;
+import java.security.cert.CertStoreParameters;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.util.Collection;
+import java.util.Vector;
+
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+
+import org.apache.tomcat.util.res.StringManager;
+import org.jboss.web.tomcat.service.modcluster.config.SSLConfiguration;
+
+/*
+ 1. Make the JSSE's jars available, either as an installed
+ extension (copy them into jre/lib/ext) or by adding
+ them to the Tomcat classpath.
+ 2. keytool -genkey -alias tomcat -keyalg RSA
+ Use "changeit" as password ( this is the default we use )
+ */
+
+/**
+ * SSL server socket factory. It _requires_ a valid RSA key and
+ * JSSE.
+ *
+ * @author Harish Prabandham
+ * @author Costin Manolache
+ * @author Stefan Freyr Stefansson
+ * @author EKR -- renamed to JSSESocketFactory
+ * @author Jan Luehe
+ * @author Bill Barker
+ */
+public class JSSESocketFactory {
+
+ private static StringManager sm =
+ StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
+
+ static org.jboss.logging.Logger log =
+ org.jboss.logging.Logger.getLogger(JSSESocketFactory.class);
+
+ protected boolean initialized;
+ //protected String clientAuth = "false";
+ protected SSLSocketFactory sslProxy = null;
+ protected String[] enabledCiphers;
+ protected SSLConfiguration config = null;
+
+ /**
+ * Flag to state that we require client authentication.
+ */
+ //protected boolean requireClientAuth = false;
+
+ /**
+ * Flag to state that we would like client authentication.
+ */
+ //protected boolean wantClientAuth = false;
+
+
+ public JSSESocketFactory (SSLConfiguration config) {
+ this.config = config;
+ }
+
+ public Socket createSocket (InetAddress ifAddress, int port)
+ throws IOException
+ {
+ if (!initialized) init();
+ Socket socket = sslProxy.createSocket(ifAddress, port);
+ initSocket(socket);
+ return socket;
+ }
+
+ public void handshake(Socket sock) throws IOException {
+ ((SSLSocket)sock).startHandshake();
+ }
+
+ /*
+ * Determines the SSL cipher suites to be enabled.
+ *
+ * @param requestedCiphers Comma-separated list of requested ciphers
+ * @param supportedCiphers Array of supported ciphers
+ *
+ * @return Array of SSL cipher suites to be enabled, or null if none of the
+ * requested ciphers are supported
+ */
+ protected String[] getEnabledCiphers(String requestedCiphers,
+ String[] supportedCiphers) {
+
+ String[] enabledCiphers = null;
+
+ if (requestedCiphers != null) {
+ Vector<String> vec = null;
+ String cipher = requestedCiphers;
+ int index = requestedCiphers.indexOf(',');
+ if (index != -1) {
+ int fromIndex = 0;
+ while (index != -1) {
+ cipher = requestedCiphers.substring(fromIndex, index).trim();
+ if (cipher.length() > 0) {
+ /*
+ * Check to see if the requested cipher is among the
+ * supported ciphers, i.e., may be enabled
+ */
+ for (int i=0; supportedCiphers != null
+ && i<supportedCiphers.length; i++) {
+ if (supportedCiphers[i].equals(cipher)) {
+ if (vec == null) {
+ vec = new Vector<String>();
+ }
+ vec.addElement(cipher);
+ break;
+ }
+ }
+ }
+ fromIndex = index+1;
+ index = requestedCiphers.indexOf(',', fromIndex);
+ } // while
+ cipher = requestedCiphers.substring(fromIndex);
+ }
+
+ if (cipher != null) {
+ cipher = cipher.trim();
+ if (cipher.length() > 0) {
+ /*
+ * Check to see if the requested cipher is among the
+ * supported ciphers, i.e., may be enabled
+ */
+ for (int i=0; supportedCiphers != null
+ && i<supportedCiphers.length; i++) {
+ if (supportedCiphers[i].equals(cipher)) {
+ if (vec == null) {
+ vec = new Vector<String>();
+ }
+ vec.addElement(cipher);
+ break;
+ }
+ }
+ }
+ }
+
+ if (vec != null) {
+ enabledCiphers = new String[vec.size()];
+ vec.copyInto(enabledCiphers);
+ }
+ } else {
+ enabledCiphers = sslProxy.getDefaultCipherSuites();
+ }
+
+ return enabledCiphers;
+ }
+
+ /*
+ * Gets the SSL server's keystore.
+ */
+ protected KeyStore getKeystore(String type, String provider, String pass)
+ throws IOException {
+ return getStore(type, provider, config.getSslKeyStore(), pass);
+ }
+
+ /*
+ * Gets the SSL server's truststore.
+ */
+ protected KeyStore getTrustStore(String keystoreType,
+ String keystoreProvider) throws IOException {
+ KeyStore trustStore = null;
+
+ String truststorePassword = config.getSslTrustStorePassword();
+ if( truststorePassword == null ) {
+ truststorePassword = config.getSslKeyStorePass();
+ } else if (truststorePassword.equals("")) {
+ truststorePassword = null;
+ }
+ String truststoreType = config.getSslTrustStoreType();
+ if(truststoreType == null) {
+ truststoreType = keystoreType;
+ }
+ String truststoreProvider = config.getSslTrustStoreProvider();
+ if (truststoreProvider == null) {
+ truststoreProvider = keystoreProvider;
+ }
+
+ if (config.getSslTrustStore() != null){
+ trustStore = getStore(truststoreType, truststoreProvider,
+ config.getSslTrustStore(), truststorePassword);
+ }
+
+ return trustStore;
+ }
+
+ /*
+ * Gets the key- or truststore with the specified type, path, and password.
+ */
+ private KeyStore getStore(String type, String provider, String path,
+ String pass) throws IOException {
+
+ KeyStore ks = null;
+ InputStream istream = null;
+ try {
+ if (provider == null) {
+ ks = KeyStore.getInstance(type);
+ } else {
+ ks = KeyStore.getInstance(type, provider);
+ }
+ if(!("PKCS11".equalsIgnoreCase(type) || "".equalsIgnoreCase(path))) {
+ File keyStoreFile = new File(path);
+ if (!keyStoreFile.isAbsolute()) {
+ keyStoreFile = new File(System.getProperty("catalina.base"),
+ path);
+ }
+ istream = new FileInputStream(keyStoreFile);
+ }
+
+ if (pass == null)
+ ks.load(istream, null);
+ else
+ ks.load(istream, pass.toCharArray());
+ } catch (FileNotFoundException fnfe) {
+ log.error(sm.getString("jsse.keystore_load_failed", type, path,
+ fnfe.getMessage()), fnfe);
+ throw fnfe;
+ } catch (IOException ioe) {
+ log.error(sm.getString("jsse.keystore_load_failed", type, path,
+ ioe.getMessage()), ioe);
+ throw ioe;
+ } catch(Exception ex) {
+ String msg = sm.getString("jsse.keystore_load_failed", type, path,
+ ex.getMessage());
+ log.error(msg, ex);
+ throw new IOException(msg);
+ } finally {
+ if (istream != null) {
+ try {
+ istream.close();
+ } catch (IOException ioe) {
+ // Do nothing
+ }
+ }
+ }
+
+ return ks;
+ }
+
+ /**
+ * Reads the keystore and initializes the SSL socket factory.
+ */
+ void init() throws IOException {
+ try {
+
+ /**
+ String clientAuthStr = (String) attributes.get("clientauth");
+ if("true".equalsIgnoreCase(clientAuthStr) ||
+ "yes".equalsIgnoreCase(clientAuthStr)) {
+ requireClientAuth = true;
+ } else if("want".equalsIgnoreCase(clientAuthStr)) {
+ wantClientAuth = true;
+ }*/
+
+ // Create and init SSLContext
+ SSLContext context = SSLContext.getInstance(config.getSslProtocol());
+ context.init(getKeyManagers(config.getSslKeyStoreType(),
+ config.getSslKeyStoreProvider(),
+ config.getSslCertificateEncodingAlgorithm(),
+ config.getSslKeyAlias()),
+ getTrustManagers(config.getSslKeyStoreType(), config.getSslKeyStoreProvider(),
+ config.getSslTrustAlgorithm()),
+ new SecureRandom());
+
+ // create proxy
+ sslProxy = context.getSocketFactory();
+
+ // Determine which cipher suites to enable
+ enabledCiphers = getEnabledCiphers(config.getSslCiphers(),
+ sslProxy.getSupportedCipherSuites());
+
+ } catch(Exception e) {
+ if( e instanceof IOException )
+ throw (IOException)e;
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ /**
+ * Gets the initialized key managers.
+ */
+ protected KeyManager[] getKeyManagers(String keystoreType,
+ String keystoreProvider,
+ String algorithm,
+ String keyAlias)
+ throws Exception {
+
+ KeyManager[] kms = null;
+
+ KeyStore ks = getKeystore(keystoreType, keystoreProvider, config.getSslKeyStorePass());
+ if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
+ throw new IOException(sm.getString("jsse.alias_no_key_entry", keyAlias));
+ }
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+ kmf.init(ks, config.getSslKeyStorePass().toCharArray());
+
+ kms = kmf.getKeyManagers();
+ if (keyAlias != null) {
+ if ("JKS".equals(keystoreType)) {
+ keyAlias = keyAlias.toLowerCase();
+ }
+ for(int i=0; i<kms.length; i++) {
+ kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], keyAlias);
+ }
+ }
+
+ return kms;
+ }
+
+ /**
+ * Gets the intialized trust managers.
+ */
+ protected TrustManager[] getTrustManagers(String keystoreType,
+ String keystoreProvider, String algorithm)
+ throws Exception {
+ TrustManager[] tms = null;
+
+ KeyStore trustStore = getTrustStore(keystoreType, keystoreProvider);
+ if (trustStore != null) {
+ if (config.getSslCrlFile() == null) {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+ tmf.init(trustStore);
+ tms = tmf.getTrustManagers();
+ } else {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+ CertPathParameters params = getParameters(algorithm, config.getSslCrlFile(), trustStore);
+ ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params);
+ tmf.init(mfp);
+ tms = tmf.getTrustManagers();
+ }
+ }
+
+ return tms;
+ }
+
+ /**
+ * Return the initialization parameters for the TrustManager.
+ * Currently, only the default <code>PKIX</code> is supported.
+ *
+ * @param algorithm The algorithm to get parameters for.
+ * @param crlf The path to the CRL file.
+ * @param trustStore The configured TrustStore.
+ * @return The parameters including the CRLs and TrustStore.
+ */
+ protected CertPathParameters getParameters(String algorithm,
+ String crlf,
+ KeyStore trustStore)
+ throws Exception {
+ CertPathParameters params = null;
+ if("PKIX".equalsIgnoreCase(algorithm)) {
+ PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore,
+ new X509CertSelector());
+ Collection<? extends CRL> crls = getCRLs(crlf);
+ CertStoreParameters csp = new CollectionCertStoreParameters(crls);
+ CertStore store = CertStore.getInstance("Collection", csp);
+ xparams.addCertStore(store);
+ xparams.setRevocationEnabled(true);
+ xparams.setMaxPathLength(config.getSslTrustMaxCertLength());
+
+ params = xparams;
+ } else {
+ throw new CRLException("CRLs not supported for type: "+algorithm);
+ }
+ return params;
+ }
+
+
+ /**
+ * Load the collection of CRLs.
+ *
+ */
+ protected Collection<? extends CRL> getCRLs(String crlf)
+ throws IOException, CRLException, CertificateException {
+
+ File crlFile = new File(crlf);
+ if( !crlFile.isAbsolute() ) {
+ crlFile = new File(System.getProperty("catalina.base"), crlf);
+ }
+ Collection<? extends CRL> crls = null;
+ InputStream is = null;
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ is = new FileInputStream(crlFile);
+ crls = cf.generateCRLs(is);
+ } catch(IOException iex) {
+ throw iex;
+ } catch(CRLException crle) {
+ throw crle;
+ } catch(CertificateException ce) {
+ throw ce;
+ } finally {
+ if(is != null) {
+ try{
+ is.close();
+ } catch(Exception ex) {
+ }
+ }
+ }
+ return crls;
+ }
+
+ /**
+ * Set the SSL protocol variants to be enabled.
+ * @param socket the SSLServerSocket.
+ * @param protocols the protocols to use.
+ */
+ protected void setEnabledProtocols(SSLSocket socket, String []protocols){
+ if (protocols != null) {
+ socket.setEnabledProtocols(protocols);
+ }
+ }
+
+ /**
+ * Determines the SSL protocol variants to be enabled.
+ *
+ * @param socket The socket to get supported list from.
+ * @param requestedProtocols Comma-separated list of requested SSL
+ * protocol variants
+ *
+ * @return Array of SSL protocol variants to be enabled, or null if none of
+ * the requested protocol variants are supported
+ */
+ protected String[] getEnabledProtocols(SSLSocket socket,
+ String requestedProtocols){
+ String[] supportedProtocols = socket.getSupportedProtocols();
+
+ String[] enabledProtocols = null;
+
+ if (requestedProtocols != null) {
+ Vector<String> vec = null;
+ String protocol = requestedProtocols;
+ int index = requestedProtocols.indexOf(',');
+ if (index != -1) {
+ int fromIndex = 0;
+ while (index != -1) {
+ protocol = requestedProtocols.substring(fromIndex, index).trim();
+ if (protocol.length() > 0) {
+ /*
+ * Check to see if the requested protocol is among the
+ * supported protocols, i.e., may be enabled
+ */
+ for (int i=0; supportedProtocols != null
+ && i<supportedProtocols.length; i++) {
+ if (supportedProtocols[i].equals(protocol)) {
+ if (vec == null) {
+ vec = new Vector<String>();
+ }
+ vec.addElement(protocol);
+ break;
+ }
+ }
+ }
+ fromIndex = index+1;
+ index = requestedProtocols.indexOf(',', fromIndex);
+ } // while
+ protocol = requestedProtocols.substring(fromIndex);
+ }
+
+ if (protocol != null) {
+ protocol = protocol.trim();
+ if (protocol.length() > 0) {
+ /*
+ * Check to see if the requested protocol is among the
+ * supported protocols, i.e., may be enabled
+ */
+ for (int i=0; supportedProtocols != null
+ && i<supportedProtocols.length; i++) {
+ if (supportedProtocols[i].equals(protocol)) {
+ if (vec == null) {
+ vec = new Vector<String>();
+ }
+ vec.addElement(protocol);
+ break;
+ }
+ }
+ }
+ }
+
+ if (vec != null) {
+ enabledProtocols = new String[vec.size()];
+ vec.copyInto(enabledProtocols);
+ }
+ }
+
+ return enabledProtocols;
+ }
+
+ /**
+ * Configures the given SSL server socket with the requested cipher suites,
+ * protocol versions, and need for client authentication
+ */
+ private void initSocket(Socket ssocket) {
+
+ SSLSocket socket = (SSLSocket) ssocket;
+
+ if (enabledCiphers != null) {
+ socket.setEnabledCipherSuites(enabledCiphers);
+ }
+
+ setEnabledProtocols(socket, getEnabledProtocols(socket,
+ config.getSslProtocol()));
+
+ // we don't know if client auth is needed -
+ // after parsing the request we may re-handshake
+ //configureClientAuth(socket);
+ }
+
+}
More information about the jboss-cvs-commits
mailing list