# See the License for the specific language governing permissions and
# limitations under the License.
-# $Id: LocalStrings_es.properties 267129 2004-03-18 16:40:35Z jfarcand $
+# $Id$
#
# Default localized string information
# Localized para Locale es_ES
<!-- DTD for XML Schemas: Part 1: Structures
Public Identifier: "-//W3C//DTD XMLSCHEMA 200102//EN"
Official Location: http://www.w3.org/2001/XMLSchema.dtd -->
-<!-- $Rev: 46020 $ $Date: 2004-09-14 06:22:15 -0600 (Tue, 14 Sep 2004) $ -->
+<!-- $Rev$ $Date$ -->
<!-- Note this DTD is NOT normative, or even definitive. --> <!--d-->
<!-- prose copy in the structures REC is the definitive version --> <!--d-->
<!-- (which shouldn't differ from this one except for this --> <!--d-->
-->
<!--
DTD for XML Schemas: Part 2: Datatypes
- $Id: datatypes.dtd 267129 2004-03-18 16:40:35Z jfarcand $
+ $Id$
Note this DTD is NOT normative, or even definitive. - - the
prose copy in the datatypes REC is the definitive version
(which shouldn't differ from this one except for this comment
* Comment
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
- * @version $Revision: 1.17 $
+ * @version $Revision$
*/
public interface AnnotationProcessor {
public void postConstruct(Object instance)
* of an already configured authentication service.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Authenticator {
* @author Bip Thelin
* @author Remy Maucherat
* @author Filip Hanik
- * @version $Revision: 303857 $, $Date: 2005-04-15 22:15:45 +0200 (ven., 15 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public interface Cluster {
*
* @author Craig R. McClanahan
* @author Peter Donald
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Contained {
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 303037 $ $Date: 2004-07-27 09:17:21 +0200 (mar., 27 juil. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Container {
* General event for notifying listeners of significant changes on a Container.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class ContainerEvent
* LifecycleEvents, not ContainerEvents.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface ContainerListener {
* this servlet is put into service.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface ContainerServlet {
* <p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303431 $ $Date: 2004-10-26 17:42:05 +0200 (mar., 26 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Context extends Container {
* should throw <code>IllegalArgumentException</code>.
*
* @author Craig R. McClanahan
- * @version $Revision: 303092 $ $Date: 2004-08-16 11:31:09 +0200 (lun., 16 août 2004) $
+ * @version $Revision$ $Date$
*/
public interface Engine extends Container {
* Global constants that are applicable to multiple packages within Catalina.
*
* @author Craig R. McClanahan
- * @version $Revision: 303495 $ $Date: 2004-11-19 07:07:56 +0100 (ven., 19 nov. 2004) $
+ * @version $Revision$ $Date$
*/
public final class Globals {
* inherits the {@link Role}s assigned to the group.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* of Context (representing an individual servlet context).
*
* @author Craig R. McClanahan
- * @version $Revision: 303092 $ $Date: 2004-08-16 11:31:09 +0200 (lun., 16 août 2004) $
+ * @version $Revision$ $Date$
*/
public interface Host extends Container {
* as opposed to the Wrapper component that manages it.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class InstanceEvent
* is managing that instance.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface InstanceListener {
* provide a consistent mechanism to start and stop the component.
*
* @author Craig R. McClanahan
- * @version $Revision: 303352 $ $Date: 2004-10-05 19:12:52 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Lifecycle {
* Tomcat 3.x.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class LifecycleEvent
* operation of the application containing this component.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class LifecycleException extends Exception {
* Lifecycle interface.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface LifecycleListener {
* </ul>
*
* @author Craig R. McClanahan
- * @version $Revision: 303352 $ $Date: 2004-10-05 19:12:52 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Loader {
* </ul>
*
* @author Craig R. McClanahan
- * @version $Revision: 303682 $ $Date: 2005-02-07 22:56:32 +0100 (lun., 07 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public interface Manager {
*
* @author Craig R. McClanahan
* @author Peter Donald
- * @version $Revision: 302978 $ $Date: 2004-06-23 18:59:42 +0200 (mer., 23 juin 2004) $
+ * @version $Revision$ $Date$
*/
public interface Pipeline {
* Container.
*
* @author Craig R. McClanahan
- * @version $Revision: 303352 $ $Date: 2004-10-05 19:12:52 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Realm {
* environments like JAAS that want to deal with <code>Principals</code>.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* class in its constructor(s).
*
* @author Craig R. McClanahan
- * @version $Revision: 302899 $ $Date: 2004-05-26 17:29:30 +0200 (mer., 26 mai 2004) $
+ * @version $Revision$ $Date$
*/
public interface Server {
* that are built around the internal component implementation classes.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ServerFactory {
* and classes on the system class path.
*
* @author Craig R. McClanahan
- * @version $Revision: 302975 $ $Date: 2004-06-23 10:25:04 +0200 (mer., 23 juin 2004) $
+ * @version $Revision$ $Date$
*/
public interface Service {
* between requests for a particular user of a web application.
*
* @author Craig R. McClanahan
- * @version $Revision: 384817 $ $Date: 2006-03-10 16:27:43 +0100 (ven., 10 mars 2006) $
+ * @version $Revision$ $Date$
*/
public interface Session {
* General event for notifying listeners of significant changes on a Session.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class SessionEvent
* Interface defining a listener for significant Session generated events.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface SessionListener {
* server or context restarts.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Store {
* of specific {@link Role}s.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* referenced by a {@link Realm} for authentication and access control.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* @author Craig R. McClanahan
* @author Gunnar Rjnning
* @author Peter Donald
- * @version $Revision: 303352 $ $Date: 2004-10-05 19:12:52 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Valve {
* <code>IllegalArgumentException</code>.
*
* @author Craig R. McClanahan
- * @version $Revision: 303442 $ $Date: 2004-10-28 00:58:17 +0200 (jeu., 28 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Wrapper extends Container {
* undeploying applications. These tasks require Ant 1.4 or later.
*
* @author Craig R. McClanahan
- * @version $Revision: 303657 $ $Date: 2005-01-22 17:34:47 +0100 (sam., 22 janv. 2005) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
*
*
* @author Gabriele Garuglieri
- * @version $Revision: 303609 $ $Date: 2005-01-03 17:21:21 +0100 (lun., 03 janv. 2005) $
+ * @version $Revision$ $Date$
* @since 5.5
*/
* the Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 303657 $ $Date: 2005-01-22 17:34:47 +0100 (sam., 22 janv. 2005) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
public class DeployTask extends AbstractCatalinaTask {
* Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
* @deprecated Replaced by DeployTask
*/
* mod_jk status (1.2.9) application.
*
* @author Peter Rossbach
- * @version $Revision: 303886 $
+ * @version $Revision$
* @since 5.5.9
*/
public class JKStatusUpdateTask extends AbstractCatalinaTask {
* supported by the Tomcat manager application.
*
* @author Peter Rossbach
- * @version $Revision: 303880 $
+ * @version $Revision$
*/
public class JMXGetTask extends AbstractCatalinaTask {
* (<code>/jmxproxy/?qry</code>) supported by the Tomcat manager application.
*
* @author Vivek Chopra
- * @version $Revision: 303236 $
+ * @version $Revision$
*/
public class JMXQueryTask extends AbstractCatalinaTask {
* supported by the Tomcat manager application.
*
* @author Vivek Chopra
- * @version $Revision: 303236 $
+ * @version $Revision$
*/
public class JMXSetTask extends AbstractCatalinaTask {
* Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
public class ListTask extends AbstractCatalinaTask {
* Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 303657 $ $Date: 2005-01-22 17:34:47 +0100 (sam., 22 janv. 2005) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
public class ReloadTask extends AbstractCatalinaTask {
* Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @deprecated Replaced by UndeployTask
*/
public class RemoveTask extends AbstractCatalinaTask {
* the Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
public class ResourcesTask extends AbstractCatalinaTask {
* Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
public class RolesTask extends AbstractCatalinaTask {
* supported by the Tomcat manager application.
*
* @author Vivek Chopra
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ServerinfoTask extends AbstractCatalinaTask {
* supported by the Tomcat manager application.
*
* @author Vivek Chopra
- * @version $Revision: 303657 $
+ * @version $Revision$
*/
public class SessionsTask extends AbstractCatalinaTask {
* Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 303657 $ $Date: 2005-01-22 17:34:47 +0100 (sam., 22 janv. 2005) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
public class StartTask extends AbstractCatalinaTask {
* Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 303657 $ $Date: 2005-01-22 17:34:47 +0100 (sam., 22 janv. 2005) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
public class StopTask extends AbstractCatalinaTask {
* the Tomcat manager application.
*
* @author Craig R. McClanahan
- * @version $Revision: 303609 $ $Date: 2005-01-03 17:21:21 +0100 (lun., 03 janv. 2005) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
public class UndeployTask extends AbstractCatalinaTask {
* schema validation.
*
* @author Remy Maucherat
- * @version $Revision: 303609 $ $Date: 2005-01-03 17:21:21 +0100 (lun., 03 janv. 2005) $
+ * @version $Revision$ $Date$
* @since 5.0
*/
-# Pure catalina tasks\r
-deploy=org.apache.catalina.ant.DeployTask\r
-list=org.apache.catalina.ant.ListTask\r
-reload=org.apache.catalina.ant.ReloadTask\r
-sessions=org.apache.catalina.ant.SessionsTask\r
-resources=org.apache.catalina.ant.ResourcesTask\r
-roles=org.apache.catalina.ant.RolesTask\r
-start=org.apache.catalina.ant.StartTask\r
-stop=org.apache.catalina.ant.StopTask\r
-undeploy=org.apache.catalina.ant.UndeployTask\r
-validator=org.apache.catalina.ant.ValidatorTask\r
-\r
-#Jk Task\r
-jkstatus=org.apache.catalina.ant.JKStatusUpdateTask\r
-\r
-# Manager JMX\r
-jmxManagerSet=org.apache.catalina.ant.JMXSetTask\r
-jmxManagerGet=org.apache.catalina.ant.JMXGetTask\r
-jmxManagerQuery=org.apache.catalina.ant.JMXQueryTask\r
-\r
-# Jasper tasks\r
-jasper=org.apache.jasper.JspC\r
-jasper2=org.apache.jasper.JspC\r
+# Pure catalina tasks
+deploy=org.apache.catalina.ant.DeployTask
+list=org.apache.catalina.ant.ListTask
+reload=org.apache.catalina.ant.ReloadTask
+sessions=org.apache.catalina.ant.SessionsTask
+resources=org.apache.catalina.ant.ResourcesTask
+roles=org.apache.catalina.ant.RolesTask
+start=org.apache.catalina.ant.StartTask
+stop=org.apache.catalina.ant.StopTask
+undeploy=org.apache.catalina.ant.UndeployTask
+validator=org.apache.catalina.ant.ValidatorTask
+
+#Jk Task
+jkstatus=org.apache.catalina.ant.JKStatusUpdateTask
+
+# Manager JMX
+jmxManagerSet=org.apache.catalina.ant.JMXSetTask
+jmxManagerGet=org.apache.catalina.ant.JMXGetTask
+jmxManagerQuery=org.apache.catalina.ant.JMXQueryTask
+
+# Jasper tasks
+jasper=org.apache.jasper.JspC
+jasper2=org.apache.jasper.JspC
/**
*
* @author Peter Rossbach
- * @version $Revision: 303958 $ $Date: 2005-06-24 13:53:42 +0200 (ven., 24 juin 2005) $
+ * @version $Revision$ $Date$
* @since 5.5.10
*/
public class Arg {
* <b>NOTE</b>: For numeric expressions the type must be set and use xml entities as operations.<br/>
* As type we currently support <em>long</em> and <em>double</em>.
* @author Peter Rossbach
- * @version $Revision: 304032 $ $Date: 2005-07-27 17:11:55 +0200 (mer., 27 juil. 2005) $
+ * @version $Revision$ $Date$
* @since 5.5.10
*
*/
* These tasks require Ant 1.6 or later interface.
*
* @author Peter Rossbach
- * @version $Revision: 304089 $
+ * @version $Revision$
* @since 5.5.12
*/
public class JMXAccessorCreateTask extends JMXAccessorTask {
* </pre>
*
* @author Peter Rossbach
- * @version $Revision: 304032 $ $Date: 2005-07-27 17:11:55 +0200 (mer., 27 juil. 2005) $
+ * @version $Revision$ $Date$
* @since 5.5.10
*
*/
* These tasks require Ant 1.6 or later interface.
*
* @author Peter Rossbach
- * @version $Revision: 304032 $ $Date: 2005-07-27 17:11:55 +0200 (mer., 27 juil. 2005) $
+ * @version $Revision$ $Date$
* @since 5.5.10
*/
* These tasks require Ant 1.6 or later interface.
*
* @author Peter Rossbach
- * @version $Revision: 304013 $ $Date: 2005-07-22 13:39:08 +0200 (ven., 22 juil. 2005) $
+ * @version $Revision$ $Date$
* @since 5.5.10
*/
* These tasks require Ant 1.6 or later interface.
*
* @author Peter Rossbach
- * @version $Revision: 304089 $ $Date: 2005-09-14 15:28:29 +0200 (mer., 14 sept. 2005) $
+ * @version $Revision$ $Date$
* @since 5.5.10
*/
* These tasks require Ant 1.6 or later interface.
*
* @author Peter Rossbach
- * @version $Revision: 304089 $ $Date: 2005-09-14 15:28:29 +0200 (mer., 14 sept. 2005) $
+ * @version $Revision$ $Date$
* @since 5.5.10
*/
* </b>: These tasks require Ant 1.6 or later interface.
*
* @author Peter Rossbach
- * @version $Revision: 304089 $ $Date: 2005-09-14 15:28:29 +0200 (mer., 14 sept. 2005) $
+ * @version $Revision$ $Date$
* @since 5.5.10
*/
* These tasks require Ant 1.6 or later interface.
*
* @author Peter Rossbach
- * @version $Revision: 304089 $
+ * @version $Revision$
* @since 5.5.12
*/
public class JMXAccessorUnregisterTask extends JMXAccessorTask {
-# JMX\r
-jmxOpen=org.apache.catalina.ant.jmx.JMXAccessorTask\r
-jmxSet=org.apache.catalina.ant.jmx.JMXAccessorSetTask\r
-jmxGet=org.apache.catalina.ant.jmx.JMXAccessorGetTask\r
-jmxInvoke=org.apache.catalina.ant.jmx.JMXAccessorInvokeTask\r
-jmxQuery=org.apache.catalina.ant.jmx.JMXAccessorQueryTask\r
-jmxCreate=org.apache.catalina.ant.jmx.JMXAccessorCreateTask\r
-jmxUnregister=org.apache.catalina.ant.jmx.JMXAccessorUnregisterTask\r
+# JMX
+jmxOpen=org.apache.catalina.ant.jmx.JMXAccessorTask
+jmxSet=org.apache.catalina.ant.jmx.JMXAccessorSetTask
+jmxGet=org.apache.catalina.ant.jmx.JMXAccessorGetTask
+jmxInvoke=org.apache.catalina.ant.jmx.JMXAccessorInvokeTask
+jmxQuery=org.apache.catalina.ant.jmx.JMXAccessorQueryTask
+jmxCreate=org.apache.catalina.ant.jmx.JMXAccessorCreateTask
+jmxUnregister=org.apache.catalina.ant.jmx.JMXAccessorUnregisterTask
* requests. Requests of any other type will simply be passed through.
*
* @author Craig R. McClanahan
- * @version $Revision: 357143 $ $Date: 2005-12-16 09:13:19 +0100 (ven., 16 déc. 2005) $
+ * @version $Revision$ $Date$
*/
* and Digest Access Authentication."
*
* @author Craig R. McClanahan
- * @version $Revision: 370985 $ $Date: 2006-01-21 06:21:15 +0100 (sam., 21 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class BasicAuthenticator
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 370985 $ $Date: 2006-01-21 06:21:15 +0100 (sam., 21 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class DigestAuthenticator
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 320670 $ $Date: 2005-10-13 07:39:55 +0200 (jeu., 13 oct. 2005) $
+ * @version $Revision$ $Date$
*/
public class FormAuthenticator
* only security constraints not involving user authentication.
*
* @author Craig R. McClanahan
- * @version $Revision: 303721 $ $Date: 2005-02-23 20:27:56 +0100 (mer., 23 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public final class NonLoginAuthenticator
* that utilizes SSL certificates to identify client users.
*
* @author Craig R. McClanahan
- * @version $Revision: 303721 $ $Date: 2005-02-23 20:27:56 +0100 (mer., 23 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public class SSLAuthenticator
* internal collection classes is performed.
*
* @author Craig R. McClanahan
- * @version $Revision: 303925 $ $Date: 2005-05-11 23:39:41 +0200 (mer., 11 mai 2005) $
+ * @version $Revision$ $Date$
*/
public final class SavedRequest {
* </ul>
*
* @author Craig R. McClanahan
- * @version $Revision: 384817 $ $Date: 2006-03-10 16:27:43 +0100 (ven., 10 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class SingleSignOn
* reauthentications when SingleSignOn is in use.
*
* @author B Stansberry, based on work by Craig R. McClanahan
- * @version $Revision: 302885 $
+ * @version $Revision$
*
* @see SingleSignOn
* @see AuthenticatorBase#reauthenticateFromSSO
* of a request by a remote client.
*
* @author Glenn L. Nielsen
- * @version $Revision: 304063 $ $Date: 2005-08-18 15:25:18 +0200 (jeu., 18 août 2005) $
+ * @version $Revision$ $Date$
*/
public final class ClientAbortException extends IOException {
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 349505 $ $Date: 2005-11-28 22:14:21 +0100 (lun., 28 nov. 2005) $
+ * @version $Revision$ $Date$
*/
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 331249 $ $Date: 2005-11-07 10:57:55 +0100 (lun., 07 nov. 2005) $
+ * @version $Revision$ $Date$
*/
public class CoyoteAdapter
* is used to represent principals authenticated at the protocol handler level.
*
* @author Remy Maucherat
- * @version $Revision: 302975 $ $Date: 2004-06-23 10:25:04 +0200 (mer., 23 juin 2004) $
+ * @version $Revision$ $Date$
*/
public class CoyotePrincipal
*
* @author Remy Maucherat
* @author Craig R. McClanahan
- * @version $Revision: 382659 $ $Date: 2006-03-03 06:01:16 +0100 (ven., 03 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class Request
* @author Craig R. McClanahan
* @author Remy Maucherat
* @author Jean-Francois Arcand
- * @version $Revision: 303900 $ $Date: 2005-04-30 00:22:29 +0200 (sam., 30 avr. 2005) $
+ * @version $Revision$ $Date$
*/
@SuppressWarnings("deprecation")
*
* @author Remy Maucherat
* @author Craig R. McClanahan
- * @version $Revision: 371866 $ $Date: 2006-01-24 09:52:54 +0100 (mar., 24 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class Response
*
* @author Remy Maucherat
* @author Jean-Francois Arcand
- * @version $Revision: 303900 $ $Date: 2005-04-30 00:22:29 +0200 (sam., 30 avr. 2005) $
+ * @version $Revision$ $Date$
*/
@SuppressWarnings("deprecation")
public class ResponseFacade
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 377994 $ $Date: 2006-02-15 13:37:28 +0100 (mer., 15 févr. 2006) $
+ * @version $Revision$ $Date$
*/
public class ApplicationContext
*
* @author Remy Maucherat
* @author Jean-Francois Arcand
- * @version $Revision: 377994 $ $Date: 2006-02-15 13:37:28 +0100 (mer., 15 févr. 2006) $
+ * @version $Revision$ $Date$
*/
public final class ApplicationContextFacade
* <code>javax.servlet.ServletResponseWrapper</code>.
*
* @author Craig R. McClanahan
- * @version $Revision: 303947 $ $Date: 2005-06-09 07:50:26 +0200 (jeu., 09 juin 2005) $
+ * @version $Revision$ $Date$
*/
final class ApplicationDispatcher
* method itself.
*
* @author Craig R. McClanahan
- * @version $Revision: 303523 $ $Date: 2004-11-22 17:35:18 +0100 (lun., 22 nov. 2004) $
+ * @version $Revision$ $Date$
*/
final class ApplicationFilterChain implements FilterChain, CometFilterChain {
* is first started.
*
* @author Craig R. McClanahan
- * @version $Revision: 355530 $ $Date: 2005-12-09 17:42:23 +0100 (ven., 09 déc. 2005) $
+ * @version $Revision$ $Date$
*/
final class ApplicationFilterConfig implements FilterConfig, Serializable {
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 303799 $ $Date: 2005-03-25 09:41:23 +0100 (ven., 25 mars 2005) $
+ * @version $Revision$ $Date$
*/
class ApplicationHttpRequest extends HttpServletRequestWrapper {
* keep these two classes in synchronization when making changes!
*
* @author Craig R. McClanahan
- * @version $Revision: 303387 $ $Date: 2004-10-15 18:09:27 +0200 (ven., 15 oct. 2004) $
+ * @version $Revision$ $Date$
*/
class ApplicationHttpResponse extends HttpServletResponseWrapper {
* keep these two classes in synchronization when making changes!
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
class ApplicationRequest extends ServletRequestWrapper {
* keep these two classes in synchronization when making changes!
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
class ApplicationResponse extends ServletResponseWrapper {
*
* @author Remy Maucherat
* @author Filip Hanik
- * @version $Revision: 441786 $ $Date: 2006-09-09 14:26:11 +0200 (sam., 09 sept. 2006) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* JSP precompilation.
*
* @author Remy Maucherat
- * @version $Revision: 302975 $ $Date: 2004-06-23 10:25:04 +0200 (mer., 23 juin 2004) $
+ * @version $Revision$ $Date$
*/
public class DummyRequest
* Dummy response object, used for JSP precompilation.
*
* @author Remy Maucherat
- * @version $Revision: 302975 $ $Date: 2004-06-23 10:25:04 +0200 (mer., 23 juin 2004) $
+ * @version $Revision$ $Date$
*/
public class DummyResponse
* started.
*
* @author Remy Maucherat
- * @version $Revision: 374878 $ $Date: 2006-02-04 16:02:39 +0100 (sam., 04 févr. 2006) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* with each context and server.
*
* @author Remy Maucherat
- * @version $Revision: 304032 $ $Date: 2005-07-27 17:11:55 +0200 (mer., 27 juil. 2005) $
+ * @version $Revision$ $Date$
*/
public class NamingContextListener
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 386331 $ $Date: 2006-03-16 15:03:12 +0100 (jeu., 16 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class StandardContext
* when processing HTTP requests.
*
* @author Craig R. McClanahan
- * @version $Revision: 302978 $ $Date: 2004-06-23 18:59:42 +0200 (mer., 23 juin 2004) $
+ * @version $Revision$ $Date$
*/
final class StandardContextValve
* You can set the jvmRoute direct or with the System.property <b>jvmRoute</b>.
*
* @author Craig R. McClanahan
- * @version $Revision: 303667 $ $Date: 2005-01-29 20:41:16 +0100 (sam., 29 janv. 2005) $
+ * @version $Revision$ $Date$
*/
public class StandardEngine
* when processing HTTP requests.
*
* @author Craig R. McClanahan
- * @version $Revision: 302978 $ $Date: 2004-06-23 18:59:42 +0200 (mer., 23 juin 2004) $
+ * @version $Revision$ $Date$
*/
final class StandardEngineValve
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 303666 $ $Date: 2005-01-29 20:38:37 +0100 (sam., 29 janv. 2005) $
+ * @version $Revision$ $Date$
*/
public class StandardHost
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 304105 $ $Date: 2005-09-27 21:20:54 +0200 (mar., 27 sept. 2005) $
+ * @version $Revision$ $Date$
*/
final class StandardHostValve
* (but not required) when deploying and starting Catalina.
*
* @author Craig R. McClanahan
- * @version $Revision: 370082 $ $Date: 2006-01-18 10:17:33 +0100 (mer., 18 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public final class StandardServer
implements Lifecycle, Server, MBeanRegistration
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 345286 $ $Date: 2005-11-17 18:00:24 +0100 (jeu., 17 nov. 2005) $
+ * @version $Revision$ $Date$
*/
public class StandardWrapper
extends ContainerBase
* Facade for the <b>StandardWrapper</b> object.
*
* @author Remy Maucharat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class StandardWrapperFacade
* <code>StandardWrapper</code> container implementation.
*
* @author Craig R. McClanahan
- * @version $Revision: 303681 $ $Date: 2005-02-06 11:39:32 +0100 (dim., 06 févr. 2005) $
+ * @version $Revision$ $Date$
*/
final class StandardWrapperValve
* to modify the application deployment descriptor itself.
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
*/
public class ApplicationParameter implements Serializable {
*
* @author Craig R. McClanahan
* @author Peter Rossbach (pero@apache.org)
- * @version $Revision: 303342 $ $Date: 2004-10-05 09:56:49 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextEjb extends ResourceBase implements Serializable {
* an <code><env-entry></code> element in the deployment descriptor.
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextEnvironment implements Serializable {
*
* @author Craig R. McClanahan
* @author Peter Rossbach (pero@apache.org)
- * @version $Revision: 303342 $ $Date: 2004-10-05 09:56:49 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextLocalEjb extends ResourceBase implements Serializable {
*
* @author Craig R. McClanahan
* @author Peter Rossbach (pero@apache.org)
- * @version $Revision: 303342 $ $Date: 2004-10-05 09:56:49 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextResource extends ResourceBase implements Serializable {
*
* @author Craig R. McClanahan
* @author Peter Rossbach (pero@apache.org)
- * @version $Revision: 303342 $ $Date: 2004-10-05 09:56:49 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextResourceEnvRef extends ResourceBase implements Serializable {
*
* @author Remy Maucherat
* @author Peter Rossbach (Peter Rossbach (pero@apache.org))
- * @version $Revision: 303342 $ $Date: 2004-10-05 09:56:49 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextResourceLink extends ResourceBase implements Serializable {
* deployment descriptor.
*
* @author Fabien Carrion
- * @version $Revision: 303342 $ $Date: 2005-03-15 23:29:49 -0700 (Web, 15 Mar 2006) $
+ * @version $Revision$ $Date$
*/
public class ContextService extends ResourceBase implements Serializable {
* an <code><res-env-refy></code> element in the deployment descriptor.
*
* @author Craig R. McClanahan
- * @version $Revision: 303032 $ $Date: 2004-07-26 18:04:02 +0200 (lun., 26 juil. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextTransaction implements Serializable {
* deployment descriptor.
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
*/
public class ErrorPage implements Serializable {
* in a <code><filter></code> element in the deployment descriptor.
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
*/
public class FilterDef implements Serializable {
* a URL pattern or a servlet name.
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
*/
public class FilterMap implements Serializable {
* deployment descriptor.
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
*/
public class LoginConfig implements Serializable {
* in the deployment descriptor.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
* @since Tomcat 5.0
*/
* in the deployment descriptor.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
* @since Tomcat 5.0
*/
* Naming Context and their associated JNDI context.
*
* @author Remy Maucherat
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public class NamingResources implements Serializable {
* Representation of an Context element
*
* @author Peter Rossbach (pero@apache.org)
- * @version $Revision: 303342 $ $Date: 2004-10-05 09:56:49 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class ResourceBase implements Serializable {
* this class is synchronized.
*
* @author Craig R. McClanahan
- * @version $Revision: 304007 $ $Date: 2005-07-21 22:14:57 +0200 (jeu., 21 juil. 2005) $
+ * @version $Revision$ $Date$
*/
public class SecurityCollection implements Serializable {
* this class is synchronized.
*
* @author Craig R. McClanahan
- * @version $Revision: 302879 $ $Date: 2004-05-13 22:40:49 +0200 (jeu., 13 mai 2004) $
+ * @version $Revision$ $Date$
*/
public class SecurityConstraint implements Serializable {
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha;\r
-\r
-import java.util.Map;\r
-\r
-import org.apache.catalina.Cluster;\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.Manager;\r
-import org.apache.catalina.Valve;\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.juli.logging.Log;\r
-\r
-\r
-\r
-/**\r
- * A <b>CatalinaCluster</b> interface allows to plug in and out the \r
- * different cluster implementations\r
- *\r
- * @author Filip Hanik\r
- * @version $Revision: 379550 $, $Date: 2006-02-21 12:06:35 -0600 (Tue, 21 Feb 2006) $\r
- */\r
-\r
-public interface CatalinaCluster extends Cluster {\r
- // ----------------------------------------------------- Instance Variables\r
-\r
- /**\r
- * Descriptive information about this component implementation.\r
- */\r
- public String info = "CatalinaCluster/2.0";\r
- \r
- /**\r
- * Start the cluster, the owning container will invoke this\r
- * @throws Exception - if failure to start cluster\r
- */\r
- public void start() throws Exception;\r
- \r
- /**\r
- * Stops the cluster, the owning container will invoke this\r
- * @throws LifecycleException\r
- */\r
- public void stop() throws LifecycleException;\r
- \r
- /**\r
- * Returns the associates logger with this cluster.\r
- *\r
- * @return Log\r
- */\r
- public Log getLogger();\r
- \r
- /**\r
- * Sends a message to all the members in the cluster\r
- * @param msg ClusterMessage\r
- */\r
- public void send(ClusterMessage msg);\r
- \r
- /**\r
- * Sends a message to a specific member in the cluster.\r
- *\r
- * @param msg ClusterMessage\r
- * @param dest Member\r
- */\r
- public void send(ClusterMessage msg, Member dest);\r
- \r
- /**\r
- * Sends a message to a all members at local cluster domain\r
- *\r
- * @param msg ClusterMessage\r
- */\r
- public void sendClusterDomain(ClusterMessage msg);\r
-\r
- /**\r
- * Returns that cluster has members.\r
- */\r
- public boolean hasMembers();\r
-\r
- /**\r
- * Returns all the members currently participating in the cluster.\r
- *\r
- * @return Member[]\r
- */\r
- public Member[] getMembers();\r
- \r
- /**\r
- * Return the member that represents this node.\r
- *\r
- * @return Member\r
- */\r
- public Member getLocalMember();\r
- \r
- public void addValve(Valve valve);\r
- \r
- public void addClusterListener(ClusterListener listener);\r
- \r
- public void removeClusterListener(ClusterListener listener);\r
- \r
- public void setClusterDeployer(ClusterDeployer deployer);\r
- \r
- public ClusterDeployer getClusterDeployer();\r
- \r
- /**\r
- * @return The map of managers\r
- */\r
- public Map getManagers();\r
-\r
- public Manager getManager(String name);\r
- public String getManagerName(String name, Manager manager);\r
- public Valve[] getValves();\r
- \r
- public void setChannel(Channel channel);\r
- public Channel getChannel();\r
- \r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+import java.util.Map;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Valve;
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.Member;
+import org.apache.juli.logging.Log;
+
+
+
+/**
+ * A <b>CatalinaCluster</b> interface allows to plug in and out the
+ * different cluster implementations
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+public interface CatalinaCluster extends Cluster {
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * Descriptive information about this component implementation.
+ */
+ public String info = "CatalinaCluster/2.0";
+
+ /**
+ * Start the cluster, the owning container will invoke this
+ * @throws Exception - if failure to start cluster
+ */
+ public void start() throws Exception;
+
+ /**
+ * Stops the cluster, the owning container will invoke this
+ * @throws LifecycleException
+ */
+ public void stop() throws LifecycleException;
+
+ /**
+ * Returns the associates logger with this cluster.
+ *
+ * @return Log
+ */
+ public Log getLogger();
+
+ /**
+ * Sends a message to all the members in the cluster
+ * @param msg ClusterMessage
+ */
+ public void send(ClusterMessage msg);
+
+ /**
+ * Sends a message to a specific member in the cluster.
+ *
+ * @param msg ClusterMessage
+ * @param dest Member
+ */
+ public void send(ClusterMessage msg, Member dest);
+
+ /**
+ * Sends a message to a all members at local cluster domain
+ *
+ * @param msg ClusterMessage
+ */
+ public void sendClusterDomain(ClusterMessage msg);
+
+ /**
+ * Returns that cluster has members.
+ */
+ public boolean hasMembers();
+
+ /**
+ * Returns all the members currently participating in the cluster.
+ *
+ * @return Member[]
+ */
+ public Member[] getMembers();
+
+ /**
+ * Return the member that represents this node.
+ *
+ * @return Member
+ */
+ public Member getLocalMember();
+
+ public void addValve(Valve valve);
+
+ public void addClusterListener(ClusterListener listener);
+
+ public void removeClusterListener(ClusterListener listener);
+
+ public void setClusterDeployer(ClusterDeployer deployer);
+
+ public ClusterDeployer getClusterDeployer();
+
+ /**
+ * @return The map of managers
+ */
+ public Map getManagers();
+
+ public Manager getManager(String name);
+ public String getManagerName(String name, Manager manager);
+ public Valve[] getValves();
+
+ public void setChannel(Channel channel);
+ public Channel getChannel();
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha;\r
-\r
-/**\r
- * A <b>ClusterDeployer</b> interface allows to plug in and out the\r
- * different deployment implementations\r
- *\r
- * @author Filip Hanik\r
- * @version $Revision: 378050 $, $Date: 2006-02-15 12:30:02 -0600 (Wed, 15 Feb 2006) $\r
- */\r
-import org.apache.catalina.LifecycleException;\r
-import java.io.IOException;\r
-import java.net.URL;\r
-import org.apache.catalina.tribes.ChannelListener;\r
-\r
-public interface ClusterDeployer extends ChannelListener {\r
- /**\r
- * Descriptive information about this component implementation.\r
- */\r
- public String info = "ClusterDeployer/1.0";\r
- /**\r
- * Start the cluster deployer, the owning container will invoke this\r
- * @throws Exception - if failure to start cluster\r
- */\r
- public void start() throws Exception;\r
-\r
- /**\r
- * Stops the cluster deployer, the owning container will invoke this\r
- * @throws LifecycleException\r
- */\r
- public void stop() throws LifecycleException;\r
-\r
- /**\r
- * Sets the deployer for this cluster deployer to use.\r
- * @param deployer Deployer\r
- */\r
- // FIXME\r
- //public void setDeployer(Deployer deployer);\r
-\r
- /**\r
- * Install a new web application, whose web application archive is at the\r
- * specified URL, into this container and all the other\r
- * members of the cluster with the specified context path.\r
- * A context path of "" (the empty string) should be used for the root\r
- * application for this container. Otherwise, the context path must\r
- * start with a slash.\r
- * <p>\r
- * If this application is successfully installed locally, \r
- * a ContainerEvent of type\r
- * <code>INSTALL_EVENT</code> will be sent to all registered listeners,\r
- * with the newly created <code>Context</code> as an argument.\r
- *\r
- * @param contextPath The context path to which this application should\r
- * be installed (must be unique)\r
- * @param war A URL of type "jar:" that points to a WAR file, or type\r
- * "file:" that points to an unpacked directory structure containing\r
- * the web application to be installed\r
- *\r
- * @exception IllegalArgumentException if the specified context path\r
- * is malformed (it must be "" or start with a slash)\r
- * @exception IllegalStateException if the specified context path\r
- * is already attached to an existing web application\r
- * @exception IOException if an input/output error was encountered\r
- * during installation\r
- */\r
- public void install(String contextPath, URL war) throws IOException;\r
-\r
- /**\r
- * Remove an existing web application, attached to the specified context\r
- * path. If this application is successfully removed, a\r
- * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all\r
- * registered listeners, with the removed <code>Context</code> as\r
- * an argument. Deletes the web application war file and/or directory\r
- * if they exist in the Host's appBase.\r
- *\r
- * @param contextPath The context path of the application to be removed\r
- * @param undeploy boolean flag to remove web application from server\r
- *\r
- * @exception IllegalArgumentException if the specified context path\r
- * is malformed (it must be "" or start with a slash)\r
- * @exception IllegalArgumentException if the specified context path does\r
- * not identify a currently installed web application\r
- * @exception IOException if an input/output error occurs during\r
- * removal\r
- */\r
- public void remove(String contextPath, boolean undeploy) throws IOException;\r
-\r
- /**\r
- * call from container Background Process\r
- */\r
- public void backgroundProcess();\r
- \r
- /**\r
- * Returns the cluster the cluster deployer is associated with\r
- * @return CatalinaCluster\r
- */\r
- public CatalinaCluster getCluster();\r
-\r
- /**\r
- * Associates the cluster deployer with a cluster\r
- * @param cluster CatalinaCluster\r
- */\r
- public void setCluster(CatalinaCluster cluster);\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+/**
+ * A <b>ClusterDeployer</b> interface allows to plug in and out the
+ * different deployment implementations
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+import org.apache.catalina.LifecycleException;
+import java.io.IOException;
+import java.net.URL;
+import org.apache.catalina.tribes.ChannelListener;
+
+public interface ClusterDeployer extends ChannelListener {
+ /**
+ * Descriptive information about this component implementation.
+ */
+ public String info = "ClusterDeployer/1.0";
+ /**
+ * Start the cluster deployer, the owning container will invoke this
+ * @throws Exception - if failure to start cluster
+ */
+ public void start() throws Exception;
+
+ /**
+ * Stops the cluster deployer, the owning container will invoke this
+ * @throws LifecycleException
+ */
+ public void stop() throws LifecycleException;
+
+ /**
+ * Sets the deployer for this cluster deployer to use.
+ * @param deployer Deployer
+ */
+ // FIXME
+ //public void setDeployer(Deployer deployer);
+
+ /**
+ * Install a new web application, whose web application archive is at the
+ * specified URL, into this container and all the other
+ * members of the cluster with the specified context path.
+ * A context path of "" (the empty string) should be used for the root
+ * application for this container. Otherwise, the context path must
+ * start with a slash.
+ * <p>
+ * If this application is successfully installed locally,
+ * a ContainerEvent of type
+ * <code>INSTALL_EVENT</code> will be sent to all registered listeners,
+ * with the newly created <code>Context</code> as an argument.
+ *
+ * @param contextPath The context path to which this application should
+ * be installed (must be unique)
+ * @param war A URL of type "jar:" that points to a WAR file, or type
+ * "file:" that points to an unpacked directory structure containing
+ * the web application to be installed
+ *
+ * @exception IllegalArgumentException if the specified context path
+ * is malformed (it must be "" or start with a slash)
+ * @exception IllegalStateException if the specified context path
+ * is already attached to an existing web application
+ * @exception IOException if an input/output error was encountered
+ * during installation
+ */
+ public void install(String contextPath, URL war) throws IOException;
+
+ /**
+ * Remove an existing web application, attached to the specified context
+ * path. If this application is successfully removed, a
+ * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+ * registered listeners, with the removed <code>Context</code> as
+ * an argument. Deletes the web application war file and/or directory
+ * if they exist in the Host's appBase.
+ *
+ * @param contextPath The context path of the application to be removed
+ * @param undeploy boolean flag to remove web application from server
+ *
+ * @exception IllegalArgumentException if the specified context path
+ * is malformed (it must be "" or start with a slash)
+ * @exception IllegalArgumentException if the specified context path does
+ * not identify a currently installed web application
+ * @exception IOException if an input/output error occurs during
+ * removal
+ */
+ public void remove(String contextPath, boolean undeploy) throws IOException;
+
+ /**
+ * call from container Background Process
+ */
+ public void backgroundProcess();
+
+ /**
+ * Returns the cluster the cluster deployer is associated with
+ * @return CatalinaCluster
+ */
+ public CatalinaCluster getCluster();
+
+ /**
+ * Associates the cluster deployer with a cluster
+ * @param cluster CatalinaCluster
+ */
+ public void setCluster(CatalinaCluster cluster);
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha;\r
-\r
-\r
-\r
-\r
-import java.io.Serializable;\r
-\r
-import org.apache.catalina.tribes.ChannelListener;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.util.StringManager;\r
-\r
-\r
-/**\r
- * Receive SessionID cluster change from other backup node after primary session\r
- * node is failed.\r
- * \r
- * @author Peter Rossbach\r
- * @author Filip Hanik\r
- * @version $Revision: 378258 $ $Date: 2006-02-16 08:42:35 -0600 (Thu, 16 Feb 2006) $\r
- */\r
-public abstract class ClusterListener implements ChannelListener {\r
-\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ClusterListener.class);\r
-\r
-\r
- //--Instance Variables--------------------------------------\r
-\r
- /**\r
- * The string manager for this package.\r
- */\r
- protected StringManager sm = StringManager.getManager(Constants.Package);\r
-\r
- protected CatalinaCluster cluster = null;\r
-\r
- //--Constructor---------------------------------------------\r
-\r
- public ClusterListener() {\r
- }\r
- \r
- //--Instance Getters/Setters--------------------------------\r
- \r
- public CatalinaCluster getCluster() {\r
- return cluster;\r
- }\r
-\r
- public void setCluster(CatalinaCluster cluster) {\r
- if (log.isDebugEnabled()) {\r
- if (cluster != null)\r
- log.debug("add ClusterListener " + this.toString() + " to cluster" + cluster);\r
- else\r
- log.debug("remove ClusterListener " + this.toString() + " from cluster");\r
- }\r
- this.cluster = cluster;\r
- }\r
-\r
- public boolean equals(Object listener) {\r
- return super.equals(listener);\r
- }\r
-\r
- public int hashCode() {\r
- return super.hashCode();\r
- }\r
-\r
- //--Logic---------------------------------------------------\r
-\r
- public final void messageReceived(Serializable msg, Member member) {\r
- if ( msg instanceof ClusterMessage ) messageReceived((ClusterMessage)msg);\r
- }\r
- public final boolean accept(Serializable msg, Member member) {\r
- if ( msg instanceof ClusterMessage ) return true;\r
- return false;\r
- }\r
-\r
-\r
-\r
- /**\r
- * Callback from the cluster, when a message is received, The cluster will\r
- * broadcast it invoking the messageReceived on the receiver.\r
- * \r
- * @param msg\r
- * ClusterMessage - the message received from the cluster\r
- */\r
- public abstract void messageReceived(ClusterMessage msg) ;\r
- \r
-\r
- /**\r
- * Accept only SessionIDMessages\r
- * \r
- * @param msg\r
- * ClusterMessage\r
- * @return boolean - returns true to indicate that messageReceived should be\r
- * invoked. If false is returned, the messageReceived method will\r
- * not be invoked.\r
- */\r
- public abstract boolean accept(ClusterMessage msg) ;\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+
+
+
+import java.io.Serializable;
+
+import org.apache.catalina.tribes.ChannelListener;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Receive SessionID cluster change from other backup node after primary session
+ * node is failed.
+ *
+ * @author Peter Rossbach
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+public abstract class ClusterListener implements ChannelListener {
+
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ClusterListener.class);
+
+
+ //--Instance Variables--------------------------------------
+
+ /**
+ * The string manager for this package.
+ */
+ protected StringManager sm = StringManager.getManager(Constants.Package);
+
+ protected CatalinaCluster cluster = null;
+
+ //--Constructor---------------------------------------------
+
+ public ClusterListener() {
+ }
+
+ //--Instance Getters/Setters--------------------------------
+
+ public CatalinaCluster getCluster() {
+ return cluster;
+ }
+
+ public void setCluster(CatalinaCluster cluster) {
+ if (log.isDebugEnabled()) {
+ if (cluster != null)
+ log.debug("add ClusterListener " + this.toString() + " to cluster" + cluster);
+ else
+ log.debug("remove ClusterListener " + this.toString() + " from cluster");
+ }
+ this.cluster = cluster;
+ }
+
+ public boolean equals(Object listener) {
+ return super.equals(listener);
+ }
+
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ //--Logic---------------------------------------------------
+
+ public final void messageReceived(Serializable msg, Member member) {
+ if ( msg instanceof ClusterMessage ) messageReceived((ClusterMessage)msg);
+ }
+ public final boolean accept(Serializable msg, Member member) {
+ if ( msg instanceof ClusterMessage ) return true;
+ return false;
+ }
+
+
+
+ /**
+ * Callback from the cluster, when a message is received, The cluster will
+ * broadcast it invoking the messageReceived on the receiver.
+ *
+ * @param msg
+ * ClusterMessage - the message received from the cluster
+ */
+ public abstract void messageReceived(ClusterMessage msg) ;
+
+
+ /**
+ * Accept only SessionIDMessages
+ *
+ * @param msg
+ * ClusterMessage
+ * @return boolean - returns true to indicate that messageReceived should be
+ * invoked. If false is returned, the messageReceived method will
+ * not be invoked.
+ */
+ public abstract boolean accept(ClusterMessage msg) ;
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha;\r
-\r
-\r
-import org.apache.catalina.Manager;\r
-import java.io.IOException;\r
-import org.apache.catalina.tribes.io.ReplicationStream;\r
-\r
-\r
-/**\r
- * The common interface used by all cluster manager.\r
- * This is so that we can have a more pluggable way\r
- * of swapping session managers for different algorithms.\r
- *\r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- */\r
-public interface ClusterManager extends Manager {\r
-\r
- /**\r
- * A message was received from another node, this\r
- * is the callback method to implement if you are interested in\r
- * receiving replication messages.\r
- * @param msg - the message received.\r
- */\r
- public void messageDataReceived(ClusterMessage msg);\r
-\r
- /**\r
- * When the request has been completed, the replication valve\r
- * will notify the manager, and the manager will decide whether\r
- * any replication is needed or not.\r
- * If there is a need for replication, the manager will\r
- * create a session message and that will be replicated.\r
- * The cluster determines where it gets sent.\r
- * @param sessionId - the sessionId that just completed.\r
- * @return a SessionMessage to be sent.\r
- */\r
- public ClusterMessage requestCompleted(String sessionId);\r
-\r
- /**\r
- * When the manager expires session not tied to a request.\r
- * The cluster will periodically ask for a list of sessions\r
- * that should expire and that should be sent across the wire.\r
- * @return String[] The invalidated sessions\r
- */\r
- public String[] getInvalidatedSessions();\r
- \r
- /**\r
- * Return the name of the manager, at host /context name and at engine hostname+/context.\r
- * @return String\r
- * @since 5.5.10\r
- */\r
- public String getName();\r
- \r
- /**\r
- * Set the name of the manager, at host /context name and at engine hostname+/context\r
- * @param name\r
- * @since 5.5.10\r
- */\r
- public void setName(String name);\r
- \r
- public CatalinaCluster getCluster();\r
-\r
- public void setCluster(CatalinaCluster cluster);\r
- \r
- /**\r
- * @return Manager send only to same cluster domain.\r
- * @since 5.5.10\r
- */\r
- public boolean doDomainReplication();\r
-\r
- /**\r
- * @param sendClusterDomainOnly Flag value.\r
- * @since 5.5.10\r
- */\r
- public void setDomainReplication(boolean domainReplication);\r
-\r
- /**\r
- * @param mode The mode\r
- * @since 5.5.10\r
- */\r
- public void setDefaultMode(boolean mode);\r
-\r
- /**\r
- * @since 5.5.10\r
- */\r
- public boolean isDefaultMode();\r
- \r
- public ReplicationStream getReplicationStream(byte[] data) throws IOException;\r
-\r
- public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException;\r
- \r
- public boolean isNotifyListenersOnReplication();\r
-\r
- public ClusterManager cloneFromTemplate();\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+
+import org.apache.catalina.Manager;
+import java.io.IOException;
+import org.apache.catalina.tribes.io.ReplicationStream;
+
+
+/**
+ * The common interface used by all cluster manager.
+ * This is so that we can have a more pluggable way
+ * of swapping session managers for different algorithms.
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ */
+public interface ClusterManager extends Manager {
+
+ /**
+ * A message was received from another node, this
+ * is the callback method to implement if you are interested in
+ * receiving replication messages.
+ * @param msg - the message received.
+ */
+ public void messageDataReceived(ClusterMessage msg);
+
+ /**
+ * When the request has been completed, the replication valve
+ * will notify the manager, and the manager will decide whether
+ * any replication is needed or not.
+ * If there is a need for replication, the manager will
+ * create a session message and that will be replicated.
+ * The cluster determines where it gets sent.
+ * @param sessionId - the sessionId that just completed.
+ * @return a SessionMessage to be sent.
+ */
+ public ClusterMessage requestCompleted(String sessionId);
+
+ /**
+ * When the manager expires session not tied to a request.
+ * The cluster will periodically ask for a list of sessions
+ * that should expire and that should be sent across the wire.
+ * @return String[] The invalidated sessions
+ */
+ public String[] getInvalidatedSessions();
+
+ /**
+ * Return the name of the manager, at host /context name and at engine hostname+/context.
+ * @return String
+ * @since 5.5.10
+ */
+ public String getName();
+
+ /**
+ * Set the name of the manager, at host /context name and at engine hostname+/context
+ * @param name
+ * @since 5.5.10
+ */
+ public void setName(String name);
+
+ public CatalinaCluster getCluster();
+
+ public void setCluster(CatalinaCluster cluster);
+
+ /**
+ * @return Manager send only to same cluster domain.
+ * @since 5.5.10
+ */
+ public boolean doDomainReplication();
+
+ /**
+ * @param sendClusterDomainOnly Flag value.
+ * @since 5.5.10
+ */
+ public void setDomainReplication(boolean domainReplication);
+
+ /**
+ * @param mode The mode
+ * @since 5.5.10
+ */
+ public void setDefaultMode(boolean mode);
+
+ /**
+ * @since 5.5.10
+ */
+ public boolean isDefaultMode();
+
+ public ReplicationStream getReplicationStream(byte[] data) throws IOException;
+
+ public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException;
+
+ public boolean isNotifyListenersOnReplication();
+
+ public ClusterManager cloneFromTemplate();
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha;\r
-\r
-import java.io.Serializable;\r
-import org.apache.catalina.tribes.Member;\r
-\r
-\r
-/**\r
- * @author Filip Hanik\r
- * \r
- */\r
-public interface ClusterMessage extends Serializable {\r
- public Member getAddress();\r
- public void setAddress(Member member);\r
- public String getUniqueId();\r
- public void setUniqueId(String id);\r
- public long getTimestamp();\r
- public void setTimestamp(long timestamp);\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+import java.io.Serializable;
+import org.apache.catalina.tribes.Member;
+
+
+/**
+ * @author Filip Hanik
+ *
+ */
+public interface ClusterMessage extends Serializable {
+ public Member getAddress();
+ public void setAddress(Member member);
+ public String getUniqueId();
+ public void setUniqueId(String id);
+ public long getTimestamp();
+ public void setTimestamp(long timestamp);
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.ha;\r
-\r
-\r
-import org.apache.tomcat.util.digester.Digester;\r
-import org.apache.tomcat.util.digester.RuleSetBase;\r
-\r
-\r
-/**\r
- * <p><strong>RuleSet</strong> for processing the contents of a\r
- * Cluster definition element. </p>\r
- *\r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * @version $Revision: 387285 $ $Date: 2006-03-20 13:30:50 -0600 (Mon, 20 Mar 2006) $\r
- */\r
-\r
-public class ClusterRuleSet extends RuleSetBase {\r
-\r
-\r
- // ----------------------------------------------------- Instance Variables\r
-\r
-\r
- /**\r
- * The matching pattern prefix to use for recognizing our elements.\r
- */\r
- protected String prefix = null;\r
-\r
-\r
- // ------------------------------------------------------------ Constructor\r
-\r
-\r
- /**\r
- * Construct an instance of this <code>RuleSet</code> with the default\r
- * matching pattern prefix.\r
- */\r
- public ClusterRuleSet() {\r
-\r
- this("");\r
-\r
- }\r
-\r
-\r
- /**\r
- * Construct an instance of this <code>RuleSet</code> with the specified\r
- * matching pattern prefix.\r
- *\r
- * @param prefix Prefix for matching pattern rules (including the\r
- * trailing slash character)\r
- */\r
- public ClusterRuleSet(String prefix) {\r
- super();\r
- this.namespaceURI = null;\r
- this.prefix = prefix;\r
- }\r
-\r
-\r
- // --------------------------------------------------------- Public Methods\r
-\r
-\r
- /**\r
- * <p>Add the set of Rule instances defined in this RuleSet to the\r
- * specified <code>Digester</code> instance, associating them with\r
- * our namespace URI (if any). This method should only be called\r
- * by a Digester instance.</p>\r
- *\r
- * @param digester Digester instance to which the new Rule instances\r
- * should be added.\r
- */\r
- public void addRuleInstances(Digester digester) {\r
- //Cluster configuration start\r
- digester.addObjectCreate(prefix + "Manager",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(prefix + "Manager");\r
- digester.addSetNext(prefix + "Manager",\r
- "setManagerTemplate",\r
- "org.apache.catalina.ha.ClusterManager");\r
- \r
-\r
-\r
- digester.addObjectCreate(prefix + "Channel",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(prefix + "Channel");\r
- digester.addSetNext(prefix + "Channel",\r
- "setChannel",\r
- "org.apache.catalina.tribes.Channel");\r
-\r
-\r
- String channelPrefix = prefix + "Channel/";\r
- { //channel properties\r
- digester.addObjectCreate(channelPrefix + "Membership",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(channelPrefix + "Membership");\r
- digester.addSetNext(channelPrefix + "Membership",\r
- "setMembershipService",\r
- "org.apache.catalina.tribes.MembershipService");\r
-\r
- digester.addObjectCreate(channelPrefix + "Sender",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(channelPrefix + "Sender");\r
- digester.addSetNext(channelPrefix + "Sender",\r
- "setChannelSender",\r
- "org.apache.catalina.tribes.ChannelSender");\r
-\r
- digester.addObjectCreate(channelPrefix + "Sender/Transport",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(channelPrefix + "Sender/Transport");\r
- digester.addSetNext(channelPrefix + "Sender/Transport",\r
- "setTransport",\r
- "org.apache.catalina.tribes.transport.MultiPointSender");\r
-\r
-\r
- digester.addObjectCreate(channelPrefix + "Receiver",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(channelPrefix + "Receiver");\r
- digester.addSetNext(channelPrefix + "Receiver",\r
- "setChannelReceiver",\r
- "org.apache.catalina.tribes.ChannelReceiver");\r
-\r
- digester.addObjectCreate(channelPrefix + "Interceptor",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(channelPrefix + "Interceptor");\r
- digester.addSetNext(channelPrefix + "Interceptor",\r
- "addInterceptor",\r
- "org.apache.catalina.tribes.ChannelInterceptor");\r
-\r
- \r
- digester.addObjectCreate(channelPrefix + "Interceptor/Member",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(channelPrefix + "Interceptor/Member");\r
- digester.addSetNext(channelPrefix + "Interceptor/Member",\r
- "addStaticMember",\r
- "org.apache.catalina.tribes.Member");\r
- }\r
-\r
- digester.addObjectCreate(prefix + "Valve",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(prefix + "Valve");\r
- digester.addSetNext(prefix + "Valve",\r
- "addValve",\r
- "org.apache.catalina.Valve");\r
- \r
- digester.addObjectCreate(prefix + "Deployer",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(prefix + "Deployer");\r
- digester.addSetNext(prefix + "Deployer",\r
- "setClusterDeployer",\r
- "org.apache.catalina.ha.ClusterDeployer");\r
- \r
- digester.addObjectCreate(prefix + "Listener",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(prefix + "Listener");\r
- digester.addSetNext(prefix + "Listener",\r
- "addLifecycleListener",\r
- "org.apache.catalina.LifecycleListener");\r
- \r
- digester.addObjectCreate(prefix + "ClusterListener",\r
- null, // MUST be specified in the element\r
- "className");\r
- digester.addSetProperties(prefix + "ClusterListener");\r
- digester.addSetNext(prefix + "ClusterListener",\r
- "addClusterListener",\r
- "org.apache.catalina.ha.ClusterListener");\r
- //Cluster configuration end\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+
+import org.apache.tomcat.util.digester.Digester;
+import org.apache.tomcat.util.digester.RuleSetBase;
+
+
+/**
+ * <p><strong>RuleSet</strong> for processing the contents of a
+ * Cluster definition element. </p>
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class ClusterRuleSet extends RuleSetBase {
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The matching pattern prefix to use for recognizing our elements.
+ */
+ protected String prefix = null;
+
+
+ // ------------------------------------------------------------ Constructor
+
+
+ /**
+ * Construct an instance of this <code>RuleSet</code> with the default
+ * matching pattern prefix.
+ */
+ public ClusterRuleSet() {
+
+ this("");
+
+ }
+
+
+ /**
+ * Construct an instance of this <code>RuleSet</code> with the specified
+ * matching pattern prefix.
+ *
+ * @param prefix Prefix for matching pattern rules (including the
+ * trailing slash character)
+ */
+ public ClusterRuleSet(String prefix) {
+ super();
+ this.namespaceURI = null;
+ this.prefix = prefix;
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * <p>Add the set of Rule instances defined in this RuleSet to the
+ * specified <code>Digester</code> instance, associating them with
+ * our namespace URI (if any). This method should only be called
+ * by a Digester instance.</p>
+ *
+ * @param digester Digester instance to which the new Rule instances
+ * should be added.
+ */
+ public void addRuleInstances(Digester digester) {
+ //Cluster configuration start
+ digester.addObjectCreate(prefix + "Manager",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(prefix + "Manager");
+ digester.addSetNext(prefix + "Manager",
+ "setManagerTemplate",
+ "org.apache.catalina.ha.ClusterManager");
+
+
+
+ digester.addObjectCreate(prefix + "Channel",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(prefix + "Channel");
+ digester.addSetNext(prefix + "Channel",
+ "setChannel",
+ "org.apache.catalina.tribes.Channel");
+
+
+ String channelPrefix = prefix + "Channel/";
+ { //channel properties
+ digester.addObjectCreate(channelPrefix + "Membership",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(channelPrefix + "Membership");
+ digester.addSetNext(channelPrefix + "Membership",
+ "setMembershipService",
+ "org.apache.catalina.tribes.MembershipService");
+
+ digester.addObjectCreate(channelPrefix + "Sender",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(channelPrefix + "Sender");
+ digester.addSetNext(channelPrefix + "Sender",
+ "setChannelSender",
+ "org.apache.catalina.tribes.ChannelSender");
+
+ digester.addObjectCreate(channelPrefix + "Sender/Transport",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(channelPrefix + "Sender/Transport");
+ digester.addSetNext(channelPrefix + "Sender/Transport",
+ "setTransport",
+ "org.apache.catalina.tribes.transport.MultiPointSender");
+
+
+ digester.addObjectCreate(channelPrefix + "Receiver",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(channelPrefix + "Receiver");
+ digester.addSetNext(channelPrefix + "Receiver",
+ "setChannelReceiver",
+ "org.apache.catalina.tribes.ChannelReceiver");
+
+ digester.addObjectCreate(channelPrefix + "Interceptor",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(channelPrefix + "Interceptor");
+ digester.addSetNext(channelPrefix + "Interceptor",
+ "addInterceptor",
+ "org.apache.catalina.tribes.ChannelInterceptor");
+
+
+ digester.addObjectCreate(channelPrefix + "Interceptor/Member",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(channelPrefix + "Interceptor/Member");
+ digester.addSetNext(channelPrefix + "Interceptor/Member",
+ "addStaticMember",
+ "org.apache.catalina.tribes.Member");
+ }
+
+ digester.addObjectCreate(prefix + "Valve",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(prefix + "Valve");
+ digester.addSetNext(prefix + "Valve",
+ "addValve",
+ "org.apache.catalina.Valve");
+
+ digester.addObjectCreate(prefix + "Deployer",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(prefix + "Deployer");
+ digester.addSetNext(prefix + "Deployer",
+ "setClusterDeployer",
+ "org.apache.catalina.ha.ClusterDeployer");
+
+ digester.addObjectCreate(prefix + "Listener",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(prefix + "Listener");
+ digester.addSetNext(prefix + "Listener",
+ "addLifecycleListener",
+ "org.apache.catalina.LifecycleListener");
+
+ digester.addObjectCreate(prefix + "ClusterListener",
+ null, // MUST be specified in the element
+ "className");
+ digester.addSetProperties(prefix + "ClusterListener");
+ digester.addSetNext(prefix + "ClusterListener",
+ "addClusterListener",
+ "org.apache.catalina.ha.ClusterListener");
+ //Cluster configuration end
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.ha;\r
-\r
-import org.apache.catalina.Session;\r
-import javax.servlet.http.HttpSession;\r
-\r
-public interface ClusterSession extends Session, HttpSession {\r
- /**\r
- * returns true if this session is the primary session, if that is the\r
- * case, the manager can expire it upon timeout.\r
- * @return True if this session is primary\r
- */\r
- public boolean isPrimarySession();\r
-\r
- /**\r
- * Sets whether this is the primary session or not.\r
- * @param primarySession Flag value\r
- */\r
- public void setPrimarySession(boolean primarySession);\r
- \r
- \r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+import org.apache.catalina.Session;
+import javax.servlet.http.HttpSession;
+
+public interface ClusterSession extends Session, HttpSession {
+ /**
+ * returns true if this session is the primary session, if that is the
+ * case, the manager can expire it upon timeout.
+ * @return True if this session is primary
+ */
+ public boolean isPrimarySession();
+
+ /**
+ * Sets whether this is the primary session or not.
+ * @param primarySession Flag value
+ */
+ public void setPrimarySession(boolean primarySession);
+
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha;\r
-\r
-import org.apache.catalina.Valve;\r
-\r
-/**\r
- * Cluster valves are a simple extension to the Tomcat valve architecture\r
- * with a small addition of being able to reference the cluster component in the container it sits in.\r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * @version $Revision: 303842 $, $Date: 2005-04-10 11:20:46 -0500 (Sun, 10 Apr 2005) $\r
- */\r
-public interface ClusterValve extends Valve{\r
- /**\r
- * Returns the cluster the cluster deployer is associated with\r
- * @return CatalinaCluster\r
- */\r
- public CatalinaCluster getCluster();\r
-\r
- /**\r
- * Associates the cluster deployer with a cluster\r
- * @param cluster CatalinaCluster\r
- */\r
- public void setCluster(CatalinaCluster cluster);\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+import org.apache.catalina.Valve;
+
+/**
+ * Cluster valves are a simple extension to the Tomcat valve architecture
+ * with a small addition of being able to reference the cluster component in the container it sits in.
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public interface ClusterValve extends Valve{
+ /**
+ * Returns the cluster the cluster deployer is associated with
+ * @return CatalinaCluster
+ */
+ public CatalinaCluster getCluster();
+
+ /**
+ * Associates the cluster deployer with a cluster
+ * @param cluster CatalinaCluster
+ */
+ public void setCluster(CatalinaCluster cluster);
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.ha;\r
-\r
-/**\r
- * Manifest constants for the <code>org.apache.catalina.ha</code>\r
- * package.\r
- *\r
- * @author Bip Thelin\r
- * @version $Revision: 302726 $, $Date: 2004-02-27 08:59:07 -0600 (Fri, 27 Feb 2004) $\r
- */\r
-\r
-public final class Constants {\r
- public static final String Package = "org.apache.catalina.ha";\r
-}\r
+/*
+ * 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.apache.catalina.ha;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.ha</code>
+ * package.
+ *
+ * @author Bip Thelin
+ * @version $Revision$, $Date$
+ */
+
+public final class Constants {
+ public static final String Package = "org.apache.catalina.ha";
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha.context;\r
-\r
-import org.apache.catalina.core.StandardContext;\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.ha.CatalinaCluster;\r
-import org.apache.catalina.tribes.tipis.ReplicatedMap;\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.Loader;\r
-import org.apache.catalina.core.ApplicationContext;\r
-import org.apache.catalina.Globals;\r
-import javax.servlet.ServletContext;\r
-import java.util.HashMap;\r
-import org.apache.catalina.tribes.tipis.LazyReplicatedMap;\r
-\r
-/**\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class ReplicatedContext extends StandardContext {\r
- private int mapSendOptions = Channel.SEND_OPTIONS_DEFAULT;\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( ReplicatedContext.class );\r
-\r
- protected static long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds\r
- \r
-\r
-\r
- public synchronized void start() throws LifecycleException {\r
- if ( this.started ) return;\r
- try {\r
- CatalinaCluster catclust = (CatalinaCluster)this.getCluster();\r
- if (this.context == null) this.context = new ReplApplContext(this.getBasePath(), this);\r
- if ( catclust != null ) {\r
- ReplicatedMap map = new ReplicatedMap(this,catclust.getChannel(),DEFAULT_REPL_TIMEOUT,\r
- getName(),getClassLoaders());\r
- map.setChannelSendOptions(mapSendOptions);\r
- ((ReplApplContext)this.context).setAttributeMap(map);\r
- if (getAltDDName() != null) context.setAttribute(Globals.ALT_DD_ATTR, getAltDDName());\r
- }\r
- super.start();\r
- } catch ( Exception x ) {\r
- log.error("Unable to start ReplicatedContext",x);\r
- throw new LifecycleException("Failed to start ReplicatedContext",x);\r
- }\r
- }\r
- \r
- public synchronized void stop() throws LifecycleException\r
- {\r
- ReplicatedMap map = (ReplicatedMap)((ReplApplContext)this.context).getAttributeMap();\r
- if ( map!=null ) {\r
- map.breakdown();\r
- }\r
- if ( !this.started ) return;\r
- try {\r
- } catch ( Exception x ){\r
- log.error("Unable to stop ReplicatedContext",x);\r
- throw new LifecycleException("Failed to stop ReplicatedContext",x);\r
- } finally {\r
- super.stop();\r
- }\r
-\r
- }\r
-\r
-\r
- public void setMapSendOptions(int mapSendOptions) {\r
- this.mapSendOptions = mapSendOptions;\r
- }\r
-\r
- public int getMapSendOptions() {\r
- return mapSendOptions;\r
- }\r
- \r
- public ClassLoader[] getClassLoaders() {\r
- Loader loader = null;\r
- ClassLoader classLoader = null;\r
- loader = this.getLoader();\r
- if (loader != null) classLoader = loader.getClassLoader();\r
- if ( classLoader == null ) classLoader = Thread.currentThread().getContextClassLoader();\r
- if ( classLoader == Thread.currentThread().getContextClassLoader() ) {\r
- return new ClassLoader[] {classLoader};\r
- } else {\r
- return new ClassLoader[] {classLoader,Thread.currentThread().getContextClassLoader()};\r
- }\r
- }\r
- \r
- public ServletContext getServletContext() {\r
- return ((ReplApplContext)context).getFacade();\r
-\r
- }\r
-\r
- \r
- protected static class ReplApplContext extends ApplicationContext {\r
- public ReplApplContext(String basePath, StandardContext context) {\r
- super(basePath,context);\r
- }\r
- \r
- protected ServletContext getFacade() {\r
- return super.getFacade();\r
- }\r
- \r
- public HashMap getAttributeMap() {\r
- return (HashMap)this.attributes;\r
- }\r
- public void setAttributeMap(HashMap map) {\r
- this.attributes = map;\r
- }\r
-\r
- }\r
-\r
-\r
+/*
+ * 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.apache.catalina.ha.context;
+
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.catalina.tribes.tipis.ReplicatedMap;
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.Loader;
+import org.apache.catalina.core.ApplicationContext;
+import org.apache.catalina.Globals;
+import javax.servlet.ServletContext;
+import java.util.HashMap;
+import org.apache.catalina.tribes.tipis.LazyReplicatedMap;
+
+/**
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class ReplicatedContext extends StandardContext {
+ private int mapSendOptions = Channel.SEND_OPTIONS_DEFAULT;
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( ReplicatedContext.class );
+
+ protected static long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds
+
+
+
+ public synchronized void start() throws LifecycleException {
+ if ( this.started ) return;
+ try {
+ CatalinaCluster catclust = (CatalinaCluster)this.getCluster();
+ if (this.context == null) this.context = new ReplApplContext(this.getBasePath(), this);
+ if ( catclust != null ) {
+ ReplicatedMap map = new ReplicatedMap(this,catclust.getChannel(),DEFAULT_REPL_TIMEOUT,
+ getName(),getClassLoaders());
+ map.setChannelSendOptions(mapSendOptions);
+ ((ReplApplContext)this.context).setAttributeMap(map);
+ if (getAltDDName() != null) context.setAttribute(Globals.ALT_DD_ATTR, getAltDDName());
+ }
+ super.start();
+ } catch ( Exception x ) {
+ log.error("Unable to start ReplicatedContext",x);
+ throw new LifecycleException("Failed to start ReplicatedContext",x);
+ }
+ }
+
+ public synchronized void stop() throws LifecycleException
+ {
+ ReplicatedMap map = (ReplicatedMap)((ReplApplContext)this.context).getAttributeMap();
+ if ( map!=null ) {
+ map.breakdown();
+ }
+ if ( !this.started ) return;
+ try {
+ } catch ( Exception x ){
+ log.error("Unable to stop ReplicatedContext",x);
+ throw new LifecycleException("Failed to stop ReplicatedContext",x);
+ } finally {
+ super.stop();
+ }
+
+ }
+
+
+ public void setMapSendOptions(int mapSendOptions) {
+ this.mapSendOptions = mapSendOptions;
+ }
+
+ public int getMapSendOptions() {
+ return mapSendOptions;
+ }
+
+ public ClassLoader[] getClassLoaders() {
+ Loader loader = null;
+ ClassLoader classLoader = null;
+ loader = this.getLoader();
+ if (loader != null) classLoader = loader.getClassLoader();
+ if ( classLoader == null ) classLoader = Thread.currentThread().getContextClassLoader();
+ if ( classLoader == Thread.currentThread().getContextClassLoader() ) {
+ return new ClassLoader[] {classLoader};
+ } else {
+ return new ClassLoader[] {classLoader,Thread.currentThread().getContextClassLoader()};
+ }
+ }
+
+ public ServletContext getServletContext() {
+ return ((ReplApplContext)context).getFacade();
+
+ }
+
+
+ protected static class ReplApplContext extends ApplicationContext {
+ public ReplApplContext(String basePath, StandardContext context) {
+ super(basePath,context);
+ }
+
+ protected ServletContext getFacade() {
+ return super.getFacade();
+ }
+
+ public HashMap getAttributeMap() {
+ return (HashMap)this.attributes;
+ }
+ public void setAttributeMap(HashMap map) {
+ this.attributes = map;
+ }
+
+ }
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.deploy;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.net.URL;\r
-import java.util.HashMap;\r
-import javax.management.MBeanServer;\r
-import javax.management.ObjectName;\r
-\r
-import org.apache.catalina.Context;\r
-import org.apache.catalina.Engine;\r
-import org.apache.catalina.Host;\r
-import org.apache.catalina.Lifecycle;\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.ha.CatalinaCluster;\r
-import org.apache.catalina.ha.ClusterDeployer;\r
-import org.apache.catalina.ha.ClusterListener;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.tomcat.util.modeler.Registry;\r
-\r
-\r
-/**\r
- * <p>\r
- * A farm war deployer is a class that is able to deploy/undeploy web\r
- * applications in WAR form within the cluster.\r
- * </p>\r
- * Any host can act as the admin, and will have three directories\r
- * <ul>\r
- * <li>deployDir - the directory where we watch for changes</li>\r
- * <li>applicationDir - the directory where we install applications</li>\r
- * <li>tempDir - a temporaryDirectory to store binary data when downloading a\r
- * war from the cluster</li>\r
- * </ul>\r
- * Currently we only support deployment of WAR files since they are easier to\r
- * send across the wire.\r
- * \r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * @version $Revision: 390639 $\r
- */\r
-public class FarmWarDeployer extends ClusterListener implements ClusterDeployer, FileChangeListener {\r
- /*--Static Variables----------------------------------------*/\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory\r
- .getLog(FarmWarDeployer.class);\r
- /**\r
- * The descriptive information about this implementation.\r
- */\r
- private static final String info = "FarmWarDeployer/1.2";\r
-\r
- /*--Instance Variables--------------------------------------*/\r
- protected CatalinaCluster cluster = null;\r
-\r
- protected boolean started = false; //default 5 seconds\r
-\r
- protected HashMap fileFactories = new HashMap();\r
-\r
- protected String deployDir;\r
-\r
- protected String tempDir;\r
-\r
- protected String watchDir;\r
-\r
- protected boolean watchEnabled = false;\r
-\r
- protected WarWatcher watcher = null;\r
-\r
- /**\r
- * Iteration count for background processing.\r
- */\r
- private int count = 0;\r
-\r
- /**\r
- * Frequency of the Farm watchDir check. Cluster wide deployment will be\r
- * done once for the specified amount of backgrondProcess calls (ie, the\r
- * lower the amount, the most often the checks will occur).\r
- */\r
- protected int processDeployFrequency = 2;\r
-\r
- /**\r
- * Path where context descriptors should be deployed.\r
- */\r
- protected File configBase = null;\r
-\r
- /**\r
- * The associated host.\r
- */\r
- protected Host host = null;\r
-\r
- /**\r
- * The host appBase.\r
- */\r
- protected File appBase = null;\r
-\r
- /**\r
- * MBean server.\r
- */\r
- protected MBeanServer mBeanServer = null;\r
-\r
- /**\r
- * The associated deployer ObjectName.\r
- */\r
- protected ObjectName oname = null;\r
-\r
- /*--Constructor---------------------------------------------*/\r
- public FarmWarDeployer() {\r
- }\r
-\r
- /**\r
- * Return descriptive information about this deployer implementation and the\r
- * corresponding version number, in the format\r
- * <code><description>/<version></code>.\r
- */\r
- public String getInfo() {\r
-\r
- return (info);\r
-\r
- }\r
-\r
- /*--Logic---------------------------------------------------*/\r
- public void start() throws Exception {\r
- if (started)\r
- return;\r
- getCluster().addClusterListener(this);\r
- if (watchEnabled) {\r
- watcher = new WarWatcher(this, new File(getWatchDir()));\r
- if (log.isInfoEnabled())\r
- log.info("Cluster deployment is watching " + getWatchDir()\r
- + " for changes.");\r
- }\r
- \r
- // Check to correct engine and host setup\r
- Object parent = getCluster().getContainer();\r
- Engine engine = null;\r
- if ( parent instanceof Host ) {\r
- host = (Host) parent;\r
- engine = (Engine) host.getParent();\r
- }else {\r
- engine = (Engine)parent;\r
- }\r
- try {\r
- oname = new ObjectName(engine.getName() + ":type=Deployer,host="\r
- + host.getName());\r
- } catch (Exception e) {\r
- log.error("Can't construct MBean object name" + e);\r
- }\r
- configBase = new File(System.getProperty("catalina.base"), "conf");\r
- if (engine != null) {\r
- configBase = new File(configBase, engine.getName());\r
- } else if (host != null) {\r
- configBase = new File(configBase, host.getName());\r
- }\r
-\r
- // Retrieve the MBean server\r
- mBeanServer = Registry.getRegistry(null, null).getMBeanServer();\r
-\r
- started = true;\r
- count = 0;\r
- if (log.isInfoEnabled())\r
- log.info("Cluster FarmWarDeployer started.");\r
- }\r
-\r
- /*\r
- * stop cluster wide deployments\r
- * \r
- * @see org.apache.catalina.ha.ClusterDeployer#stop()\r
- */\r
- public void stop() throws LifecycleException {\r
- started = false;\r
- getCluster().removeClusterListener(this);\r
- count = 0;\r
- if (watcher != null) {\r
- watcher.clear();\r
- watcher = null;\r
-\r
- }\r
- if (log.isInfoEnabled())\r
- log.info("Cluster FarmWarDeployer stopped.");\r
- }\r
-\r
- public void cleanDeployDir() {\r
- throw new java.lang.UnsupportedOperationException(\r
- "Method cleanDeployDir() not yet implemented.");\r
- }\r
-\r
- /**\r
- * Callback from the cluster, when a message is received, The cluster will\r
- * broadcast it invoking the messageReceived on the receiver.\r
- * \r
- * @param msg\r
- * ClusterMessage - the message received from the cluster\r
- */\r
- public void messageReceived(ClusterMessage msg) {\r
- try {\r
- if (msg instanceof FileMessage && msg != null) {\r
- FileMessage fmsg = (FileMessage) msg;\r
- if (log.isDebugEnabled())\r
- log.debug("receive cluster deployment [ path: "\r
- + fmsg.getContextPath() + " war: "\r
- + fmsg.getFileName() + " ]");\r
- FileMessageFactory factory = getFactory(fmsg);\r
- // TODO correct second try after app is in service!\r
- if (factory.writeMessage(fmsg)) {\r
- //last message received war file is completed\r
- String name = factory.getFile().getName();\r
- if (!name.endsWith(".war"))\r
- name = name + ".war";\r
- File deployable = new File(getDeployDir(), name);\r
- try {\r
- String path = fmsg.getContextPath();\r
- if (!isServiced(path)) {\r
- addServiced(path);\r
- try {\r
- remove(path);\r
- factory.getFile().renameTo(deployable);\r
- check(path);\r
- } finally {\r
- removeServiced(path);\r
- }\r
- if (log.isDebugEnabled())\r
- log.debug("deployment from " + path\r
- + " finished.");\r
- } else\r
- log.error("Application " + path\r
- + " in used. touch war file " + name\r
- + " again!");\r
- } catch (Exception ex) {\r
- log.error(ex);\r
- } finally {\r
- removeFactory(fmsg);\r
- }\r
- }\r
- } else if (msg instanceof UndeployMessage && msg != null) {\r
- try {\r
- UndeployMessage umsg = (UndeployMessage) msg;\r
- String path = umsg.getContextPath();\r
- if (log.isDebugEnabled())\r
- log.debug("receive cluster undeployment from " + path);\r
- if (!isServiced(path)) {\r
- addServiced(path);\r
- try {\r
- remove(path);\r
- } finally {\r
- removeServiced(path);\r
- }\r
- if (log.isDebugEnabled())\r
- log.debug("undeployment from " + path\r
- + " finished.");\r
- } else\r
- log.error("Application "\r
- + path\r
- + " in used. Sorry not remove from backup cluster nodes!");\r
- } catch (Exception ex) {\r
- log.error(ex);\r
- }\r
- }\r
- } catch (java.io.IOException x) {\r
- log.error("Unable to read farm deploy file message.", x);\r
- }\r
- }\r
-\r
- /**\r
- * create factory for all transported war files\r
- * \r
- * @param msg\r
- * @return Factory for all app message (war files)\r
- * @throws java.io.FileNotFoundException\r
- * @throws java.io.IOException\r
- */\r
- public synchronized FileMessageFactory getFactory(FileMessage msg)\r
- throws java.io.FileNotFoundException, java.io.IOException {\r
- File tmpFile = new File(msg.getFileName());\r
- File writeToFile = new File(getTempDir(), tmpFile.getName());\r
- FileMessageFactory factory = (FileMessageFactory) fileFactories.get(msg\r
- .getFileName());\r
- if (factory == null) {\r
- factory = FileMessageFactory.getInstance(writeToFile, true);\r
- fileFactories.put(msg.getFileName(), factory);\r
- }\r
- return factory;\r
- }\r
-\r
- /**\r
- * Remove file (war) from messages)\r
- * \r
- * @param msg\r
- */\r
- public void removeFactory(FileMessage msg) {\r
- fileFactories.remove(msg.getFileName());\r
- }\r
-\r
- /**\r
- * Before the cluster invokes messageReceived the cluster will ask the\r
- * receiver to accept or decline the message, In the future, when messages\r
- * get big, the accept method will only take a message header\r
- * \r
- * @param msg\r
- * ClusterMessage\r
- * @return boolean - returns true to indicate that messageReceived should be\r
- * invoked. If false is returned, the messageReceived method will\r
- * not be invoked.\r
- */\r
- public boolean accept(ClusterMessage msg) {\r
- return (msg instanceof FileMessage) || (msg instanceof UndeployMessage);\r
- }\r
-\r
- /**\r
- * Install a new web application, whose web application archive is at the\r
- * specified URL, into this container and all the other members of the\r
- * cluster with the specified context path. A context path of "" (the empty\r
- * string) should be used for the root application for this container.\r
- * Otherwise, the context path must start with a slash.\r
- * <p>\r
- * If this application is successfully installed locally, a ContainerEvent\r
- * of type <code>INSTALL_EVENT</code> will be sent to all registered\r
- * listeners, with the newly created <code>Context</code> as an argument.\r
- * \r
- * @param contextPath\r
- * The context path to which this application should be installed\r
- * (must be unique)\r
- * @param war\r
- * A URL of type "jar:" that points to a WAR file, or type\r
- * "file:" that points to an unpacked directory structure\r
- * containing the web application to be installed\r
- * \r
- * @exception IllegalArgumentException\r
- * if the specified context path is malformed (it must be ""\r
- * or start with a slash)\r
- * @exception IllegalStateException\r
- * if the specified context path is already attached to an\r
- * existing web application\r
- * @exception IOException\r
- * if an input/output error was encountered during\r
- * installation\r
- */\r
- public void install(String contextPath, URL war) throws IOException {\r
- Member[] members = getCluster().getMembers();\r
- Member localMember = getCluster().getLocalMember();\r
- FileMessageFactory factory = FileMessageFactory.getInstance(new File(\r
- war.getFile()), false);\r
- FileMessage msg = new FileMessage(localMember, war.getFile(),\r
- contextPath);\r
- if(log.isDebugEnabled())\r
- log.debug("Send cluster war deployment [ path:"\r
- + contextPath + " war: " + war + " ] started.");\r
- msg = factory.readMessage(msg);\r
- while (msg != null) {\r
- for (int i = 0; i < members.length; i++) {\r
- if (log.isDebugEnabled())\r
- log.debug("Send cluster war fragment [ path: "\r
- + contextPath + " war: " + war + " to: " + members[i] + " ]");\r
- getCluster().send(msg, members[i]);\r
- }\r
- msg = factory.readMessage(msg);\r
- }\r
- if(log.isDebugEnabled())\r
- log.debug("Send cluster war deployment [ path: "\r
- + contextPath + " war: " + war + " ] finished.");\r
- }\r
-\r
- /**\r
- * Remove an existing web application, attached to the specified context\r
- * path. If this application is successfully removed, a ContainerEvent of\r
- * type <code>REMOVE_EVENT</code> will be sent to all registered\r
- * listeners, with the removed <code>Context</code> as an argument.\r
- * Deletes the web application war file and/or directory if they exist in\r
- * the Host's appBase.\r
- * \r
- * @param contextPath\r
- * The context path of the application to be removed\r
- * @param undeploy\r
- * boolean flag to remove web application from server\r
- * \r
- * @exception IllegalArgumentException\r
- * if the specified context path is malformed (it must be ""\r
- * or start with a slash)\r
- * @exception IllegalArgumentException\r
- * if the specified context path does not identify a\r
- * currently installed web application\r
- * @exception IOException\r
- * if an input/output error occurs during removal\r
- */\r
- public void remove(String contextPath, boolean undeploy) throws IOException {\r
- if (log.isInfoEnabled())\r
- log.info("Cluster wide remove of web app " + contextPath);\r
- Member localMember = getCluster().getLocalMember();\r
- UndeployMessage msg = new UndeployMessage(localMember, System\r
- .currentTimeMillis(), "Undeploy:" + contextPath + ":"\r
- + System.currentTimeMillis(), contextPath, undeploy);\r
- if (log.isDebugEnabled())\r
- log.debug("Send cluster wide undeployment from "\r
- + contextPath );\r
- cluster.send(msg);\r
- // remove locally\r
- if (undeploy) {\r
- try {\r
- if (!isServiced(contextPath)) {\r
- addServiced(contextPath);\r
- try {\r
- remove(contextPath);\r
- } finally {\r
- removeServiced(contextPath);\r
- }\r
- } else\r
- log.error("Local remove from " + contextPath\r
- + "failed, other manager has app in service!");\r
-\r
- } catch (Exception ex) {\r
- log.error("local remove from " + contextPath + " failed", ex);\r
- }\r
- }\r
-\r
- }\r
-\r
- /*\r
- * Modifcation from watchDir war detected!\r
- * \r
- * @see org.apache.catalina.ha.deploy.FileChangeListener#fileModified(java.io.File)\r
- */\r
- public void fileModified(File newWar) {\r
- try {\r
- File deployWar = new File(getDeployDir(), newWar.getName());\r
- copy(newWar, deployWar);\r
- String contextName = getContextName(deployWar);\r
- if (log.isInfoEnabled())\r
- log.info("Installing webapp[" + contextName + "] from "\r
- + deployWar.getAbsolutePath());\r
- try {\r
- remove(contextName, false);\r
- } catch (Exception x) {\r
- log.error("No removal", x);\r
- }\r
- install(contextName, deployWar.toURL());\r
- } catch (Exception x) {\r
- log.error("Unable to install WAR file", x);\r
- }\r
- }\r
-\r
- /*\r
- * War remvoe from watchDir\r
- * \r
- * @see org.apache.catalina.ha.deploy.FileChangeListener#fileRemoved(java.io.File)\r
- */\r
- public void fileRemoved(File removeWar) {\r
- try {\r
- String contextName = getContextName(removeWar);\r
- if (log.isInfoEnabled())\r
- log.info("Removing webapp[" + contextName + "]");\r
- remove(contextName, true);\r
- } catch (Exception x) {\r
- log.error("Unable to remove WAR file", x);\r
- }\r
- }\r
-\r
- /**\r
- * Create a context path from war\r
- * @param war War filename\r
- * @return '/filename' or if war name is ROOT.war context name is empty string '' \r
- */\r
- protected String getContextName(File war) {\r
- String contextName = "/"\r
- + war.getName().substring(0,\r
- war.getName().lastIndexOf(".war"));\r
- if("/ROOT".equals(contextName))\r
- contextName= "" ;\r
- return contextName ;\r
- }\r
- \r
- /**\r
- * Given a context path, get the config file name.\r
- */\r
- protected String getConfigFile(String path) {\r
- String basename = null;\r
- if (path.equals("")) {\r
- basename = "ROOT";\r
- } else {\r
- basename = path.substring(1).replace('/', '#');\r
- }\r
- return (basename);\r
- }\r
-\r
- /**\r
- * Given a context path, get the config file name.\r
- */\r
- protected String getDocBase(String path) {\r
- String basename = null;\r
- if (path.equals("")) {\r
- basename = "ROOT";\r
- } else {\r
- basename = path.substring(1);\r
- }\r
- return (basename);\r
- }\r
-\r
- /**\r
- * Return a File object representing the "application root" directory for\r
- * our associated Host.\r
- */\r
- protected File getAppBase() {\r
-\r
- if (appBase != null) {\r
- return appBase;\r
- }\r
-\r
- File file = new File(host.getAppBase());\r
- if (!file.isAbsolute())\r
- file = new File(System.getProperty("catalina.base"), host\r
- .getAppBase());\r
- try {\r
- appBase = file.getCanonicalFile();\r
- } catch (IOException e) {\r
- appBase = file;\r
- }\r
- return (appBase);\r
-\r
- }\r
-\r
- /**\r
- * Invoke the remove method on the deployer.\r
- */\r
- protected void remove(String path) throws Exception {\r
- // TODO Handle remove also work dir content !\r
- // Stop the context first to be nicer\r
- Context context = (Context) host.findChild(path);\r
- if (context != null) {\r
- if(log.isDebugEnabled())\r
- log.debug("Undeploy local context " +path );\r
- ((Lifecycle) context).stop();\r
- File war = new File(getAppBase(), getDocBase(path) + ".war");\r
- File dir = new File(getAppBase(), getDocBase(path));\r
- File xml = new File(configBase, getConfigFile(path) + ".xml");\r
- if (war.exists()) {\r
- war.delete();\r
- } else if (dir.exists()) {\r
- undeployDir(dir);\r
- } else {\r
- xml.delete();\r
- }\r
- // Perform new deployment and remove internal HostConfig state\r
- check(path);\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Delete the specified directory, including all of its contents and\r
- * subdirectories recursively.\r
- * \r
- * @param dir\r
- * File object representing the directory to be deleted\r
- */\r
- protected void undeployDir(File dir) {\r
-\r
- String files[] = dir.list();\r
- if (files == null) {\r
- files = new String[0];\r
- }\r
- for (int i = 0; i < files.length; i++) {\r
- File file = new File(dir, files[i]);\r
- if (file.isDirectory()) {\r
- undeployDir(file);\r
- } else {\r
- file.delete();\r
- }\r
- }\r
- dir.delete();\r
-\r
- }\r
-\r
- /*\r
- * Call watcher to check for deploy changes\r
- * \r
- * @see org.apache.catalina.ha.ClusterDeployer#backgroundProcess()\r
- */\r
- public void backgroundProcess() {\r
- if (started) {\r
- count = (count + 1) % processDeployFrequency;\r
- if (count == 0 && watchEnabled) {\r
- watcher.check();\r
- }\r
- }\r
-\r
- }\r
-\r
- /*--Deployer Operations ------------------------------------*/\r
-\r
- /**\r
- * Invoke the check method on the deployer.\r
- */\r
- protected void check(String name) throws Exception {\r
- String[] params = { name };\r
- String[] signature = { "java.lang.String" };\r
- mBeanServer.invoke(oname, "check", params, signature);\r
- }\r
-\r
- /**\r
- * Invoke the check method on the deployer.\r
- */\r
- protected boolean isServiced(String name) throws Exception {\r
- String[] params = { name };\r
- String[] signature = { "java.lang.String" };\r
- Boolean result = (Boolean) mBeanServer.invoke(oname, "isServiced",\r
- params, signature);\r
- return result.booleanValue();\r
- }\r
-\r
- /**\r
- * Invoke the check method on the deployer.\r
- */\r
- protected void addServiced(String name) throws Exception {\r
- String[] params = { name };\r
- String[] signature = { "java.lang.String" };\r
- mBeanServer.invoke(oname, "addServiced", params, signature);\r
- }\r
-\r
- /**\r
- * Invoke the check method on the deployer.\r
- */\r
- protected void removeServiced(String name) throws Exception {\r
- String[] params = { name };\r
- String[] signature = { "java.lang.String" };\r
- mBeanServer.invoke(oname, "removeServiced", params, signature);\r
- }\r
-\r
- /*--Instance Getters/Setters--------------------------------*/\r
- public CatalinaCluster getCluster() {\r
- return cluster;\r
- }\r
-\r
- public void setCluster(CatalinaCluster cluster) {\r
- this.cluster = cluster;\r
- }\r
-\r
- public boolean equals(Object listener) {\r
- return super.equals(listener);\r
- }\r
-\r
- public int hashCode() {\r
- return super.hashCode();\r
- }\r
-\r
- public String getDeployDir() {\r
- return deployDir;\r
- }\r
-\r
- public void setDeployDir(String deployDir) {\r
- this.deployDir = deployDir;\r
- }\r
-\r
- public String getTempDir() {\r
- return tempDir;\r
- }\r
-\r
- public void setTempDir(String tempDir) {\r
- this.tempDir = tempDir;\r
- }\r
-\r
- public String getWatchDir() {\r
- return watchDir;\r
- }\r
-\r
- public void setWatchDir(String watchDir) {\r
- this.watchDir = watchDir;\r
- }\r
-\r
- public boolean isWatchEnabled() {\r
- return watchEnabled;\r
- }\r
-\r
- public boolean getWatchEnabled() {\r
- return watchEnabled;\r
- }\r
-\r
- public void setWatchEnabled(boolean watchEnabled) {\r
- this.watchEnabled = watchEnabled;\r
- }\r
-\r
- /**\r
- * Return the frequency of watcher checks.\r
- */\r
- public int getProcessDeployFrequency() {\r
-\r
- return (this.processDeployFrequency);\r
-\r
- }\r
-\r
- /**\r
- * Set the watcher checks frequency.\r
- * \r
- * @param processExpiresFrequency\r
- * the new manager checks frequency\r
- */\r
- public void setProcessDeployFrequency(int processExpiresFrequency) {\r
-\r
- if (processExpiresFrequency <= 0) {\r
- return;\r
- }\r
- this.processDeployFrequency = processExpiresFrequency;\r
- }\r
-\r
- /**\r
- * Copy a file to the specified temp directory.\r
- * @param from copy from temp\r
- * @param to to host appBase directory\r
- * @return true, copy successful\r
- */\r
- protected boolean copy(File from, File to) {\r
- try {\r
- if (!to.exists())\r
- to.createNewFile();\r
- java.io.FileInputStream is = new java.io.FileInputStream(from);\r
- java.io.FileOutputStream os = new java.io.FileOutputStream(to,\r
- false);\r
- byte[] buf = new byte[4096];\r
- while (true) {\r
- int len = is.read(buf);\r
- if (len < 0)\r
- break;\r
- os.write(buf, 0, len);\r
- }\r
- is.close();\r
- os.close();\r
- } catch (IOException e) {\r
- log.error("Unable to copy file from:" + from + " to:" + to, e);\r
- return false;\r
- }\r
- return true;\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.deploy;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.catalina.ha.ClusterDeployer;
+import org.apache.catalina.ha.ClusterListener;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.tomcat.util.modeler.Registry;
+
+
+/**
+ * <p>
+ * A farm war deployer is a class that is able to deploy/undeploy web
+ * applications in WAR form within the cluster.
+ * </p>
+ * Any host can act as the admin, and will have three directories
+ * <ul>
+ * <li>deployDir - the directory where we watch for changes</li>
+ * <li>applicationDir - the directory where we install applications</li>
+ * <li>tempDir - a temporaryDirectory to store binary data when downloading a
+ * war from the cluster</li>
+ * </ul>
+ * Currently we only support deployment of WAR files since they are easier to
+ * send across the wire.
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$
+ */
+public class FarmWarDeployer extends ClusterListener implements ClusterDeployer, FileChangeListener {
+ /*--Static Variables----------------------------------------*/
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+ .getLog(FarmWarDeployer.class);
+ /**
+ * The descriptive information about this implementation.
+ */
+ private static final String info = "FarmWarDeployer/1.2";
+
+ /*--Instance Variables--------------------------------------*/
+ protected CatalinaCluster cluster = null;
+
+ protected boolean started = false; //default 5 seconds
+
+ protected HashMap fileFactories = new HashMap();
+
+ protected String deployDir;
+
+ protected String tempDir;
+
+ protected String watchDir;
+
+ protected boolean watchEnabled = false;
+
+ protected WarWatcher watcher = null;
+
+ /**
+ * Iteration count for background processing.
+ */
+ private int count = 0;
+
+ /**
+ * Frequency of the Farm watchDir check. Cluster wide deployment will be
+ * done once for the specified amount of backgrondProcess calls (ie, the
+ * lower the amount, the most often the checks will occur).
+ */
+ protected int processDeployFrequency = 2;
+
+ /**
+ * Path where context descriptors should be deployed.
+ */
+ protected File configBase = null;
+
+ /**
+ * The associated host.
+ */
+ protected Host host = null;
+
+ /**
+ * The host appBase.
+ */
+ protected File appBase = null;
+
+ /**
+ * MBean server.
+ */
+ protected MBeanServer mBeanServer = null;
+
+ /**
+ * The associated deployer ObjectName.
+ */
+ protected ObjectName oname = null;
+
+ /*--Constructor---------------------------------------------*/
+ public FarmWarDeployer() {
+ }
+
+ /**
+ * Return descriptive information about this deployer implementation and the
+ * corresponding version number, in the format
+ * <code><description>/<version></code>.
+ */
+ public String getInfo() {
+
+ return (info);
+
+ }
+
+ /*--Logic---------------------------------------------------*/
+ public void start() throws Exception {
+ if (started)
+ return;
+ getCluster().addClusterListener(this);
+ if (watchEnabled) {
+ watcher = new WarWatcher(this, new File(getWatchDir()));
+ if (log.isInfoEnabled())
+ log.info("Cluster deployment is watching " + getWatchDir()
+ + " for changes.");
+ }
+
+ // Check to correct engine and host setup
+ Object parent = getCluster().getContainer();
+ Engine engine = null;
+ if ( parent instanceof Host ) {
+ host = (Host) parent;
+ engine = (Engine) host.getParent();
+ }else {
+ engine = (Engine)parent;
+ }
+ try {
+ oname = new ObjectName(engine.getName() + ":type=Deployer,host="
+ + host.getName());
+ } catch (Exception e) {
+ log.error("Can't construct MBean object name" + e);
+ }
+ configBase = new File(System.getProperty("catalina.base"), "conf");
+ if (engine != null) {
+ configBase = new File(configBase, engine.getName());
+ } else if (host != null) {
+ configBase = new File(configBase, host.getName());
+ }
+
+ // Retrieve the MBean server
+ mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+
+ started = true;
+ count = 0;
+ if (log.isInfoEnabled())
+ log.info("Cluster FarmWarDeployer started.");
+ }
+
+ /*
+ * stop cluster wide deployments
+ *
+ * @see org.apache.catalina.ha.ClusterDeployer#stop()
+ */
+ public void stop() throws LifecycleException {
+ started = false;
+ getCluster().removeClusterListener(this);
+ count = 0;
+ if (watcher != null) {
+ watcher.clear();
+ watcher = null;
+
+ }
+ if (log.isInfoEnabled())
+ log.info("Cluster FarmWarDeployer stopped.");
+ }
+
+ public void cleanDeployDir() {
+ throw new java.lang.UnsupportedOperationException(
+ "Method cleanDeployDir() not yet implemented.");
+ }
+
+ /**
+ * Callback from the cluster, when a message is received, The cluster will
+ * broadcast it invoking the messageReceived on the receiver.
+ *
+ * @param msg
+ * ClusterMessage - the message received from the cluster
+ */
+ public void messageReceived(ClusterMessage msg) {
+ try {
+ if (msg instanceof FileMessage && msg != null) {
+ FileMessage fmsg = (FileMessage) msg;
+ if (log.isDebugEnabled())
+ log.debug("receive cluster deployment [ path: "
+ + fmsg.getContextPath() + " war: "
+ + fmsg.getFileName() + " ]");
+ FileMessageFactory factory = getFactory(fmsg);
+ // TODO correct second try after app is in service!
+ if (factory.writeMessage(fmsg)) {
+ //last message received war file is completed
+ String name = factory.getFile().getName();
+ if (!name.endsWith(".war"))
+ name = name + ".war";
+ File deployable = new File(getDeployDir(), name);
+ try {
+ String path = fmsg.getContextPath();
+ if (!isServiced(path)) {
+ addServiced(path);
+ try {
+ remove(path);
+ factory.getFile().renameTo(deployable);
+ check(path);
+ } finally {
+ removeServiced(path);
+ }
+ if (log.isDebugEnabled())
+ log.debug("deployment from " + path
+ + " finished.");
+ } else
+ log.error("Application " + path
+ + " in used. touch war file " + name
+ + " again!");
+ } catch (Exception ex) {
+ log.error(ex);
+ } finally {
+ removeFactory(fmsg);
+ }
+ }
+ } else if (msg instanceof UndeployMessage && msg != null) {
+ try {
+ UndeployMessage umsg = (UndeployMessage) msg;
+ String path = umsg.getContextPath();
+ if (log.isDebugEnabled())
+ log.debug("receive cluster undeployment from " + path);
+ if (!isServiced(path)) {
+ addServiced(path);
+ try {
+ remove(path);
+ } finally {
+ removeServiced(path);
+ }
+ if (log.isDebugEnabled())
+ log.debug("undeployment from " + path
+ + " finished.");
+ } else
+ log.error("Application "
+ + path
+ + " in used. Sorry not remove from backup cluster nodes!");
+ } catch (Exception ex) {
+ log.error(ex);
+ }
+ }
+ } catch (java.io.IOException x) {
+ log.error("Unable to read farm deploy file message.", x);
+ }
+ }
+
+ /**
+ * create factory for all transported war files
+ *
+ * @param msg
+ * @return Factory for all app message (war files)
+ * @throws java.io.FileNotFoundException
+ * @throws java.io.IOException
+ */
+ public synchronized FileMessageFactory getFactory(FileMessage msg)
+ throws java.io.FileNotFoundException, java.io.IOException {
+ File tmpFile = new File(msg.getFileName());
+ File writeToFile = new File(getTempDir(), tmpFile.getName());
+ FileMessageFactory factory = (FileMessageFactory) fileFactories.get(msg
+ .getFileName());
+ if (factory == null) {
+ factory = FileMessageFactory.getInstance(writeToFile, true);
+ fileFactories.put(msg.getFileName(), factory);
+ }
+ return factory;
+ }
+
+ /**
+ * Remove file (war) from messages)
+ *
+ * @param msg
+ */
+ public void removeFactory(FileMessage msg) {
+ fileFactories.remove(msg.getFileName());
+ }
+
+ /**
+ * Before the cluster invokes messageReceived the cluster will ask the
+ * receiver to accept or decline the message, In the future, when messages
+ * get big, the accept method will only take a message header
+ *
+ * @param msg
+ * ClusterMessage
+ * @return boolean - returns true to indicate that messageReceived should be
+ * invoked. If false is returned, the messageReceived method will
+ * not be invoked.
+ */
+ public boolean accept(ClusterMessage msg) {
+ return (msg instanceof FileMessage) || (msg instanceof UndeployMessage);
+ }
+
+ /**
+ * Install a new web application, whose web application archive is at the
+ * specified URL, into this container and all the other members of the
+ * cluster with the specified context path. A context path of "" (the empty
+ * string) should be used for the root application for this container.
+ * Otherwise, the context path must start with a slash.
+ * <p>
+ * If this application is successfully installed locally, a ContainerEvent
+ * of type <code>INSTALL_EVENT</code> will be sent to all registered
+ * listeners, with the newly created <code>Context</code> as an argument.
+ *
+ * @param contextPath
+ * The context path to which this application should be installed
+ * (must be unique)
+ * @param war
+ * A URL of type "jar:" that points to a WAR file, or type
+ * "file:" that points to an unpacked directory structure
+ * containing the web application to be installed
+ *
+ * @exception IllegalArgumentException
+ * if the specified context path is malformed (it must be ""
+ * or start with a slash)
+ * @exception IllegalStateException
+ * if the specified context path is already attached to an
+ * existing web application
+ * @exception IOException
+ * if an input/output error was encountered during
+ * installation
+ */
+ public void install(String contextPath, URL war) throws IOException {
+ Member[] members = getCluster().getMembers();
+ Member localMember = getCluster().getLocalMember();
+ FileMessageFactory factory = FileMessageFactory.getInstance(new File(
+ war.getFile()), false);
+ FileMessage msg = new FileMessage(localMember, war.getFile(),
+ contextPath);
+ if(log.isDebugEnabled())
+ log.debug("Send cluster war deployment [ path:"
+ + contextPath + " war: " + war + " ] started.");
+ msg = factory.readMessage(msg);
+ while (msg != null) {
+ for (int i = 0; i < members.length; i++) {
+ if (log.isDebugEnabled())
+ log.debug("Send cluster war fragment [ path: "
+ + contextPath + " war: " + war + " to: " + members[i] + " ]");
+ getCluster().send(msg, members[i]);
+ }
+ msg = factory.readMessage(msg);
+ }
+ if(log.isDebugEnabled())
+ log.debug("Send cluster war deployment [ path: "
+ + contextPath + " war: " + war + " ] finished.");
+ }
+
+ /**
+ * Remove an existing web application, attached to the specified context
+ * path. If this application is successfully removed, a ContainerEvent of
+ * type <code>REMOVE_EVENT</code> will be sent to all registered
+ * listeners, with the removed <code>Context</code> as an argument.
+ * Deletes the web application war file and/or directory if they exist in
+ * the Host's appBase.
+ *
+ * @param contextPath
+ * The context path of the application to be removed
+ * @param undeploy
+ * boolean flag to remove web application from server
+ *
+ * @exception IllegalArgumentException
+ * if the specified context path is malformed (it must be ""
+ * or start with a slash)
+ * @exception IllegalArgumentException
+ * if the specified context path does not identify a
+ * currently installed web application
+ * @exception IOException
+ * if an input/output error occurs during removal
+ */
+ public void remove(String contextPath, boolean undeploy) throws IOException {
+ if (log.isInfoEnabled())
+ log.info("Cluster wide remove of web app " + contextPath);
+ Member localMember = getCluster().getLocalMember();
+ UndeployMessage msg = new UndeployMessage(localMember, System
+ .currentTimeMillis(), "Undeploy:" + contextPath + ":"
+ + System.currentTimeMillis(), contextPath, undeploy);
+ if (log.isDebugEnabled())
+ log.debug("Send cluster wide undeployment from "
+ + contextPath );
+ cluster.send(msg);
+ // remove locally
+ if (undeploy) {
+ try {
+ if (!isServiced(contextPath)) {
+ addServiced(contextPath);
+ try {
+ remove(contextPath);
+ } finally {
+ removeServiced(contextPath);
+ }
+ } else
+ log.error("Local remove from " + contextPath
+ + "failed, other manager has app in service!");
+
+ } catch (Exception ex) {
+ log.error("local remove from " + contextPath + " failed", ex);
+ }
+ }
+
+ }
+
+ /*
+ * Modifcation from watchDir war detected!
+ *
+ * @see org.apache.catalina.ha.deploy.FileChangeListener#fileModified(java.io.File)
+ */
+ public void fileModified(File newWar) {
+ try {
+ File deployWar = new File(getDeployDir(), newWar.getName());
+ copy(newWar, deployWar);
+ String contextName = getContextName(deployWar);
+ if (log.isInfoEnabled())
+ log.info("Installing webapp[" + contextName + "] from "
+ + deployWar.getAbsolutePath());
+ try {
+ remove(contextName, false);
+ } catch (Exception x) {
+ log.error("No removal", x);
+ }
+ install(contextName, deployWar.toURL());
+ } catch (Exception x) {
+ log.error("Unable to install WAR file", x);
+ }
+ }
+
+ /*
+ * War remvoe from watchDir
+ *
+ * @see org.apache.catalina.ha.deploy.FileChangeListener#fileRemoved(java.io.File)
+ */
+ public void fileRemoved(File removeWar) {
+ try {
+ String contextName = getContextName(removeWar);
+ if (log.isInfoEnabled())
+ log.info("Removing webapp[" + contextName + "]");
+ remove(contextName, true);
+ } catch (Exception x) {
+ log.error("Unable to remove WAR file", x);
+ }
+ }
+
+ /**
+ * Create a context path from war
+ * @param war War filename
+ * @return '/filename' or if war name is ROOT.war context name is empty string ''
+ */
+ protected String getContextName(File war) {
+ String contextName = "/"
+ + war.getName().substring(0,
+ war.getName().lastIndexOf(".war"));
+ if("/ROOT".equals(contextName))
+ contextName= "" ;
+ return contextName ;
+ }
+
+ /**
+ * Given a context path, get the config file name.
+ */
+ protected String getConfigFile(String path) {
+ String basename = null;
+ if (path.equals("")) {
+ basename = "ROOT";
+ } else {
+ basename = path.substring(1).replace('/', '#');
+ }
+ return (basename);
+ }
+
+ /**
+ * Given a context path, get the config file name.
+ */
+ protected String getDocBase(String path) {
+ String basename = null;
+ if (path.equals("")) {
+ basename = "ROOT";
+ } else {
+ basename = path.substring(1);
+ }
+ return (basename);
+ }
+
+ /**
+ * Return a File object representing the "application root" directory for
+ * our associated Host.
+ */
+ protected File getAppBase() {
+
+ if (appBase != null) {
+ return appBase;
+ }
+
+ File file = new File(host.getAppBase());
+ if (!file.isAbsolute())
+ file = new File(System.getProperty("catalina.base"), host
+ .getAppBase());
+ try {
+ appBase = file.getCanonicalFile();
+ } catch (IOException e) {
+ appBase = file;
+ }
+ return (appBase);
+
+ }
+
+ /**
+ * Invoke the remove method on the deployer.
+ */
+ protected void remove(String path) throws Exception {
+ // TODO Handle remove also work dir content !
+ // Stop the context first to be nicer
+ Context context = (Context) host.findChild(path);
+ if (context != null) {
+ if(log.isDebugEnabled())
+ log.debug("Undeploy local context " +path );
+ ((Lifecycle) context).stop();
+ File war = new File(getAppBase(), getDocBase(path) + ".war");
+ File dir = new File(getAppBase(), getDocBase(path));
+ File xml = new File(configBase, getConfigFile(path) + ".xml");
+ if (war.exists()) {
+ war.delete();
+ } else if (dir.exists()) {
+ undeployDir(dir);
+ } else {
+ xml.delete();
+ }
+ // Perform new deployment and remove internal HostConfig state
+ check(path);
+ }
+
+ }
+
+ /**
+ * Delete the specified directory, including all of its contents and
+ * subdirectories recursively.
+ *
+ * @param dir
+ * File object representing the directory to be deleted
+ */
+ protected void undeployDir(File dir) {
+
+ String files[] = dir.list();
+ if (files == null) {
+ files = new String[0];
+ }
+ for (int i = 0; i < files.length; i++) {
+ File file = new File(dir, files[i]);
+ if (file.isDirectory()) {
+ undeployDir(file);
+ } else {
+ file.delete();
+ }
+ }
+ dir.delete();
+
+ }
+
+ /*
+ * Call watcher to check for deploy changes
+ *
+ * @see org.apache.catalina.ha.ClusterDeployer#backgroundProcess()
+ */
+ public void backgroundProcess() {
+ if (started) {
+ count = (count + 1) % processDeployFrequency;
+ if (count == 0 && watchEnabled) {
+ watcher.check();
+ }
+ }
+
+ }
+
+ /*--Deployer Operations ------------------------------------*/
+
+ /**
+ * Invoke the check method on the deployer.
+ */
+ protected void check(String name) throws Exception {
+ String[] params = { name };
+ String[] signature = { "java.lang.String" };
+ mBeanServer.invoke(oname, "check", params, signature);
+ }
+
+ /**
+ * Invoke the check method on the deployer.
+ */
+ protected boolean isServiced(String name) throws Exception {
+ String[] params = { name };
+ String[] signature = { "java.lang.String" };
+ Boolean result = (Boolean) mBeanServer.invoke(oname, "isServiced",
+ params, signature);
+ return result.booleanValue();
+ }
+
+ /**
+ * Invoke the check method on the deployer.
+ */
+ protected void addServiced(String name) throws Exception {
+ String[] params = { name };
+ String[] signature = { "java.lang.String" };
+ mBeanServer.invoke(oname, "addServiced", params, signature);
+ }
+
+ /**
+ * Invoke the check method on the deployer.
+ */
+ protected void removeServiced(String name) throws Exception {
+ String[] params = { name };
+ String[] signature = { "java.lang.String" };
+ mBeanServer.invoke(oname, "removeServiced", params, signature);
+ }
+
+ /*--Instance Getters/Setters--------------------------------*/
+ public CatalinaCluster getCluster() {
+ return cluster;
+ }
+
+ public void setCluster(CatalinaCluster cluster) {
+ this.cluster = cluster;
+ }
+
+ public boolean equals(Object listener) {
+ return super.equals(listener);
+ }
+
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ public String getDeployDir() {
+ return deployDir;
+ }
+
+ public void setDeployDir(String deployDir) {
+ this.deployDir = deployDir;
+ }
+
+ public String getTempDir() {
+ return tempDir;
+ }
+
+ public void setTempDir(String tempDir) {
+ this.tempDir = tempDir;
+ }
+
+ public String getWatchDir() {
+ return watchDir;
+ }
+
+ public void setWatchDir(String watchDir) {
+ this.watchDir = watchDir;
+ }
+
+ public boolean isWatchEnabled() {
+ return watchEnabled;
+ }
+
+ public boolean getWatchEnabled() {
+ return watchEnabled;
+ }
+
+ public void setWatchEnabled(boolean watchEnabled) {
+ this.watchEnabled = watchEnabled;
+ }
+
+ /**
+ * Return the frequency of watcher checks.
+ */
+ public int getProcessDeployFrequency() {
+
+ return (this.processDeployFrequency);
+
+ }
+
+ /**
+ * Set the watcher checks frequency.
+ *
+ * @param processExpiresFrequency
+ * the new manager checks frequency
+ */
+ public void setProcessDeployFrequency(int processExpiresFrequency) {
+
+ if (processExpiresFrequency <= 0) {
+ return;
+ }
+ this.processDeployFrequency = processExpiresFrequency;
+ }
+
+ /**
+ * Copy a file to the specified temp directory.
+ * @param from copy from temp
+ * @param to to host appBase directory
+ * @return true, copy successful
+ */
+ protected boolean copy(File from, File to) {
+ try {
+ if (!to.exists())
+ to.createNewFile();
+ java.io.FileInputStream is = new java.io.FileInputStream(from);
+ java.io.FileOutputStream os = new java.io.FileOutputStream(to,
+ false);
+ byte[] buf = new byte[4096];
+ while (true) {
+ int len = is.read(buf);
+ if (len < 0)
+ break;
+ os.write(buf, 0, len);
+ }
+ is.close();
+ os.close();
+ } catch (IOException e) {
+ log.error("Unable to copy file from:" + from + " to:" + to, e);
+ return false;
+ }
+ return true;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.deploy;\r
-import java.io.File;\r
-\r
-public interface FileChangeListener {\r
- public void fileModified(File f);\r
- public void fileRemoved(File f);\r
-}\r
+/*
+ * 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.apache.catalina.ha.deploy;
+import java.io.File;
+
+public interface FileChangeListener {
+ public void fileModified(File f);
+ public void fileRemoved(File f);
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.deploy;\r
-\r
-import java.io.Serializable;\r
-\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.ha.ClusterMessageBase;\r
-\r
-/**\r
- * Contains the data for a file being transferred over TCP, this is \r
- * essentially a fragment of a file, read and written by the FileMessageFactory\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-\r
-public class FileMessage extends ClusterMessageBase implements ClusterMessage, Serializable {\r
- private int messageNumber;\r
- private byte[] data;\r
- private int dataLength;\r
- \r
- private long totalLength;\r
- private long totalNrOfMsgs;\r
- private String fileName;\r
- private String contextPath;\r
- \r
- public FileMessage(Member source,\r
- String fileName,\r
- String contextPath) {\r
- this.address=source;\r
- this.fileName=fileName;\r
- this.contextPath=contextPath;\r
- }\r
- \r
- /*\r
- public void writeExternal(ObjectOutput out) throws IOException {\r
- \r
- }\r
- \r
- public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {\r
- \r
- }\r
- */\r
- \r
- public int getMessageNumber() {\r
- return messageNumber;\r
- }\r
- public void setMessageNumber(int messageNumber) {\r
- this.messageNumber = messageNumber;\r
- }\r
- public long getTotalNrOfMsgs() {\r
- return totalNrOfMsgs;\r
- }\r
- public void setTotalNrOfMsgs(long totalNrOfMsgs) {\r
- this.totalNrOfMsgs = totalNrOfMsgs;\r
- }\r
- public byte[] getData() {\r
- return data;\r
- }\r
- public void setData(byte[] data, int length) {\r
- this.data = data;\r
- this.dataLength = length;\r
- }\r
- public int getDataLength() {\r
- return dataLength;\r
- }\r
- public void setDataLength(int dataLength) {\r
- this.dataLength = dataLength;\r
- }\r
- public long getTotalLength() {\r
- return totalLength;\r
- }\r
- public void setTotalLength(long totalLength) {\r
- this.totalLength = totalLength;\r
- }\r
-\r
- public String getUniqueId() {\r
- StringBuffer result = new StringBuffer(getFileName());\r
- result.append("#-#");\r
- result.append(getMessageNumber());\r
- result.append("#-#");\r
- result.append(System.currentTimeMillis());\r
- return result.toString();\r
- }\r
-\r
- \r
- public String getFileName() {\r
- return fileName;\r
- }\r
- public void setFileName(String fileName) {\r
- this.fileName = fileName;\r
- }\r
- public String getContextPath() {\r
- return contextPath;\r
- }\r
- \r
-}\r
+/*
+ * 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.apache.catalina.ha.deploy;
+
+import java.io.Serializable;
+
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.ha.ClusterMessageBase;
+
+/**
+ * Contains the data for a file being transferred over TCP, this is
+ * essentially a fragment of a file, read and written by the FileMessageFactory
+ * @author Filip Hanik
+ * @version 1.0
+ */
+
+public class FileMessage extends ClusterMessageBase implements ClusterMessage, Serializable {
+ private int messageNumber;
+ private byte[] data;
+ private int dataLength;
+
+ private long totalLength;
+ private long totalNrOfMsgs;
+ private String fileName;
+ private String contextPath;
+
+ public FileMessage(Member source,
+ String fileName,
+ String contextPath) {
+ this.address=source;
+ this.fileName=fileName;
+ this.contextPath=contextPath;
+ }
+
+ /*
+ public void writeExternal(ObjectOutput out) throws IOException {
+
+ }
+
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+
+ }
+ */
+
+ public int getMessageNumber() {
+ return messageNumber;
+ }
+ public void setMessageNumber(int messageNumber) {
+ this.messageNumber = messageNumber;
+ }
+ public long getTotalNrOfMsgs() {
+ return totalNrOfMsgs;
+ }
+ public void setTotalNrOfMsgs(long totalNrOfMsgs) {
+ this.totalNrOfMsgs = totalNrOfMsgs;
+ }
+ public byte[] getData() {
+ return data;
+ }
+ public void setData(byte[] data, int length) {
+ this.data = data;
+ this.dataLength = length;
+ }
+ public int getDataLength() {
+ return dataLength;
+ }
+ public void setDataLength(int dataLength) {
+ this.dataLength = dataLength;
+ }
+ public long getTotalLength() {
+ return totalLength;
+ }
+ public void setTotalLength(long totalLength) {
+ this.totalLength = totalLength;
+ }
+
+ public String getUniqueId() {
+ StringBuffer result = new StringBuffer(getFileName());
+ result.append("#-#");
+ result.append(getMessageNumber());
+ result.append("#-#");
+ result.append(System.currentTimeMillis());
+ return result.toString();
+ }
+
+
+ public String getFileName() {
+ return fileName;
+ }
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+ public String getContextPath() {
+ return contextPath;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.deploy;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.io.FileInputStream;\r
-import java.io.FileOutputStream;\r
-import java.io.FileNotFoundException;\r
-\r
-/**\r
- * This factory is used to read files and write files by splitting them up into\r
- * smaller messages. So that entire files don't have to be read into memory.\r
- * <BR>\r
- * The factory can be used as a reader or writer but not both at the same time.\r
- * When done reading or writing the factory will close the input or output\r
- * streams and mark the factory as closed. It is not possible to use it after\r
- * that. <BR>\r
- * To force a cleanup, call cleanup() from the calling object. <BR>\r
- * This class is not thread safe.\r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class FileMessageFactory {\r
- /*--Static Variables----------------------------------------*/\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory\r
- .getLog(FileMessageFactory.class);\r
-\r
- /**\r
- * The number of bytes that we read from file\r
- */\r
- public static final int READ_SIZE = 1024 * 10; //10kb\r
-\r
- /**\r
- * The file that we are reading/writing\r
- */\r
- protected File file = null;\r
-\r
- /**\r
- * True means that we are writing with this factory. False means that we are\r
- * reading with this factory\r
- */\r
- protected boolean openForWrite;\r
-\r
- /**\r
- * Once the factory is used, it can not be reused.\r
- */\r
- protected boolean closed = false;\r
-\r
- /**\r
- * When openForWrite=false, the input stream is held by this variable\r
- */\r
- protected FileInputStream in;\r
-\r
- /**\r
- * When openForWrite=true, the output stream is held by this variable\r
- */\r
- protected FileOutputStream out;\r
-\r
- /**\r
- * The number of messages we have read or written\r
- */\r
- protected int nrOfMessagesProcessed = 0;\r
-\r
- /**\r
- * The total size of the file\r
- */\r
- protected long size = 0;\r
-\r
- /**\r
- * The total number of packets that we split this file into\r
- */\r
- protected long totalNrOfMessages = 0;\r
-\r
- /**\r
- * The bytes that we hold the data in, not thread safe.\r
- */\r
- protected byte[] data = new byte[READ_SIZE];\r
-\r
- /**\r
- * Private constructor, either instantiates a factory to read or write. <BR>\r
- * When openForWrite==true, then a the file, f, will be created and an\r
- * output stream is opened to write to it. <BR>\r
- * When openForWrite==false, an input stream is opened, the file has to\r
- * exist.\r
- * \r
- * @param f\r
- * File - the file to be read/written\r
- * @param openForWrite\r
- * boolean - true means we are writing to the file, false means\r
- * we are reading from the file\r
- * @throws FileNotFoundException -\r
- * if the file to be read doesn't exist\r
- * @throws IOException -\r
- * if the system fails to open input/output streams to the file\r
- * or if it fails to create the file to be written to.\r
- */\r
- private FileMessageFactory(File f, boolean openForWrite)\r
- throws FileNotFoundException, IOException {\r
- this.file = f;\r
- this.openForWrite = openForWrite;\r
- if (log.isDebugEnabled())\r
- log.debug("open file " + f + " write " + openForWrite);\r
- if (openForWrite) {\r
- if (!file.exists())\r
- file.createNewFile();\r
- out = new FileOutputStream(f);\r
- } else {\r
- size = file.length();\r
- totalNrOfMessages = (size / READ_SIZE) + 1;\r
- in = new FileInputStream(f);\r
- }//end if\r
-\r
- }\r
-\r
- /**\r
- * Creates a factory to read or write from a file. When opening for read,\r
- * the readMessage can be invoked, and when opening for write the\r
- * writeMessage can be invoked.\r
- * \r
- * @param f\r
- * File - the file to be read or written\r
- * @param openForWrite\r
- * boolean - true, means we are writing to the file, false means\r
- * we are reading from it\r
- * @throws FileNotFoundException -\r
- * if the file to be read doesn't exist\r
- * @throws IOException -\r
- * if it fails to create the file that is to be written\r
- * @return FileMessageFactory\r
- */\r
- public static FileMessageFactory getInstance(File f, boolean openForWrite)\r
- throws FileNotFoundException, IOException {\r
- return new FileMessageFactory(f, openForWrite);\r
- }\r
-\r
- /**\r
- * Reads file data into the file message and sets the size, totalLength,\r
- * totalNrOfMsgs and the message number <BR>\r
- * If EOF is reached, the factory returns null, and closes itself, otherwise\r
- * the same message is returned as was passed in. This makes sure that not\r
- * more memory is ever used. To remember, neither the file message or the\r
- * factory are thread safe. dont hand off the message to one thread and read\r
- * the same with another.\r
- * \r
- * @param f\r
- * FileMessage - the message to be populated with file data\r
- * @throws IllegalArgumentException -\r
- * if the factory is for writing or is closed\r
- * @throws IOException -\r
- * if a file read exception occurs\r
- * @return FileMessage - returns the same message passed in as a parameter,\r
- * or null if EOF\r
- */\r
- public FileMessage readMessage(FileMessage f)\r
- throws IllegalArgumentException, IOException {\r
- checkState(false);\r
- int length = in.read(data);\r
- if (length == -1) {\r
- cleanup();\r
- return null;\r
- } else {\r
- f.setData(data, length);\r
- f.setTotalLength(size);\r
- f.setTotalNrOfMsgs(totalNrOfMessages);\r
- f.setMessageNumber(++nrOfMessagesProcessed);\r
- return f;\r
- }//end if\r
- }\r
-\r
- /**\r
- * Writes a message to file. If (msg.getMessageNumber() ==\r
- * msg.getTotalNrOfMsgs()) the output stream will be closed after writing.\r
- * \r
- * @param msg\r
- * FileMessage - message containing data to be written\r
- * @throws IllegalArgumentException -\r
- * if the factory is opened for read or closed\r
- * @throws IOException -\r
- * if a file write error occurs\r
- * @return returns true if the file is complete and outputstream is closed,\r
- * false otherwise.\r
- */\r
- public boolean writeMessage(FileMessage msg)\r
- throws IllegalArgumentException, IOException {\r
- if (!openForWrite)\r
- throw new IllegalArgumentException(\r
- "Can't write message, this factory is reading.");\r
- if (log.isDebugEnabled())\r
- log.debug("Message " + msg + " data " + msg.getData()\r
- + " data length " + msg.getDataLength() + " out " + out);\r
- if (out != null) {\r
- out.write(msg.getData(), 0, msg.getDataLength());\r
- nrOfMessagesProcessed++;\r
- out.flush();\r
- if (msg.getMessageNumber() == msg.getTotalNrOfMsgs()) {\r
- out.close();\r
- cleanup();\r
- return true;\r
- }//end if\r
- } else {\r
- if (log.isWarnEnabled())\r
- log.warn("Receive Message again -- Sender ActTimeout to short [ path: "\r
- + msg.getContextPath()\r
- + " war: "\r
- + msg.getFileName()\r
- + " data: "\r
- + msg.getData()\r
- + " data length: " + msg.getDataLength() + " ]");\r
- }\r
- return false;\r
- }//writeMessage\r
-\r
- /**\r
- * Closes the factory, its streams and sets all its references to null\r
- */\r
- public void cleanup() {\r
- if (in != null)\r
- try {\r
- in.close();\r
- } catch (Exception ignore) {\r
- }\r
- if (out != null)\r
- try {\r
- out.close();\r
- } catch (Exception ignore) {\r
- }\r
- in = null;\r
- out = null;\r
- size = 0;\r
- closed = true;\r
- data = null;\r
- nrOfMessagesProcessed = 0;\r
- totalNrOfMessages = 0;\r
- }\r
-\r
- /**\r
- * Check to make sure the factory is able to perform the function it is\r
- * asked to do. Invoked by readMessage/writeMessage before those methods\r
- * proceed.\r
- * \r
- * @param openForWrite\r
- * boolean\r
- * @throws IllegalArgumentException\r
- */\r
- protected void checkState(boolean openForWrite)\r
- throws IllegalArgumentException {\r
- if (this.openForWrite != openForWrite) {\r
- cleanup();\r
- if (openForWrite)\r
- throw new IllegalArgumentException(\r
- "Can't write message, this factory is reading.");\r
- else\r
- throw new IllegalArgumentException(\r
- "Can't read message, this factory is writing.");\r
- }\r
- if (this.closed) {\r
- cleanup();\r
- throw new IllegalArgumentException("Factory has been closed.");\r
- }\r
- }\r
-\r
- /**\r
- * Example usage.\r
- * \r
- * @param args\r
- * String[], args[0] - read from filename, args[1] write to\r
- * filename\r
- * @throws Exception\r
- */\r
- public static void main(String[] args) throws Exception {\r
-\r
- System.out\r
- .println("Usage: FileMessageFactory fileToBeRead fileToBeWritten");\r
- System.out\r
- .println("Usage: This will make a copy of the file on the local file system");\r
- FileMessageFactory read = getInstance(new File(args[0]), false);\r
- FileMessageFactory write = getInstance(new File(args[1]), true);\r
- FileMessage msg = new FileMessage(null, args[0], args[0]);\r
- msg = read.readMessage(msg);\r
- System.out.println("Expecting to write " + msg.getTotalNrOfMsgs()\r
- + " messages.");\r
- int cnt = 0;\r
- while (msg != null) {\r
- write.writeMessage(msg);\r
- cnt++;\r
- msg = read.readMessage(msg);\r
- }//while\r
- System.out.println("Actually wrote " + cnt + " messages.");\r
- }///main\r
-\r
- public File getFile() {\r
- return file;\r
- }\r
-\r
+/*
+ * 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.apache.catalina.ha.deploy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+
+/**
+ * This factory is used to read files and write files by splitting them up into
+ * smaller messages. So that entire files don't have to be read into memory.
+ * <BR>
+ * The factory can be used as a reader or writer but not both at the same time.
+ * When done reading or writing the factory will close the input or output
+ * streams and mark the factory as closed. It is not possible to use it after
+ * that. <BR>
+ * To force a cleanup, call cleanup() from the calling object. <BR>
+ * This class is not thread safe.
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class FileMessageFactory {
+ /*--Static Variables----------------------------------------*/
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+ .getLog(FileMessageFactory.class);
+
+ /**
+ * The number of bytes that we read from file
+ */
+ public static final int READ_SIZE = 1024 * 10; //10kb
+
+ /**
+ * The file that we are reading/writing
+ */
+ protected File file = null;
+
+ /**
+ * True means that we are writing with this factory. False means that we are
+ * reading with this factory
+ */
+ protected boolean openForWrite;
+
+ /**
+ * Once the factory is used, it can not be reused.
+ */
+ protected boolean closed = false;
+
+ /**
+ * When openForWrite=false, the input stream is held by this variable
+ */
+ protected FileInputStream in;
+
+ /**
+ * When openForWrite=true, the output stream is held by this variable
+ */
+ protected FileOutputStream out;
+
+ /**
+ * The number of messages we have read or written
+ */
+ protected int nrOfMessagesProcessed = 0;
+
+ /**
+ * The total size of the file
+ */
+ protected long size = 0;
+
+ /**
+ * The total number of packets that we split this file into
+ */
+ protected long totalNrOfMessages = 0;
+
+ /**
+ * The bytes that we hold the data in, not thread safe.
+ */
+ protected byte[] data = new byte[READ_SIZE];
+
+ /**
+ * Private constructor, either instantiates a factory to read or write. <BR>
+ * When openForWrite==true, then a the file, f, will be created and an
+ * output stream is opened to write to it. <BR>
+ * When openForWrite==false, an input stream is opened, the file has to
+ * exist.
+ *
+ * @param f
+ * File - the file to be read/written
+ * @param openForWrite
+ * boolean - true means we are writing to the file, false means
+ * we are reading from the file
+ * @throws FileNotFoundException -
+ * if the file to be read doesn't exist
+ * @throws IOException -
+ * if the system fails to open input/output streams to the file
+ * or if it fails to create the file to be written to.
+ */
+ private FileMessageFactory(File f, boolean openForWrite)
+ throws FileNotFoundException, IOException {
+ this.file = f;
+ this.openForWrite = openForWrite;
+ if (log.isDebugEnabled())
+ log.debug("open file " + f + " write " + openForWrite);
+ if (openForWrite) {
+ if (!file.exists())
+ file.createNewFile();
+ out = new FileOutputStream(f);
+ } else {
+ size = file.length();
+ totalNrOfMessages = (size / READ_SIZE) + 1;
+ in = new FileInputStream(f);
+ }//end if
+
+ }
+
+ /**
+ * Creates a factory to read or write from a file. When opening for read,
+ * the readMessage can be invoked, and when opening for write the
+ * writeMessage can be invoked.
+ *
+ * @param f
+ * File - the file to be read or written
+ * @param openForWrite
+ * boolean - true, means we are writing to the file, false means
+ * we are reading from it
+ * @throws FileNotFoundException -
+ * if the file to be read doesn't exist
+ * @throws IOException -
+ * if it fails to create the file that is to be written
+ * @return FileMessageFactory
+ */
+ public static FileMessageFactory getInstance(File f, boolean openForWrite)
+ throws FileNotFoundException, IOException {
+ return new FileMessageFactory(f, openForWrite);
+ }
+
+ /**
+ * Reads file data into the file message and sets the size, totalLength,
+ * totalNrOfMsgs and the message number <BR>
+ * If EOF is reached, the factory returns null, and closes itself, otherwise
+ * the same message is returned as was passed in. This makes sure that not
+ * more memory is ever used. To remember, neither the file message or the
+ * factory are thread safe. dont hand off the message to one thread and read
+ * the same with another.
+ *
+ * @param f
+ * FileMessage - the message to be populated with file data
+ * @throws IllegalArgumentException -
+ * if the factory is for writing or is closed
+ * @throws IOException -
+ * if a file read exception occurs
+ * @return FileMessage - returns the same message passed in as a parameter,
+ * or null if EOF
+ */
+ public FileMessage readMessage(FileMessage f)
+ throws IllegalArgumentException, IOException {
+ checkState(false);
+ int length = in.read(data);
+ if (length == -1) {
+ cleanup();
+ return null;
+ } else {
+ f.setData(data, length);
+ f.setTotalLength(size);
+ f.setTotalNrOfMsgs(totalNrOfMessages);
+ f.setMessageNumber(++nrOfMessagesProcessed);
+ return f;
+ }//end if
+ }
+
+ /**
+ * Writes a message to file. If (msg.getMessageNumber() ==
+ * msg.getTotalNrOfMsgs()) the output stream will be closed after writing.
+ *
+ * @param msg
+ * FileMessage - message containing data to be written
+ * @throws IllegalArgumentException -
+ * if the factory is opened for read or closed
+ * @throws IOException -
+ * if a file write error occurs
+ * @return returns true if the file is complete and outputstream is closed,
+ * false otherwise.
+ */
+ public boolean writeMessage(FileMessage msg)
+ throws IllegalArgumentException, IOException {
+ if (!openForWrite)
+ throw new IllegalArgumentException(
+ "Can't write message, this factory is reading.");
+ if (log.isDebugEnabled())
+ log.debug("Message " + msg + " data " + msg.getData()
+ + " data length " + msg.getDataLength() + " out " + out);
+ if (out != null) {
+ out.write(msg.getData(), 0, msg.getDataLength());
+ nrOfMessagesProcessed++;
+ out.flush();
+ if (msg.getMessageNumber() == msg.getTotalNrOfMsgs()) {
+ out.close();
+ cleanup();
+ return true;
+ }//end if
+ } else {
+ if (log.isWarnEnabled())
+ log.warn("Receive Message again -- Sender ActTimeout to short [ path: "
+ + msg.getContextPath()
+ + " war: "
+ + msg.getFileName()
+ + " data: "
+ + msg.getData()
+ + " data length: " + msg.getDataLength() + " ]");
+ }
+ return false;
+ }//writeMessage
+
+ /**
+ * Closes the factory, its streams and sets all its references to null
+ */
+ public void cleanup() {
+ if (in != null)
+ try {
+ in.close();
+ } catch (Exception ignore) {
+ }
+ if (out != null)
+ try {
+ out.close();
+ } catch (Exception ignore) {
+ }
+ in = null;
+ out = null;
+ size = 0;
+ closed = true;
+ data = null;
+ nrOfMessagesProcessed = 0;
+ totalNrOfMessages = 0;
+ }
+
+ /**
+ * Check to make sure the factory is able to perform the function it is
+ * asked to do. Invoked by readMessage/writeMessage before those methods
+ * proceed.
+ *
+ * @param openForWrite
+ * boolean
+ * @throws IllegalArgumentException
+ */
+ protected void checkState(boolean openForWrite)
+ throws IllegalArgumentException {
+ if (this.openForWrite != openForWrite) {
+ cleanup();
+ if (openForWrite)
+ throw new IllegalArgumentException(
+ "Can't write message, this factory is reading.");
+ else
+ throw new IllegalArgumentException(
+ "Can't read message, this factory is writing.");
+ }
+ if (this.closed) {
+ cleanup();
+ throw new IllegalArgumentException("Factory has been closed.");
+ }
+ }
+
+ /**
+ * Example usage.
+ *
+ * @param args
+ * String[], args[0] - read from filename, args[1] write to
+ * filename
+ * @throws Exception
+ */
+ public static void main(String[] args) throws Exception {
+
+ System.out
+ .println("Usage: FileMessageFactory fileToBeRead fileToBeWritten");
+ System.out
+ .println("Usage: This will make a copy of the file on the local file system");
+ FileMessageFactory read = getInstance(new File(args[0]), false);
+ FileMessageFactory write = getInstance(new File(args[1]), true);
+ FileMessage msg = new FileMessage(null, args[0], args[0]);
+ msg = read.readMessage(msg);
+ System.out.println("Expecting to write " + msg.getTotalNrOfMsgs()
+ + " messages.");
+ int cnt = 0;
+ while (msg != null) {
+ write.writeMessage(msg);
+ cnt++;
+ msg = read.readMessage(msg);
+ }//while
+ System.out.println("Actually wrote " + cnt + " messages.");
+ }///main
+
+ public File getFile() {
+ return file;
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.deploy;\r
-\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import java.io.Serializable;\r
-public class UndeployMessage implements ClusterMessage,Serializable {\r
- private Member address;\r
- private long timestamp;\r
- private String uniqueId;\r
- private String contextPath;\r
- private boolean undeploy;\r
- private int resend = 0;\r
- private int compress = 0;\r
-\r
- public UndeployMessage() {} //for serialization\r
- public UndeployMessage(Member address,\r
- long timestamp,\r
- String uniqueId,\r
- String contextPath,\r
- boolean undeploy) {\r
- this.address = address;\r
- this.timestamp= timestamp;\r
- this.undeploy = undeploy;\r
- this.uniqueId = uniqueId;\r
- this.undeploy = undeploy;\r
- this.contextPath = contextPath;\r
- }\r
-\r
- public Member getAddress() {\r
- return address;\r
- }\r
-\r
- public void setAddress(Member address) {\r
- this.address = address;\r
- }\r
-\r
- public long getTimestamp() {\r
- return timestamp;\r
- }\r
-\r
- public void setTimestamp(long timestamp) {\r
- this.timestamp = timestamp;\r
- }\r
-\r
- public String getUniqueId() {\r
- return uniqueId;\r
- }\r
-\r
- public void setUniqueId(String uniqueId) {\r
- this.uniqueId = uniqueId;\r
- }\r
-\r
- public String getContextPath() {\r
- return contextPath;\r
- }\r
-\r
- public void setContextPath(String contextPath) {\r
- this.contextPath = contextPath;\r
- }\r
-\r
- public boolean getUndeploy() {\r
- return undeploy;\r
- }\r
-\r
- public void setUndeploy(boolean undeploy) {\r
- this.undeploy = undeploy;\r
- }\r
- /**\r
- * @return Returns the compress.\r
- * @since 5.5.10 \r
- */\r
- public int getCompress() {\r
- return compress;\r
- }\r
- /**\r
- * @param compress The compress to set.\r
- * @since 5.5.10\r
- */\r
- public void setCompress(int compress) {\r
- this.compress = compress;\r
- }\r
- /**\r
- * @return Returns the resend.\r
- * @since 5.5.10\r
- */\r
- public int getResend() {\r
- return resend;\r
- }\r
- /**\r
- * @param resend The resend to set.\r
- * @since 5.5.10\r
- */\r
- public void setResend(int resend) {\r
- this.resend = resend;\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.deploy;
+
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.tribes.Member;
+import java.io.Serializable;
+public class UndeployMessage implements ClusterMessage,Serializable {
+ private Member address;
+ private long timestamp;
+ private String uniqueId;
+ private String contextPath;
+ private boolean undeploy;
+ private int resend = 0;
+ private int compress = 0;
+
+ public UndeployMessage() {} //for serialization
+ public UndeployMessage(Member address,
+ long timestamp,
+ String uniqueId,
+ String contextPath,
+ boolean undeploy) {
+ this.address = address;
+ this.timestamp= timestamp;
+ this.undeploy = undeploy;
+ this.uniqueId = uniqueId;
+ this.undeploy = undeploy;
+ this.contextPath = contextPath;
+ }
+
+ public Member getAddress() {
+ return address;
+ }
+
+ public void setAddress(Member address) {
+ this.address = address;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getContextPath() {
+ return contextPath;
+ }
+
+ public void setContextPath(String contextPath) {
+ this.contextPath = contextPath;
+ }
+
+ public boolean getUndeploy() {
+ return undeploy;
+ }
+
+ public void setUndeploy(boolean undeploy) {
+ this.undeploy = undeploy;
+ }
+ /**
+ * @return Returns the compress.
+ * @since 5.5.10
+ */
+ public int getCompress() {
+ return compress;
+ }
+ /**
+ * @param compress The compress to set.
+ * @since 5.5.10
+ */
+ public void setCompress(int compress) {
+ this.compress = compress;
+ }
+ /**
+ * @return Returns the resend.
+ * @since 5.5.10
+ */
+ public int getResend() {
+ return resend;
+ }
+ /**
+ * @param resend The resend to set.
+ * @since 5.5.10
+ */
+ public void setResend(int resend) {
+ this.resend = resend;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.deploy;\r
-\r
-import java.io.File;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.Iterator;\r
-\r
-/**\r
- * <p>\r
- * The <b>WarWatcher </b> watches the deployDir for changes made to the\r
- * directory (adding new WAR files->deploy or remove WAR files->undeploy) And\r
- * notifies a listener of the changes made\r
- * </p>\r
- * \r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * @version 1.1\r
- */\r
-\r
-public class WarWatcher {\r
-\r
- /*--Static Variables----------------------------------------*/\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory\r
- .getLog(WarWatcher.class);\r
-\r
- /*--Instance Variables--------------------------------------*/\r
- /**\r
- * Directory to watch for war files\r
- */\r
- protected File watchDir = null;\r
-\r
- /**\r
- * Parent to be notified of changes\r
- */\r
- protected FileChangeListener listener = null;\r
-\r
- /**\r
- * Currently deployed files\r
- */\r
- protected Map currentStatus = new HashMap();\r
-\r
- /*--Constructor---------------------------------------------*/\r
-\r
- public WarWatcher() {\r
- }\r
-\r
- public WarWatcher(FileChangeListener listener, File watchDir) {\r
- this.listener = listener;\r
- this.watchDir = watchDir;\r
- }\r
-\r
- /*--Logic---------------------------------------------------*/\r
-\r
- /**\r
- * check for modification and send notifcation to listener\r
- */\r
- public void check() {\r
- if (log.isInfoEnabled())\r
- log.info("check cluster wars at " + watchDir);\r
- File[] list = watchDir.listFiles(new WarFilter());\r
- if (list == null)\r
- list = new File[0];\r
- //first make sure all the files are listed in our current status\r
- for (int i = 0; i < list.length; i++) {\r
- addWarInfo(list[i]);\r
- }\r
-\r
- //check all the status codes and update the FarmDeployer\r
- for (Iterator i = currentStatus.entrySet().iterator(); i.hasNext();) {\r
- Map.Entry entry = (Map.Entry) i.next();\r
- WarInfo info = (WarInfo) entry.getValue();\r
- int check = info.check();\r
- if (check == 1) {\r
- listener.fileModified(info.getWar());\r
- } else if (check == -1) {\r
- listener.fileRemoved(info.getWar());\r
- //no need to keep in memory\r
- currentStatus.remove(info.getWar());\r
- }\r
- }\r
-\r
- }\r
-\r
- /**\r
- * add cluster war to the watcher state\r
- * @param warfile\r
- */\r
- protected void addWarInfo(File warfile) {\r
- WarInfo info = (WarInfo) currentStatus.get(warfile.getAbsolutePath());\r
- if (info == null) {\r
- info = new WarInfo(warfile);\r
- info.setLastState(-1); //assume file is non existent\r
- currentStatus.put(warfile.getAbsolutePath(), info);\r
- }\r
- }\r
-\r
- /**\r
- * clear watcher state\r
- */\r
- public void clear() {\r
- currentStatus.clear();\r
- }\r
-\r
- /**\r
- * @return Returns the watchDir.\r
- */\r
- public File getWatchDir() {\r
- return watchDir;\r
- }\r
-\r
- /**\r
- * @param watchDir\r
- * The watchDir to set.\r
- */\r
- public void setWatchDir(File watchDir) {\r
- this.watchDir = watchDir;\r
- }\r
-\r
- /**\r
- * @return Returns the listener.\r
- */\r
- public FileChangeListener getListener() {\r
- return listener;\r
- }\r
-\r
- /**\r
- * @param listener\r
- * The listener to set.\r
- */\r
- public void setListener(FileChangeListener listener) {\r
- this.listener = listener;\r
- }\r
-\r
- /*--Inner classes-------------------------------------------*/\r
-\r
- /**\r
- * File name filter for war files\r
- */\r
- protected class WarFilter implements java.io.FilenameFilter {\r
- public boolean accept(File path, String name) {\r
- if (name == null)\r
- return false;\r
- return name.endsWith(".war");\r
- }\r
- }\r
-\r
- /**\r
- * File information on existing WAR files\r
- */\r
- protected class WarInfo {\r
- protected File war = null;\r
-\r
- protected long lastChecked = 0;\r
-\r
- protected long lastState = 0;\r
-\r
- public WarInfo(File war) {\r
- this.war = war;\r
- this.lastChecked = war.lastModified();\r
- if (!war.exists())\r
- lastState = -1;\r
- }\r
-\r
- public boolean modified() {\r
- return war.exists() && war.lastModified() > lastChecked;\r
- }\r
-\r
- public boolean exists() {\r
- return war.exists();\r
- }\r
-\r
- /**\r
- * Returns 1 if the file has been added/modified, 0 if the file is\r
- * unchanged and -1 if the file has been removed\r
- * \r
- * @return int 1=file added; 0=unchanged; -1=file removed\r
- */\r
- public int check() {\r
- //file unchanged by default\r
- int result = 0;\r
-\r
- if (modified()) {\r
- //file has changed - timestamp\r
- result = 1;\r
- lastState = result;\r
- } else if ((!exists()) && (!(lastState == -1))) {\r
- //file was removed\r
- result = -1;\r
- lastState = result;\r
- } else if ((lastState == -1) && exists()) {\r
- //file was added\r
- result = 1;\r
- lastState = result;\r
- }\r
- this.lastChecked = System.currentTimeMillis();\r
- return result;\r
- }\r
-\r
- public File getWar() {\r
- return war;\r
- }\r
-\r
- public int hashCode() {\r
- return war.getAbsolutePath().hashCode();\r
- }\r
-\r
- public boolean equals(Object other) {\r
- if (other instanceof WarInfo) {\r
- WarInfo wo = (WarInfo) other;\r
- return wo.getWar().equals(getWar());\r
- } else {\r
- return false;\r
- }\r
- }\r
-\r
- protected void setLastState(int lastState) {\r
- this.lastState = lastState;\r
- }\r
-\r
- }\r
-\r
+/*
+ * 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.apache.catalina.ha.deploy;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * <p>
+ * The <b>WarWatcher </b> watches the deployDir for changes made to the
+ * directory (adding new WAR files->deploy or remove WAR files->undeploy) And
+ * notifies a listener of the changes made
+ * </p>
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version 1.1
+ */
+
+public class WarWatcher {
+
+ /*--Static Variables----------------------------------------*/
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+ .getLog(WarWatcher.class);
+
+ /*--Instance Variables--------------------------------------*/
+ /**
+ * Directory to watch for war files
+ */
+ protected File watchDir = null;
+
+ /**
+ * Parent to be notified of changes
+ */
+ protected FileChangeListener listener = null;
+
+ /**
+ * Currently deployed files
+ */
+ protected Map currentStatus = new HashMap();
+
+ /*--Constructor---------------------------------------------*/
+
+ public WarWatcher() {
+ }
+
+ public WarWatcher(FileChangeListener listener, File watchDir) {
+ this.listener = listener;
+ this.watchDir = watchDir;
+ }
+
+ /*--Logic---------------------------------------------------*/
+
+ /**
+ * check for modification and send notifcation to listener
+ */
+ public void check() {
+ if (log.isInfoEnabled())
+ log.info("check cluster wars at " + watchDir);
+ File[] list = watchDir.listFiles(new WarFilter());
+ if (list == null)
+ list = new File[0];
+ //first make sure all the files are listed in our current status
+ for (int i = 0; i < list.length; i++) {
+ addWarInfo(list[i]);
+ }
+
+ //check all the status codes and update the FarmDeployer
+ for (Iterator i = currentStatus.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry) i.next();
+ WarInfo info = (WarInfo) entry.getValue();
+ int check = info.check();
+ if (check == 1) {
+ listener.fileModified(info.getWar());
+ } else if (check == -1) {
+ listener.fileRemoved(info.getWar());
+ //no need to keep in memory
+ currentStatus.remove(info.getWar());
+ }
+ }
+
+ }
+
+ /**
+ * add cluster war to the watcher state
+ * @param warfile
+ */
+ protected void addWarInfo(File warfile) {
+ WarInfo info = (WarInfo) currentStatus.get(warfile.getAbsolutePath());
+ if (info == null) {
+ info = new WarInfo(warfile);
+ info.setLastState(-1); //assume file is non existent
+ currentStatus.put(warfile.getAbsolutePath(), info);
+ }
+ }
+
+ /**
+ * clear watcher state
+ */
+ public void clear() {
+ currentStatus.clear();
+ }
+
+ /**
+ * @return Returns the watchDir.
+ */
+ public File getWatchDir() {
+ return watchDir;
+ }
+
+ /**
+ * @param watchDir
+ * The watchDir to set.
+ */
+ public void setWatchDir(File watchDir) {
+ this.watchDir = watchDir;
+ }
+
+ /**
+ * @return Returns the listener.
+ */
+ public FileChangeListener getListener() {
+ return listener;
+ }
+
+ /**
+ * @param listener
+ * The listener to set.
+ */
+ public void setListener(FileChangeListener listener) {
+ this.listener = listener;
+ }
+
+ /*--Inner classes-------------------------------------------*/
+
+ /**
+ * File name filter for war files
+ */
+ protected class WarFilter implements java.io.FilenameFilter {
+ public boolean accept(File path, String name) {
+ if (name == null)
+ return false;
+ return name.endsWith(".war");
+ }
+ }
+
+ /**
+ * File information on existing WAR files
+ */
+ protected class WarInfo {
+ protected File war = null;
+
+ protected long lastChecked = 0;
+
+ protected long lastState = 0;
+
+ public WarInfo(File war) {
+ this.war = war;
+ this.lastChecked = war.lastModified();
+ if (!war.exists())
+ lastState = -1;
+ }
+
+ public boolean modified() {
+ return war.exists() && war.lastModified() > lastChecked;
+ }
+
+ public boolean exists() {
+ return war.exists();
+ }
+
+ /**
+ * Returns 1 if the file has been added/modified, 0 if the file is
+ * unchanged and -1 if the file has been removed
+ *
+ * @return int 1=file added; 0=unchanged; -1=file removed
+ */
+ public int check() {
+ //file unchanged by default
+ int result = 0;
+
+ if (modified()) {
+ //file has changed - timestamp
+ result = 1;
+ lastState = result;
+ } else if ((!exists()) && (!(lastState == -1))) {
+ //file was removed
+ result = -1;
+ lastState = result;
+ } else if ((lastState == -1) && exists()) {
+ //file was added
+ result = 1;
+ lastState = result;
+ }
+ this.lastChecked = System.currentTimeMillis();
+ return result;
+ }
+
+ public File getWar() {
+ return war;
+ }
+
+ public int hashCode() {
+ return war.getAbsolutePath().hashCode();
+ }
+
+ public boolean equals(Object other) {
+ if (other instanceof WarInfo) {
+ WarInfo wo = (WarInfo) other;
+ return wo.getWar().equals(getWar());
+ } else {
+ return false;
+ }
+ }
+
+ protected void setLastState(int lastState) {
+ this.lastState = lastState;
+ }
+
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha.session;\r
-\r
-import java.io.ByteArrayInputStream;\r
-import java.io.IOException;\r
-\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.Session;\r
-import org.apache.catalina.ha.CatalinaCluster;\r
-import org.apache.catalina.ha.ClusterManager;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.session.StandardManager;\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.io.ReplicationStream;\r
-import org.apache.catalina.tribes.tipis.LazyReplicatedMap;\r
-\r
-/**\r
- *@author Filip Hanik\r
- *@version 1.0\r
- */\r
-public class BackupManager extends StandardManager implements ClusterManager\r
-{\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( BackupManager.class );\r
-\r
- protected static long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds\r
-\r
- /** Set to true if we don't want the sessions to expire on shutdown */\r
- protected boolean mExpireSessionsOnShutdown = true;\r
- \r
- /**\r
- * The name of this manager\r
- */\r
- protected String name;\r
-\r
- /**\r
- * A reference to the cluster\r
- */\r
- protected CatalinaCluster cluster;\r
- \r
- /**\r
- * Should listeners be notified?\r
- */\r
- private boolean notifyListenersOnReplication;\r
- /**\r
- * \r
- */\r
- private int mapSendOptions = Channel.SEND_OPTIONS_SYNCHRONIZED_ACK|Channel.SEND_OPTIONS_USE_ACK;\r
-\r
- /**\r
- * Constructor, just calls super()\r
- *\r
- */\r
- public BackupManager() {\r
- super();\r
- }\r
-\r
-\r
-//******************************************************************************/\r
-// ClusterManager Interface \r
-//******************************************************************************/\r
-\r
- public void messageDataReceived(ClusterMessage msg) {\r
- }\r
-\r
- public boolean doDomainReplication() {\r
- return false;\r
- }\r
-\r
- /**\r
- * @param sendClusterDomainOnly The sendClusterDomainOnly to set.\r
- */\r
- public void setDomainReplication(boolean sendClusterDomainOnly) {\r
- }\r
-\r
- /**\r
- * @return Returns the defaultMode.\r
- */\r
- public boolean isDefaultMode() {\r
- return false;\r
- }\r
- /**\r
- * @param defaultMode The defaultMode to set.\r
- */\r
- public void setDefaultMode(boolean defaultMode) {\r
- }\r
-\r
- public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown)\r
- {\r
- mExpireSessionsOnShutdown = expireSessionsOnShutdown;\r
- }\r
-\r
- public void setCluster(CatalinaCluster cluster) {\r
- if(log.isDebugEnabled())\r
- log.debug("Cluster associated with SimpleTcpReplicationManager");\r
- this.cluster = cluster;\r
- }\r
-\r
- public boolean getExpireSessionsOnShutdown()\r
- {\r
- return mExpireSessionsOnShutdown;\r
- }\r
-\r
-\r
- /**\r
- * Override persistence since they don't go hand in hand with replication for now.\r
- */\r
- public void unload() throws IOException {\r
- }\r
- \r
- public ClusterMessage requestCompleted(String sessionId) {\r
- if ( !this.started ) return null;\r
- LazyReplicatedMap map = (LazyReplicatedMap)sessions;\r
- map.replicate(sessionId,false);\r
- return null;\r
- }\r
-\r
-\r
-//=========================================================================\r
-// OVERRIDE THESE METHODS TO IMPLEMENT THE REPLICATION\r
-//=========================================================================\r
-\r
- public Session createEmptySession() {\r
- return new DeltaSession(this);\r
- }\r
- \r
- public ClassLoader[] getClassLoaders() {\r
- return ClusterManagerBase.getClassLoaders(this.container);\r
- }\r
-\r
- /**\r
- * Open Stream and use correct ClassLoader (Container) Switch\r
- * ThreadClassLoader\r
- * \r
- * @param data\r
- * @return The object input stream\r
- * @throws IOException\r
- */\r
- public ReplicationStream getReplicationStream(byte[] data) throws IOException {\r
- return getReplicationStream(data,0,data.length);\r
- }\r
-\r
- public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException {\r
- ByteArrayInputStream fis = new ByteArrayInputStream(data, offset, length);\r
- return new ReplicationStream(fis, getClassLoaders());\r
- } \r
-\r
-\r
-\r
-\r
- public String getName() {\r
- return this.name;\r
- }\r
- /**\r
- * Prepare for the beginning of active use of the public methods of this\r
- * component. This method should be called after <code>configure()</code>,\r
- * and before any of the public methods of the component are utilized.<BR>\r
- * Starts the cluster communication channel, this will connect with the other nodes\r
- * in the cluster, and request the current session state to be transferred to this node.\r
- * @exception IllegalStateException if this component has already been\r
- * started\r
- * @exception LifecycleException if this component detects a fatal error\r
- * that prevents this component from being used\r
- */\r
- public void start() throws LifecycleException {\r
- if ( this.started ) return;\r
- \r
- try {\r
- cluster.registerManager(this);\r
- CatalinaCluster catclust = (CatalinaCluster)cluster;\r
- LazyReplicatedMap map = new LazyReplicatedMap(this,\r
- catclust.getChannel(),\r
- DEFAULT_REPL_TIMEOUT,\r
- getMapName(),\r
- getClassLoaders());\r
- map.setChannelSendOptions(mapSendOptions);\r
- this.sessions = map;\r
- super.start();\r
- this.started = true;\r
- } catch ( Exception x ) {\r
- log.error("Unable to start BackupManager",x);\r
- throw new LifecycleException("Failed to start BackupManager",x);\r
- }\r
- }\r
- \r
- public String getMapName() {\r
- CatalinaCluster catclust = (CatalinaCluster)cluster;\r
- String name = catclust.getManagerName(getName(),this)+"-"+"map";\r
- if ( log.isDebugEnabled() ) log.debug("Backup manager, Setting map name to:"+name);\r
- return name;\r
- }\r
-\r
- /**\r
- * Gracefully terminate the active use of the public methods of this\r
- * component. This method should be the last one called on a given\r
- * instance of this component.<BR>\r
- * This will disconnect the cluster communication channel and stop the listener thread.\r
- * @exception IllegalStateException if this component has not been started\r
- * @exception LifecycleException if this component detects a fatal error\r
- * that needs to be reported\r
- */\r
- public void stop() throws LifecycleException\r
- {\r
- \r
- LazyReplicatedMap map = (LazyReplicatedMap)sessions;\r
- if ( map!=null ) {\r
- map.breakdown();\r
- }\r
- if ( !this.started ) return;\r
- try {\r
- } catch ( Exception x ){\r
- log.error("Unable to stop BackupManager",x);\r
- throw new LifecycleException("Failed to stop BackupManager",x);\r
- } finally {\r
- super.stop();\r
- }\r
- cluster.removeManager(this);\r
-\r
- }\r
-\r
- public void setDistributable(boolean dist) {\r
- this.distributable = dist;\r
- }\r
-\r
- public boolean getDistributable() {\r
- return distributable;\r
- }\r
-\r
- public void setName(String name) {\r
- this.name = name;\r
- }\r
- public boolean isNotifyListenersOnReplication() {\r
- return notifyListenersOnReplication;\r
- }\r
- public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {\r
- this.notifyListenersOnReplication = notifyListenersOnReplication;\r
- }\r
-\r
- public void setMapSendOptions(int mapSendOptions) {\r
- this.mapSendOptions = mapSendOptions;\r
- }\r
-\r
- /* \r
- * @see org.apache.catalina.ha.ClusterManager#getCluster()\r
- */\r
- public CatalinaCluster getCluster() {\r
- return cluster;\r
- }\r
-\r
- public int getMapSendOptions() {\r
- return mapSendOptions;\r
- }\r
-\r
- public String[] getInvalidatedSessions() {\r
- return new String[0];\r
- }\r
- \r
- public ClusterManager cloneFromTemplate() {\r
- BackupManager result = new BackupManager();\r
- result.mExpireSessionsOnShutdown = mExpireSessionsOnShutdown;\r
- result.name = "Clone-from-"+name;\r
- result.cluster = cluster;\r
- result.notifyListenersOnReplication = notifyListenersOnReplication;\r
- result.mapSendOptions = mapSendOptions;\r
- return result;\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.catalina.ha.ClusterManager;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.io.ReplicationStream;
+import org.apache.catalina.tribes.tipis.LazyReplicatedMap;
+
+/**
+ *@author Filip Hanik
+ *@version 1.0
+ */
+public class BackupManager extends StandardManager implements ClusterManager
+{
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( BackupManager.class );
+
+ protected static long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds
+
+ /** Set to true if we don't want the sessions to expire on shutdown */
+ protected boolean mExpireSessionsOnShutdown = true;
+
+ /**
+ * The name of this manager
+ */
+ protected String name;
+
+ /**
+ * A reference to the cluster
+ */
+ protected CatalinaCluster cluster;
+
+ /**
+ * Should listeners be notified?
+ */
+ private boolean notifyListenersOnReplication;
+ /**
+ *
+ */
+ private int mapSendOptions = Channel.SEND_OPTIONS_SYNCHRONIZED_ACK|Channel.SEND_OPTIONS_USE_ACK;
+
+ /**
+ * Constructor, just calls super()
+ *
+ */
+ public BackupManager() {
+ super();
+ }
+
+
+//******************************************************************************/
+// ClusterManager Interface
+//******************************************************************************/
+
+ public void messageDataReceived(ClusterMessage msg) {
+ }
+
+ public boolean doDomainReplication() {
+ return false;
+ }
+
+ /**
+ * @param sendClusterDomainOnly The sendClusterDomainOnly to set.
+ */
+ public void setDomainReplication(boolean sendClusterDomainOnly) {
+ }
+
+ /**
+ * @return Returns the defaultMode.
+ */
+ public boolean isDefaultMode() {
+ return false;
+ }
+ /**
+ * @param defaultMode The defaultMode to set.
+ */
+ public void setDefaultMode(boolean defaultMode) {
+ }
+
+ public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown)
+ {
+ mExpireSessionsOnShutdown = expireSessionsOnShutdown;
+ }
+
+ public void setCluster(CatalinaCluster cluster) {
+ if(log.isDebugEnabled())
+ log.debug("Cluster associated with SimpleTcpReplicationManager");
+ this.cluster = cluster;
+ }
+
+ public boolean getExpireSessionsOnShutdown()
+ {
+ return mExpireSessionsOnShutdown;
+ }
+
+
+ /**
+ * Override persistence since they don't go hand in hand with replication for now.
+ */
+ public void unload() throws IOException {
+ }
+
+ public ClusterMessage requestCompleted(String sessionId) {
+ if ( !this.started ) return null;
+ LazyReplicatedMap map = (LazyReplicatedMap)sessions;
+ map.replicate(sessionId,false);
+ return null;
+ }
+
+
+//=========================================================================
+// OVERRIDE THESE METHODS TO IMPLEMENT THE REPLICATION
+//=========================================================================
+
+ public Session createEmptySession() {
+ return new DeltaSession(this);
+ }
+
+ public ClassLoader[] getClassLoaders() {
+ return ClusterManagerBase.getClassLoaders(this.container);
+ }
+
+ /**
+ * Open Stream and use correct ClassLoader (Container) Switch
+ * ThreadClassLoader
+ *
+ * @param data
+ * @return The object input stream
+ * @throws IOException
+ */
+ public ReplicationStream getReplicationStream(byte[] data) throws IOException {
+ return getReplicationStream(data,0,data.length);
+ }
+
+ public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException {
+ ByteArrayInputStream fis = new ByteArrayInputStream(data, offset, length);
+ return new ReplicationStream(fis, getClassLoaders());
+ }
+
+
+
+
+ public String getName() {
+ return this.name;
+ }
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized.<BR>
+ * Starts the cluster communication channel, this will connect with the other nodes
+ * in the cluster, and request the current session state to be transferred to this node.
+ * @exception IllegalStateException if this component has already been
+ * started
+ * @exception LifecycleException if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ public void start() throws LifecycleException {
+ if ( this.started ) return;
+
+ try {
+ cluster.registerManager(this);
+ CatalinaCluster catclust = (CatalinaCluster)cluster;
+ LazyReplicatedMap map = new LazyReplicatedMap(this,
+ catclust.getChannel(),
+ DEFAULT_REPL_TIMEOUT,
+ getMapName(),
+ getClassLoaders());
+ map.setChannelSendOptions(mapSendOptions);
+ this.sessions = map;
+ super.start();
+ this.started = true;
+ } catch ( Exception x ) {
+ log.error("Unable to start BackupManager",x);
+ throw new LifecycleException("Failed to start BackupManager",x);
+ }
+ }
+
+ public String getMapName() {
+ CatalinaCluster catclust = (CatalinaCluster)cluster;
+ String name = catclust.getManagerName(getName(),this)+"-"+"map";
+ if ( log.isDebugEnabled() ) log.debug("Backup manager, Setting map name to:"+name);
+ return name;
+ }
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component.<BR>
+ * This will disconnect the cluster communication channel and stop the listener thread.
+ * @exception IllegalStateException if this component has not been started
+ * @exception LifecycleException if this component detects a fatal error
+ * that needs to be reported
+ */
+ public void stop() throws LifecycleException
+ {
+
+ LazyReplicatedMap map = (LazyReplicatedMap)sessions;
+ if ( map!=null ) {
+ map.breakdown();
+ }
+ if ( !this.started ) return;
+ try {
+ } catch ( Exception x ){
+ log.error("Unable to stop BackupManager",x);
+ throw new LifecycleException("Failed to stop BackupManager",x);
+ } finally {
+ super.stop();
+ }
+ cluster.removeManager(this);
+
+ }
+
+ public void setDistributable(boolean dist) {
+ this.distributable = dist;
+ }
+
+ public boolean getDistributable() {
+ return distributable;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ public boolean isNotifyListenersOnReplication() {
+ return notifyListenersOnReplication;
+ }
+ public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
+ this.notifyListenersOnReplication = notifyListenersOnReplication;
+ }
+
+ public void setMapSendOptions(int mapSendOptions) {
+ this.mapSendOptions = mapSendOptions;
+ }
+
+ /*
+ * @see org.apache.catalina.ha.ClusterManager#getCluster()
+ */
+ public CatalinaCluster getCluster() {
+ return cluster;
+ }
+
+ public int getMapSendOptions() {
+ return mapSendOptions;
+ }
+
+ public String[] getInvalidatedSessions() {
+ return new String[0];
+ }
+
+ public ClusterManager cloneFromTemplate() {
+ BackupManager result = new BackupManager();
+ result.mExpireSessionsOnShutdown = mExpireSessionsOnShutdown;
+ result.name = "Clone-from-"+name;
+ result.cluster = cluster;
+ result.notifyListenersOnReplication = notifyListenersOnReplication;
+ result.mapSendOptions = mapSendOptions;
+ return result;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-import org.apache.catalina.ha.ClusterManager;\r
-import java.beans.PropertyChangeListener;\r
-import org.apache.catalina.Lifecycle;\r
-import org.apache.catalina.session.ManagerBase;\r
-import org.apache.catalina.Loader;\r
-import java.io.ByteArrayInputStream;\r
-import java.io.IOException;\r
-import org.apache.catalina.tribes.io.ReplicationStream;\r
-import org.apache.catalina.Container;\r
-\r
-/**\r
- * \r
- * @author Filip Hanik\r
- * @version $Revision: 380100 $ $Date: 2006-02-23 06:08:14 -0600 (Thu, 23 Feb 2006) $\r
- */\r
-\r
-public abstract class ClusterManagerBase extends ManagerBase implements Lifecycle, PropertyChangeListener, ClusterManager{\r
- \r
-\r
- public static ClassLoader[] getClassLoaders(Container container) {\r
- Loader loader = null;\r
- ClassLoader classLoader = null;\r
- if (container != null) loader = container.getLoader();\r
- if (loader != null) classLoader = loader.getClassLoader();\r
- else classLoader = Thread.currentThread().getContextClassLoader();\r
- if ( classLoader == Thread.currentThread().getContextClassLoader() ) {\r
- return new ClassLoader[] {classLoader};\r
- } else {\r
- return new ClassLoader[] {classLoader,Thread.currentThread().getContextClassLoader()};\r
- }\r
- }\r
-\r
-\r
- public ClassLoader[] getClassLoaders() {\r
- return getClassLoaders(container);\r
- }\r
-\r
- /**\r
- * Open Stream and use correct ClassLoader (Container) Switch\r
- * ThreadClassLoader\r
- * \r
- * @param data\r
- * @return The object input stream\r
- * @throws IOException\r
- */\r
- public ReplicationStream getReplicationStream(byte[] data) throws IOException {\r
- return getReplicationStream(data,0,data.length);\r
- }\r
-\r
- public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException {\r
- ByteArrayInputStream fis = new ByteArrayInputStream(data, offset, length);\r
- return new ReplicationStream(fis, getClassLoaders());\r
- } \r
-\r
-\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import org.apache.catalina.ha.ClusterManager;
+import java.beans.PropertyChangeListener;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.session.ManagerBase;
+import org.apache.catalina.Loader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import org.apache.catalina.tribes.io.ReplicationStream;
+import org.apache.catalina.Container;
+
+/**
+ *
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+
+public abstract class ClusterManagerBase extends ManagerBase implements Lifecycle, PropertyChangeListener, ClusterManager{
+
+
+ public static ClassLoader[] getClassLoaders(Container container) {
+ Loader loader = null;
+ ClassLoader classLoader = null;
+ if (container != null) loader = container.getLoader();
+ if (loader != null) classLoader = loader.getClassLoader();
+ else classLoader = Thread.currentThread().getContextClassLoader();
+ if ( classLoader == Thread.currentThread().getContextClassLoader() ) {
+ return new ClassLoader[] {classLoader};
+ } else {
+ return new ClassLoader[] {classLoader,Thread.currentThread().getContextClassLoader()};
+ }
+ }
+
+
+ public ClassLoader[] getClassLoaders() {
+ return getClassLoaders(container);
+ }
+
+ /**
+ * Open Stream and use correct ClassLoader (Container) Switch
+ * ThreadClassLoader
+ *
+ * @param data
+ * @return The object input stream
+ * @throws IOException
+ */
+ public ReplicationStream getReplicationStream(byte[] data) throws IOException {
+ return getReplicationStream(data,0,data.length);
+ }
+
+ public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException {
+ ByteArrayInputStream fis = new ByteArrayInputStream(data, offset, length);
+ return new ReplicationStream(fis, getClassLoaders());
+ }
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-import java.util.Map;\r
-\r
-import org.apache.catalina.ha.ClusterManager;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.ha.*;\r
-\r
-/**\r
- * Receive replicated SessionMessage form other cluster node.\r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * @version $Revision: 378258 $ $Date: 2006-02-16 08:42:35 -0600 (Thu, 16 Feb 2006) $\r
- */\r
-public class ClusterSessionListener extends ClusterListener {\r
- \r
- /**\r
- * The descriptive information about this implementation.\r
- */\r
- protected static final String info = "org.apache.catalina.session.ClusterSessionListener/1.1";\r
-\r
- //--Constructor---------------------------------------------\r
-\r
- public ClusterSessionListener() {\r
- }\r
-\r
- //--Logic---------------------------------------------------\r
-\r
- /**\r
- * Return descriptive information about this implementation.\r
- */\r
- public String getInfo() {\r
-\r
- return (info);\r
-\r
- }\r
-\r
- /**\r
- * Callback from the cluster, when a message is received, The cluster will\r
- * broadcast it invoking the messageReceived on the receiver.\r
- * \r
- * @param myobj\r
- * ClusterMessage - the message received from the cluster\r
- */\r
- public void messageReceived(ClusterMessage myobj) {\r
- if (myobj != null && myobj instanceof SessionMessage) {\r
- SessionMessage msg = (SessionMessage) myobj;\r
- String ctxname = msg.getContextName();\r
- //check if the message is a EVT_GET_ALL_SESSIONS,\r
- //if so, wait until we are fully started up\r
- Map managers = cluster.getManagers() ;\r
- if (ctxname == null) {\r
- java.util.Iterator i = managers.keySet().iterator();\r
- while (i.hasNext()) {\r
- String key = (String) i.next();\r
- ClusterManager mgr = (ClusterManager) managers.get(key);\r
- if (mgr != null)\r
- mgr.messageDataReceived(msg);\r
- else {\r
- //this happens a lot before the system has started\r
- // up\r
- if (log.isDebugEnabled())\r
- log.debug("Context manager doesn't exist:"\r
- + key);\r
- }\r
- }\r
- } else {\r
- ClusterManager mgr = (ClusterManager) managers.get(ctxname);\r
- if (mgr != null)\r
- mgr.messageDataReceived(msg);\r
- else if (log.isWarnEnabled())\r
- log.warn("Context manager doesn't exist:" + ctxname);\r
- }\r
- }\r
- return;\r
- }\r
-\r
- /**\r
- * Accept only SessionMessage\r
- * \r
- * @param msg\r
- * ClusterMessage\r
- * @return boolean - returns true to indicate that messageReceived should be\r
- * invoked. If false is returned, the messageReceived method will\r
- * not be invoked.\r
- */\r
- public boolean accept(ClusterMessage msg) {\r
- return (msg instanceof SessionMessage);\r
- }\r
-}\r
-\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import java.util.Map;
+
+import org.apache.catalina.ha.ClusterManager;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.ha.*;
+
+/**
+ * Receive replicated SessionMessage form other cluster node.
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class ClusterSessionListener extends ClusterListener {
+
+ /**
+ * The descriptive information about this implementation.
+ */
+ protected static final String info = "org.apache.catalina.session.ClusterSessionListener/1.1";
+
+ //--Constructor---------------------------------------------
+
+ public ClusterSessionListener() {
+ }
+
+ //--Logic---------------------------------------------------
+
+ /**
+ * Return descriptive information about this implementation.
+ */
+ public String getInfo() {
+
+ return (info);
+
+ }
+
+ /**
+ * Callback from the cluster, when a message is received, The cluster will
+ * broadcast it invoking the messageReceived on the receiver.
+ *
+ * @param myobj
+ * ClusterMessage - the message received from the cluster
+ */
+ public void messageReceived(ClusterMessage myobj) {
+ if (myobj != null && myobj instanceof SessionMessage) {
+ SessionMessage msg = (SessionMessage) myobj;
+ String ctxname = msg.getContextName();
+ //check if the message is a EVT_GET_ALL_SESSIONS,
+ //if so, wait until we are fully started up
+ Map managers = cluster.getManagers() ;
+ if (ctxname == null) {
+ java.util.Iterator i = managers.keySet().iterator();
+ while (i.hasNext()) {
+ String key = (String) i.next();
+ ClusterManager mgr = (ClusterManager) managers.get(key);
+ if (mgr != null)
+ mgr.messageDataReceived(msg);
+ else {
+ //this happens a lot before the system has started
+ // up
+ if (log.isDebugEnabled())
+ log.debug("Context manager doesn't exist:"
+ + key);
+ }
+ }
+ } else {
+ ClusterManager mgr = (ClusterManager) managers.get(ctxname);
+ if (mgr != null)
+ mgr.messageDataReceived(msg);
+ else if (log.isWarnEnabled())
+ log.warn("Context manager doesn't exist:" + ctxname);
+ }
+ }
+ return;
+ }
+
+ /**
+ * Accept only SessionMessage
+ *
+ * @param msg
+ * ClusterMessage
+ * @return boolean - returns true to indicate that messageReceived should be
+ * invoked. If false is returned, the messageReceived method will
+ * not be invoked.
+ */
+ public boolean accept(ClusterMessage msg) {
+ return (msg instanceof SessionMessage);
+ }
+}
+
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-/**\r
- * Manifest constants for the <code>org.apache.catalina.ha.session</code>\r
- * package.\r
- *\r
- * @author Peter Rossbach Pero\r
- */\r
-\r
-public class Constants {\r
-\r
- public static final String Package = "org.apache.catalina.ha.session";\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.ha.session</code>
+ * package.
+ *
+ * @author Peter Rossbach Pero
+ */
+
+public class Constants {
+
+ public static final String Package = "org.apache.catalina.ha.session";
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-import java.beans.PropertyChangeEvent;\r
-import java.io.BufferedOutputStream;\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.IOException;\r
-import java.io.ObjectInputStream;\r
-import java.io.ObjectOutputStream;\r
-import java.util.ArrayList;\r
-import java.util.Date;\r
-import java.util.Iterator;\r
-\r
-import org.apache.catalina.Cluster;\r
-import org.apache.catalina.Container;\r
-import org.apache.catalina.Context;\r
-import org.apache.catalina.Engine;\r
-import org.apache.catalina.Host;\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.LifecycleListener;\r
-import org.apache.catalina.Session;\r
-import org.apache.catalina.Valve;\r
-import org.apache.catalina.core.StandardContext;\r
-import org.apache.catalina.ha.CatalinaCluster;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.ha.tcp.ReplicationValve;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.io.ReplicationStream;\r
-import org.apache.catalina.util.LifecycleSupport;\r
-import org.apache.catalina.util.StringManager;\r
-import org.apache.catalina.ha.ClusterManager;\r
-\r
-/**\r
- * The DeltaManager manages replicated sessions by only replicating the deltas\r
- * in data. For applications written to handle this, the DeltaManager is the\r
- * optimal way of replicating data.\r
- * \r
- * This code is almost identical to StandardManager with a difference in how it\r
- * persists sessions and some modifications to it.\r
- * \r
- * <b>IMPLEMENTATION NOTE </b>: Correct behavior of session storing and\r
- * reloading depends upon external calls to the <code>start()</code> and\r
- * <code>stop()</code> methods of this class at the correct times.\r
- * \r
- * @author Filip Hanik\r
- * @author Craig R. McClanahan\r
- * @author Jean-Francois Arcand\r
- * @author Peter Rossbach\r
- * @version $Revision: 380100 $ $Date: 2006-02-23 06:08:14 -0600 (Thu, 23 Feb 2006) $\r
- */\r
-\r
-public class DeltaManager extends ClusterManagerBase{\r
-\r
- // ---------------------------------------------------- Security Classes\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(DeltaManager.class);\r
-\r
- /**\r
- * The string manager for this package.\r
- */\r
- protected static StringManager sm = StringManager.getManager(Constants.Package);\r
-\r
- // ----------------------------------------------------- Instance Variables\r
-\r
- /**\r
- * The descriptive information about this implementation.\r
- */\r
- private static final String info = "DeltaManager/2.1";\r
-\r
- /**\r
- * Has this component been started yet?\r
- */\r
- private boolean started = false;\r
-\r
- /**\r
- * The descriptive name of this Manager implementation (for logging).\r
- */\r
- protected static String managerName = "DeltaManager";\r
- protected String name = null;\r
- protected boolean defaultMode = false;\r
- private CatalinaCluster cluster = null;\r
-\r
- /**\r
- * cached replication valve cluster container!\r
- */\r
- private ReplicationValve replicationValve = null ;\r
- \r
- /**\r
- * The lifecycle event support for this component.\r
- */\r
- protected LifecycleSupport lifecycle = new LifecycleSupport(this);\r
-\r
- /**\r
- * The maximum number of active Sessions allowed, or -1 for no limit.\r
- */\r
- private int maxActiveSessions = -1;\r
- private boolean expireSessionsOnShutdown = false;\r
- private boolean notifyListenersOnReplication = true;\r
- private boolean notifySessionListenersOnReplication = true;\r
- private boolean stateTransfered = false ;\r
- private int stateTransferTimeout = 60;\r
- private boolean sendAllSessions = true;\r
- private boolean sendClusterDomainOnly = true ;\r
- private int sendAllSessionsSize = 1000 ;\r
- \r
- /**\r
- * wait time between send session block (default 2 sec) \r
- */\r
- private int sendAllSessionsWaitTime = 2 * 1000 ; \r
- private ArrayList receivedMessageQueue = new ArrayList() ;\r
- private boolean receiverQueue = false ;\r
- private boolean stateTimestampDrop = true ;\r
- private long stateTransferCreateSendTime; \r
- \r
- // ------------------------------------------------------------------ stats attributes\r
- \r
- int rejectedSessions = 0;\r
- private long sessionReplaceCounter = 0 ;\r
- long processingTime = 0;\r
- private long counterReceive_EVT_GET_ALL_SESSIONS = 0 ;\r
- private long counterSend_EVT_ALL_SESSION_DATA = 0 ;\r
- private long counterReceive_EVT_ALL_SESSION_DATA = 0 ;\r
- private long counterReceive_EVT_SESSION_CREATED = 0 ;\r
- private long counterReceive_EVT_SESSION_EXPIRED = 0;\r
- private long counterReceive_EVT_SESSION_ACCESSED = 0 ;\r
- private long counterReceive_EVT_SESSION_DELTA = 0;\r
- private long counterSend_EVT_GET_ALL_SESSIONS = 0 ;\r
- private long counterSend_EVT_SESSION_CREATED = 0;\r
- private long counterSend_EVT_SESSION_DELTA = 0 ;\r
- private long counterSend_EVT_SESSION_ACCESSED = 0;\r
- private long counterSend_EVT_SESSION_EXPIRED = 0;\r
- private int counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0 ;\r
- private int counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0 ;\r
- private int counterNoStateTransfered = 0 ;\r
-\r
-\r
- // ------------------------------------------------------------- Constructor\r
- public DeltaManager() {\r
- super();\r
- }\r
-\r
- // ------------------------------------------------------------- Properties\r
- \r
- /**\r
- * Return descriptive information about this Manager implementation and the\r
- * corresponding version number, in the format\r
- * <code><description>/<version></code>.\r
- */\r
- public String getInfo() {\r
- return info;\r
- }\r
-\r
- public void setName(String name) {\r
- this.name = name;\r
- }\r
-\r
- /**\r
- * Return the descriptive short name of this Manager implementation.\r
- */\r
- public String getName() {\r
- return name;\r
- }\r
-\r
- /**\r
- * @return Returns the counterSend_EVT_GET_ALL_SESSIONS.\r
- */\r
- public long getCounterSend_EVT_GET_ALL_SESSIONS() {\r
- return counterSend_EVT_GET_ALL_SESSIONS;\r
- }\r
- \r
- /**\r
- * @return Returns the counterSend_EVT_SESSION_ACCESSED.\r
- */\r
- public long getCounterSend_EVT_SESSION_ACCESSED() {\r
- return counterSend_EVT_SESSION_ACCESSED;\r
- }\r
- \r
- /**\r
- * @return Returns the counterSend_EVT_SESSION_CREATED.\r
- */\r
- public long getCounterSend_EVT_SESSION_CREATED() {\r
- return counterSend_EVT_SESSION_CREATED;\r
- }\r
-\r
- /**\r
- * @return Returns the counterSend_EVT_SESSION_DELTA.\r
- */\r
- public long getCounterSend_EVT_SESSION_DELTA() {\r
- return counterSend_EVT_SESSION_DELTA;\r
- }\r
-\r
- /**\r
- * @return Returns the counterSend_EVT_SESSION_EXPIRED.\r
- */\r
- public long getCounterSend_EVT_SESSION_EXPIRED() {\r
- return counterSend_EVT_SESSION_EXPIRED;\r
- }\r
- \r
- /**\r
- * @return Returns the counterSend_EVT_ALL_SESSION_DATA.\r
- */\r
- public long getCounterSend_EVT_ALL_SESSION_DATA() {\r
- return counterSend_EVT_ALL_SESSION_DATA;\r
- }\r
-\r
- /**\r
- * @return Returns the counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE.\r
- */\r
- public int getCounterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE() {\r
- return counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE;\r
- }\r
- \r
- /**\r
- * @return Returns the counterReceive_EVT_ALL_SESSION_DATA.\r
- */\r
- public long getCounterReceive_EVT_ALL_SESSION_DATA() {\r
- return counterReceive_EVT_ALL_SESSION_DATA;\r
- }\r
- \r
- /**\r
- * @return Returns the counterReceive_EVT_GET_ALL_SESSIONS.\r
- */\r
- public long getCounterReceive_EVT_GET_ALL_SESSIONS() {\r
- return counterReceive_EVT_GET_ALL_SESSIONS;\r
- }\r
- \r
- /**\r
- * @return Returns the counterReceive_EVT_SESSION_ACCESSED.\r
- */\r
- public long getCounterReceive_EVT_SESSION_ACCESSED() {\r
- return counterReceive_EVT_SESSION_ACCESSED;\r
- }\r
- \r
- /**\r
- * @return Returns the counterReceive_EVT_SESSION_CREATED.\r
- */\r
- public long getCounterReceive_EVT_SESSION_CREATED() {\r
- return counterReceive_EVT_SESSION_CREATED;\r
- }\r
- \r
- /**\r
- * @return Returns the counterReceive_EVT_SESSION_DELTA.\r
- */\r
- public long getCounterReceive_EVT_SESSION_DELTA() {\r
- return counterReceive_EVT_SESSION_DELTA;\r
- }\r
- \r
- /**\r
- * @return Returns the counterReceive_EVT_SESSION_EXPIRED.\r
- */\r
- public long getCounterReceive_EVT_SESSION_EXPIRED() {\r
- return counterReceive_EVT_SESSION_EXPIRED;\r
- }\r
- \r
- \r
- /**\r
- * @return Returns the counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE.\r
- */\r
- public int getCounterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE() {\r
- return counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE;\r
- }\r
- \r
- /**\r
- * @return Returns the processingTime.\r
- */\r
- public long getProcessingTime() {\r
- return processingTime;\r
- }\r
- \r
- /**\r
- * @return Returns the sessionReplaceCounter.\r
- */\r
- public long getSessionReplaceCounter() {\r
- return sessionReplaceCounter;\r
- }\r
- \r
- /**\r
- * Number of session creations that failed due to maxActiveSessions\r
- * \r
- * @return The count\r
- */\r
- public int getRejectedSessions() {\r
- return rejectedSessions;\r
- }\r
-\r
- public void setRejectedSessions(int rejectedSessions) {\r
- this.rejectedSessions = rejectedSessions;\r
- }\r
-\r
- /**\r
- * @return Returns the counterNoStateTransfered.\r
- */\r
- public int getCounterNoStateTransfered() {\r
- return counterNoStateTransfered;\r
- }\r
- \r
- public int getReceivedQueueSize() {\r
- return receivedMessageQueue.size() ;\r
- }\r
- \r
- /**\r
- * @return Returns the stateTransferTimeout.\r
- */\r
- public int getStateTransferTimeout() {\r
- return stateTransferTimeout;\r
- }\r
- /**\r
- * @param timeoutAllSession The timeout\r
- */\r
- public void setStateTransferTimeout(int timeoutAllSession) {\r
- this.stateTransferTimeout = timeoutAllSession;\r
- }\r
-\r
- /**\r
- * is session state transfered complete?\r
- * \r
- */\r
- public boolean getStateTransfered() {\r
- return stateTransfered;\r
- }\r
-\r
- /**\r
- * set that state ist complete transfered \r
- * @param stateTransfered\r
- */\r
- public void setStateTransfered(boolean stateTransfered) {\r
- this.stateTransfered = stateTransfered;\r
- }\r
- \r
- /**\r
- * @return Returns the sendAllSessionsWaitTime in msec\r
- */\r
- public int getSendAllSessionsWaitTime() {\r
- return sendAllSessionsWaitTime;\r
- }\r
- \r
- /**\r
- * @param sendAllSessionsWaitTime The sendAllSessionsWaitTime to set at msec.\r
- */\r
- public void setSendAllSessionsWaitTime(int sendAllSessionsWaitTime) {\r
- this.sendAllSessionsWaitTime = sendAllSessionsWaitTime;\r
- }\r
- \r
- /**\r
- * @return Returns the sendClusterDomainOnly.\r
- */\r
- public boolean doDomainReplication() {\r
- return sendClusterDomainOnly;\r
- }\r
- \r
- /**\r
- * @param sendClusterDomainOnly The sendClusterDomainOnly to set.\r
- */\r
- public void setDomainReplication(boolean sendClusterDomainOnly) {\r
- this.sendClusterDomainOnly = sendClusterDomainOnly;\r
- }\r
-\r
- /**\r
- * @return Returns the stateTimestampDrop.\r
- */\r
- public boolean isStateTimestampDrop() {\r
- return stateTimestampDrop;\r
- }\r
- \r
- /**\r
- * @param isTimestampDrop The new flag value\r
- */\r
- public void setStateTimestampDrop(boolean isTimestampDrop) {\r
- this.stateTimestampDrop = isTimestampDrop;\r
- }\r
- \r
- /**\r
- * Return the maximum number of active Sessions allowed, or -1 for no limit.\r
- */\r
- public int getMaxActiveSessions() {\r
- return (this.maxActiveSessions);\r
- }\r
-\r
- /**\r
- * Set the maximum number of actives Sessions allowed, or -1 for no limit.\r
- * \r
- * @param max\r
- * The new maximum number of sessions\r
- */\r
- public void setMaxActiveSessions(int max) {\r
- int oldMaxActiveSessions = this.maxActiveSessions;\r
- this.maxActiveSessions = max;\r
- support.firePropertyChange("maxActiveSessions", new Integer(oldMaxActiveSessions), new Integer(this.maxActiveSessions));\r
- }\r
- \r
- /**\r
- * \r
- * @return Returns the sendAllSessions.\r
- */\r
- public boolean isSendAllSessions() {\r
- return sendAllSessions;\r
- }\r
- \r
- /**\r
- * @param sendAllSessions The sendAllSessions to set.\r
- */\r
- public void setSendAllSessions(boolean sendAllSessions) {\r
- this.sendAllSessions = sendAllSessions;\r
- }\r
- \r
- /**\r
- * @return Returns the sendAllSessionsSize.\r
- */\r
- public int getSendAllSessionsSize() {\r
- return sendAllSessionsSize;\r
- }\r
- \r
- /**\r
- * @param sendAllSessionsSize The sendAllSessionsSize to set.\r
- */\r
- public void setSendAllSessionsSize(int sendAllSessionsSize) {\r
- this.sendAllSessionsSize = sendAllSessionsSize;\r
- }\r
- \r
- /**\r
- * @return Returns the notifySessionListenersOnReplication.\r
- */\r
- public boolean isNotifySessionListenersOnReplication() {\r
- return notifySessionListenersOnReplication;\r
- }\r
- \r
- /**\r
- * @param notifyListenersCreateSessionOnReplication The notifySessionListenersOnReplication to set.\r
- */\r
- public void setNotifySessionListenersOnReplication(boolean notifyListenersCreateSessionOnReplication) {\r
- this.notifySessionListenersOnReplication = notifyListenersCreateSessionOnReplication;\r
- }\r
- \r
- \r
- public boolean isExpireSessionsOnShutdown() {\r
- return expireSessionsOnShutdown;\r
- }\r
-\r
- public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown) {\r
- this.expireSessionsOnShutdown = expireSessionsOnShutdown;\r
- }\r
- \r
- public boolean isNotifyListenersOnReplication() {\r
- return notifyListenersOnReplication;\r
- }\r
-\r
- public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {\r
- this.notifyListenersOnReplication = notifyListenersOnReplication;\r
- }\r
-\r
- \r
- /**\r
- * @return Returns the defaultMode.\r
- */\r
- public boolean isDefaultMode() {\r
- return defaultMode;\r
- }\r
- /**\r
- * @param defaultMode The defaultMode to set.\r
- */\r
- public void setDefaultMode(boolean defaultMode) {\r
- this.defaultMode = defaultMode;\r
- }\r
- \r
- public CatalinaCluster getCluster() {\r
- return cluster;\r
- }\r
-\r
- public void setCluster(CatalinaCluster cluster) {\r
- this.cluster = cluster;\r
- }\r
-\r
- /**\r
- * Set the Container with which this Manager has been associated. If it is a\r
- * Context (the usual case), listen for changes to the session timeout\r
- * property.\r
- * \r
- * @param container\r
- * The associated Container\r
- */\r
- public void setContainer(Container container) {\r
- // De-register from the old Container (if any)\r
- if ((this.container != null) && (this.container instanceof Context))\r
- ((Context) this.container).removePropertyChangeListener(this);\r
-\r
- // Default processing provided by our superclass\r
- super.setContainer(container);\r
-\r
- // Register with the new Container (if any)\r
- if ((this.container != null) && (this.container instanceof Context)) {\r
- setMaxInactiveInterval(((Context) this.container).getSessionTimeout() * 60);\r
- ((Context) this.container).addPropertyChangeListener(this);\r
- }\r
-\r
- }\r
- \r
- // --------------------------------------------------------- Public Methods\r
-\r
- /**\r
- * Construct and return a new session object, based on the default settings\r
- * specified by this Manager's properties. The session id will be assigned\r
- * by this method, and available via the getId() method of the returned\r
- * session. If a new session cannot be created for any reason, return\r
- * <code>null</code>.\r
- * \r
- * @exception IllegalStateException\r
- * if a new session cannot be instantiated for any reason\r
- * \r
- * Construct and return a new session object, based on the default settings\r
- * specified by this Manager's properties. The session id will be assigned\r
- * by this method, and available via the getId() method of the returned\r
- * session. If a new session cannot be created for any reason, return\r
- * <code>null</code>.\r
- * \r
- * @exception IllegalStateException\r
- * if a new session cannot be instantiated for any reason\r
- */\r
- public Session createSession(String sessionId) {\r
- return createSession(sessionId, true);\r
- }\r
-\r
- /**\r
- * create new session with check maxActiveSessions and send session creation\r
- * to other cluster nodes.\r
- * \r
- * @param distribute\r
- * @return The session\r
- */\r
- public Session createSession(String sessionId, boolean distribute) {\r
- if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) {\r
- rejectedSessions++;\r
- throw new IllegalStateException(sm.getString("deltaManager.createSession.ise"));\r
- }\r
- DeltaSession session = (DeltaSession) super.createSession(sessionId) ;\r
- if (distribute) {\r
- sendCreateSession(session.getId(), session);\r
- }\r
- if (log.isDebugEnabled())\r
- log.debug(sm.getString("deltaManager.createSession.newSession",session.getId(), new Integer(sessions.size())));\r
- return (session);\r
-\r
- }\r
-\r
- /**\r
- * Send create session evt to all backup node\r
- * @param sessionId\r
- * @param session\r
- */\r
- protected void sendCreateSession(String sessionId, DeltaSession session) {\r
- if(cluster.getMembers().length > 0 ) {\r
- SessionMessage msg = \r
- new SessionMessageImpl(getName(),\r
- SessionMessage.EVT_SESSION_CREATED, \r
- null, \r
- sessionId,\r
- sessionId + "-" + System.currentTimeMillis());\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.sendMessage.newSession",name, sessionId));\r
- msg.setTimestamp(session.getCreationTime());\r
- counterSend_EVT_SESSION_CREATED++;\r
- send(msg);\r
- }\r
- }\r
- \r
- /**\r
- * Send messages to other backup member (domain or all)\r
- * @param msg Session message\r
- */\r
- protected void send(SessionMessage msg) {\r
- if(cluster != null) {\r
- if(doDomainReplication())\r
- cluster.sendClusterDomain(msg);\r
- else\r
- cluster.send(msg);\r
- }\r
- }\r
-\r
- /**\r
- * Create DeltaSession\r
- * @see org.apache.catalina.Manager#createEmptySession()\r
- */\r
- public Session createEmptySession() {\r
- return getNewDeltaSession() ;\r
- }\r
- \r
- /**\r
- * Get new session class to be used in the doLoad() method.\r
- */\r
- protected DeltaSession getNewDeltaSession() {\r
- return new DeltaSession(this);\r
- }\r
-\r
- /**\r
- * Load Deltarequest from external node\r
- * Load the Class at container classloader\r
- * @see DeltaRequest#readExternal(java.io.ObjectInput)\r
- * @param session\r
- * @param data message data\r
- * @return The request\r
- * @throws ClassNotFoundException\r
- * @throws IOException\r
- */\r
- protected DeltaRequest deserializeDeltaRequest(DeltaSession session, byte[] data) throws ClassNotFoundException, IOException {\r
- ReplicationStream ois = getReplicationStream(data);\r
- session.getDeltaRequest().readExternal(ois);\r
- ois.close();\r
- return session.getDeltaRequest();\r
- }\r
-\r
- /**\r
- * serialize DeltaRequest\r
- * @see DeltaRequest#writeExternal(java.io.ObjectOutput)\r
- * \r
- * @param deltaRequest\r
- * @return serialized delta request\r
- * @throws IOException\r
- */\r
- protected byte[] serializeDeltaRequest(DeltaRequest deltaRequest) throws IOException {\r
- return deltaRequest.serialize();\r
- }\r
-\r
- /**\r
- * Load sessions from other cluster node.\r
- * FIXME replace currently sessions with same id without notifcation.\r
- * FIXME SSO handling is not really correct with the session replacement!\r
- * @exception ClassNotFoundException\r
- * if a serialized class cannot be found during the reload\r
- * @exception IOException\r
- * if an input/output error occurs\r
- */\r
- protected void deserializeSessions(byte[] data) throws ClassNotFoundException,IOException {\r
-\r
- // Initialize our internal data structures\r
- //sessions.clear(); //should not do this\r
- // Open an input stream to the specified pathname, if any\r
- ClassLoader originalLoader = Thread.currentThread().getContextClassLoader();\r
- ObjectInputStream ois = null;\r
- // Load the previously unloaded active sessions\r
- try {\r
- ois = getReplicationStream(data);\r
- Integer count = (Integer) ois.readObject();\r
- int n = count.intValue();\r
- for (int i = 0; i < n; i++) {\r
- DeltaSession session = (DeltaSession) createEmptySession();\r
- session.readObjectData(ois);\r
- session.setManager(this);\r
- session.setValid(true);\r
- session.setPrimarySession(false);\r
- //in case the nodes in the cluster are out of\r
- //time synch, this will make sure that we have the\r
- //correct timestamp, isValid returns true, cause\r
- // accessCount=1\r
- session.access();\r
- //make sure that the session gets ready to expire if\r
- // needed\r
- session.setAccessCount(0);\r
- session.resetDeltaRequest();\r
- // FIXME How inform other session id cache like SingleSignOn\r
- // increment sessionCounter to correct stats report\r
- if (findSession(session.getIdInternal()) == null ) {\r
- sessionCounter++;\r
- } else {\r
- sessionReplaceCounter++;\r
- // FIXME better is to grap this sessions again !\r
- if (log.isWarnEnabled()) log.warn(sm.getString("deltaManager.loading.existing.session",session.getIdInternal()));\r
- }\r
- add(session);\r
- }\r
- } catch (ClassNotFoundException e) {\r
- log.error(sm.getString("deltaManager.loading.cnfe", e), e);\r
- throw e;\r
- } catch (IOException e) {\r
- log.error(sm.getString("deltaManager.loading.ioe", e), e);\r
- throw e;\r
- } finally {\r
- // Close the input stream\r
- try {\r
- if (ois != null) ois.close();\r
- } catch (IOException f) {\r
- // ignored\r
- }\r
- ois = null;\r
- if (originalLoader != null) Thread.currentThread().setContextClassLoader(originalLoader);\r
- }\r
-\r
- }\r
-\r
- \r
-\r
- /**\r
- * Save any currently active sessions in the appropriate persistence\r
- * mechanism, if any. If persistence is not supported, this method returns\r
- * without doing anything.\r
- * \r
- * @exception IOException\r
- * if an input/output error occurs\r
- */\r
- protected byte[] serializeSessions(Session[] currentSessions) throws IOException {\r
-\r
- // Open an output stream to the specified pathname, if any\r
- ByteArrayOutputStream fos = null;\r
- ObjectOutputStream oos = null;\r
-\r
- try {\r
- fos = new ByteArrayOutputStream();\r
- oos = new ObjectOutputStream(new BufferedOutputStream(fos));\r
- oos.writeObject(new Integer(currentSessions.length));\r
- for(int i=0 ; i < currentSessions.length;i++) {\r
- ((DeltaSession)currentSessions[i]).writeObjectData(oos); \r
- }\r
- // Flush and close the output stream\r
- oos.flush();\r
- } catch (IOException e) {\r
- log.error(sm.getString("deltaManager.unloading.ioe", e), e);\r
- throw e;\r
- } finally {\r
- if (oos != null) {\r
- try {\r
- oos.close();\r
- } catch (IOException f) {\r
- ;\r
- }\r
- oos = null;\r
- }\r
- }\r
- // send object data as byte[]\r
- return fos.toByteArray();\r
- }\r
-\r
- // ------------------------------------------------------ Lifecycle Methods\r
-\r
- /**\r
- * Add a lifecycle event listener to this component.\r
- * \r
- * @param listener\r
- * The listener to add\r
- */\r
- public void addLifecycleListener(LifecycleListener listener) {\r
- lifecycle.addLifecycleListener(listener);\r
- }\r
-\r
- /**\r
- * Get the lifecycle listeners associated with this lifecycle. If this\r
- * Lifecycle has no listeners registered, a zero-length array is returned.\r
- */\r
- public LifecycleListener[] findLifecycleListeners() {\r
- return lifecycle.findLifecycleListeners();\r
- }\r
-\r
- /**\r
- * Remove a lifecycle event listener from this component.\r
- * \r
- * @param listener\r
- * The listener to remove\r
- */\r
- public void removeLifecycleListener(LifecycleListener listener) {\r
- lifecycle.removeLifecycleListener(listener);\r
- }\r
-\r
- /**\r
- * Prepare for the beginning of active use of the public methods of this\r
- * component. This method should be called after <code>configure()</code>,\r
- * and before any of the public methods of the component are utilized.\r
- * \r
- * @exception LifecycleException\r
- * if this component detects a fatal error that prevents this\r
- * component from being used\r
- */\r
- public void start() throws LifecycleException {\r
- if (!initialized) init();\r
-\r
- // Validate and update our current component state\r
- if (started) {\r
- return;\r
- }\r
- started = true;\r
- lifecycle.fireLifecycleEvent(START_EVENT, null);\r
-\r
- // Force initialization of the random number generator\r
- generateSessionId();\r
-\r
- // Load unloaded sessions, if any\r
- try {\r
- //the channel is already running\r
- Cluster cluster = getCluster() ;\r
- // stop remove cluster binding\r
- //wow, how many nested levels of if statements can we have ;)\r
- if(cluster == null) {\r
- Container context = getContainer() ;\r
- if(context != null && context instanceof Context) {\r
- Container host = context.getParent() ;\r
- if(host != null && host instanceof Host) {\r
- cluster = host.getCluster();\r
- if(cluster != null && cluster instanceof CatalinaCluster) {\r
- setCluster((CatalinaCluster) cluster) ;\r
- } else {\r
- Container engine = host.getParent() ;\r
- if(engine != null && engine instanceof Engine) {\r
- cluster = engine.getCluster();\r
- if(cluster != null && cluster instanceof CatalinaCluster) {\r
- setCluster((CatalinaCluster) cluster) ;\r
- }\r
- } else {\r
- cluster = null ;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- if (cluster == null) {\r
- log.error(sm.getString("deltaManager.noCluster", getName()));\r
- return;\r
- } else {\r
- if (log.isInfoEnabled()) {\r
- String type = "unknown" ;\r
- if( cluster.getContainer() instanceof Host){\r
- type = "Host" ;\r
- } else if( cluster.getContainer() instanceof Engine){\r
- type = "Engine" ;\r
- }\r
- log.info(sm.getString("deltaManager.registerCluster", getName(), type, cluster.getClusterName()));\r
- }\r
- }\r
- if (log.isInfoEnabled()) log.info(sm.getString("deltaManager.startClustering", getName()));\r
- //to survice context reloads, as only a stop/start is called, not\r
- // createManager\r
- cluster.registerManager(this);\r
-\r
- getAllClusterSessions();\r
-\r
- } catch (Throwable t) {\r
- log.error(sm.getString("deltaManager.managerLoad"), t);\r
- }\r
- }\r
-\r
- /**\r
- * get from first session master the backup from all clustered sessions\r
- * @see #findSessionMasterMember()\r
- */\r
- public synchronized void getAllClusterSessions() {\r
- if (cluster != null && cluster.getMembers().length > 0) {\r
- long beforeSendTime = System.currentTimeMillis();\r
- Member mbr = findSessionMasterMember();\r
- if(mbr == null) { // No domain member found\r
- return;\r
- }\r
- SessionMessage msg = new SessionMessageImpl(this.getName(),SessionMessage.EVT_GET_ALL_SESSIONS, null, "GET-ALL","GET-ALL-" + getName());\r
- // set reference time\r
- stateTransferCreateSendTime = beforeSendTime ;\r
- // request session state\r
- counterSend_EVT_GET_ALL_SESSIONS++;\r
- stateTransfered = false ;\r
- // FIXME This send call block the deploy thread, when sender waitForAck is enabled\r
- try {\r
- synchronized(receivedMessageQueue) {\r
- receiverQueue = true ;\r
- }\r
- cluster.send(msg, mbr);\r
- if (log.isWarnEnabled()) log.warn(sm.getString("deltaManager.waitForSessionState",getName(), mbr));\r
- // FIXME At sender ack mode this method check only the state transfer and resend is a problem!\r
- waitForSendAllSessions(beforeSendTime);\r
- } finally {\r
- synchronized(receivedMessageQueue) {\r
- for (Iterator iter = receivedMessageQueue.iterator(); iter.hasNext();) {\r
- SessionMessage smsg = (SessionMessage) iter.next();\r
- if (!stateTimestampDrop) {\r
- messageReceived(smsg, smsg.getAddress() != null ? (Member) smsg.getAddress() : null);\r
- } else {\r
- if (smsg.getEventType() != SessionMessage.EVT_GET_ALL_SESSIONS && smsg.getTimestamp() >= stateTransferCreateSendTime) {\r
- // FIXME handle EVT_GET_ALL_SESSIONS later\r
- messageReceived(smsg,smsg.getAddress() != null ? (Member) smsg.getAddress() : null);\r
- } else {\r
- if (log.isWarnEnabled()) {\r
- log.warn(sm.getString("deltaManager.dropMessage",getName(), smsg.getEventTypeString(),new Date(stateTransferCreateSendTime), new Date(smsg.getTimestamp())));\r
- }\r
- }\r
- }\r
- } \r
- receivedMessageQueue.clear();\r
- receiverQueue = false ;\r
- }\r
- }\r
- } else {\r
- if (log.isInfoEnabled()) log.info(sm.getString("deltaManager.noMembers", getName()));\r
- }\r
- }\r
-\r
- /**\r
- * Register cross context session at replication valve thread local\r
- * @param session cross context session\r
- */\r
- protected void registerSessionAtReplicationValve(DeltaSession session) {\r
- if(replicationValve == null) {\r
- if(container instanceof StandardContext && ((StandardContext)container).getCrossContext()) {\r
- Cluster cluster = getCluster() ;\r
- if(cluster != null && cluster instanceof CatalinaCluster) {\r
- Valve[] valves = ((CatalinaCluster)cluster).getValves();\r
- if(valves != null && valves.length > 0) {\r
- for(int i=0; replicationValve == null && i < valves.length ; i++ ){\r
- if(valves[i] instanceof ReplicationValve) replicationValve = (ReplicationValve)valves[i] ;\r
- }//for\r
-\r
- if(replicationValve == null && log.isDebugEnabled()) {\r
- log.debug("no ReplicationValve found for CrossContext Support");\r
- }//endif \r
- }//end if\r
- }//endif\r
- }//end if\r
- }//end if\r
- if(replicationValve != null) {\r
- replicationValve.registerReplicationSession(session);\r
- }\r
- }\r
- \r
- /**\r
- * Find the master of the session state\r
- * @return master member of sessions \r
- */\r
- protected Member findSessionMasterMember() {\r
- Member mbr = null;\r
- Member mbrs[] = cluster.getMembers();\r
- if(mbrs.length != 0 ) mbr = mbrs[0];\r
- if(mbr == null && log.isWarnEnabled()) log.warn(sm.getString("deltaManager.noMasterMember",getName(), ""));\r
- if(mbr != null && log.isDebugEnabled()) log.warn(sm.getString("deltaManager.foundMasterMember",getName(), mbr));\r
- return mbr;\r
- }\r
-\r
- /**\r
- * Wait that cluster session state is transfer or timeout after 60 Sec\r
- * With stateTransferTimeout == -1 wait that backup is transfered (forever mode)\r
- */\r
- protected void waitForSendAllSessions(long beforeSendTime) {\r
- long reqStart = System.currentTimeMillis();\r
- long reqNow = reqStart ;\r
- boolean isTimeout = false;\r
- if(getStateTransferTimeout() > 0) {\r
- // wait that state is transfered with timeout check\r
- do {\r
- try {\r
- Thread.sleep(100);\r
- } catch (Exception sleep) {\r
- //\r
- }\r
- reqNow = System.currentTimeMillis();\r
- isTimeout = ((reqNow - reqStart) > (1000 * getStateTransferTimeout()));\r
- } while ((!getStateTransfered()) && (!isTimeout));\r
- } else {\r
- if(getStateTransferTimeout() == -1) {\r
- // wait that state is transfered\r
- do {\r
- try {\r
- Thread.sleep(100);\r
- } catch (Exception sleep) {\r
- }\r
- } while ((!getStateTransfered()));\r
- reqNow = System.currentTimeMillis();\r
- }\r
- }\r
- if (isTimeout || (!getStateTransfered())) {\r
- counterNoStateTransfered++ ;\r
- log.error(sm.getString("deltaManager.noSessionState",getName(),new Date(beforeSendTime),new Long(reqNow - beforeSendTime)));\r
- } else {\r
- if (log.isInfoEnabled())\r
- log.info(sm.getString("deltaManager.sessionReceived",getName(), new Date(beforeSendTime), new Long(reqNow - beforeSendTime)));\r
- }\r
- }\r
-\r
- /**\r
- * Gracefully terminate the active use of the public methods of this\r
- * component. This method should be the last one called on a given instance\r
- * of this component.\r
- * \r
- * @exception LifecycleException\r
- * if this component detects a fatal error that needs to be\r
- * reported\r
- */\r
- public void stop() throws LifecycleException {\r
-\r
- if (log.isDebugEnabled())\r
- log.debug(sm.getString("deltaManager.stopped", getName()));\r
-\r
-\r
- // Validate and update our current component state\r
- if (!started)\r
- throw new LifecycleException(sm.getString("deltaManager.notStarted"));\r
- lifecycle.fireLifecycleEvent(STOP_EVENT, null);\r
- started = false;\r
-\r
- // Expire all active sessions\r
- if (log.isInfoEnabled()) log.info(sm.getString("deltaManager.expireSessions", getName()));\r
- Session sessions[] = findSessions();\r
- for (int i = 0; i < sessions.length; i++) {\r
- DeltaSession session = (DeltaSession) sessions[i];\r
- if (!session.isValid())\r
- continue;\r
- try {\r
- session.expire(true, isExpireSessionsOnShutdown());\r
- } catch (Throwable ignore) {\r
- ;\r
- } \r
- }\r
-\r
- // Require a new random number generator if we are restarted\r
- this.random = null;\r
- getCluster().removeManager(this);\r
- replicationValve = null;\r
- if (initialized) {\r
- destroy();\r
- }\r
- }\r
-\r
- // ----------------------------------------- PropertyChangeListener Methods\r
-\r
- /**\r
- * Process property change events from our associated Context.\r
- * \r
- * @param event\r
- * The property change event that has occurred\r
- */\r
- public void propertyChange(PropertyChangeEvent event) {\r
-\r
- // Validate the source of this event\r
- if (!(event.getSource() instanceof Context))\r
- return;\r
- // Process a relevant property change\r
- if (event.getPropertyName().equals("sessionTimeout")) {\r
- try {\r
- setMaxInactiveInterval(((Integer) event.getNewValue()).intValue() * 60);\r
- } catch (NumberFormatException e) {\r
- log.error(sm.getString("deltaManager.sessionTimeout", event.getNewValue()));\r
- }\r
- }\r
-\r
- }\r
-\r
- // -------------------------------------------------------- Replication\r
- // Methods\r
-\r
- /**\r
- * A message was received from another node, this is the callback method to\r
- * implement if you are interested in receiving replication messages.\r
- * \r
- * @param cmsg -\r
- * the message received.\r
- */\r
- public void messageDataReceived(ClusterMessage cmsg) {\r
- if (cmsg != null && cmsg instanceof SessionMessage) {\r
- SessionMessage msg = (SessionMessage) cmsg;\r
- switch (msg.getEventType()) {\r
- case SessionMessage.EVT_GET_ALL_SESSIONS:\r
- case SessionMessage.EVT_SESSION_CREATED: \r
- case SessionMessage.EVT_SESSION_EXPIRED: \r
- case SessionMessage.EVT_SESSION_ACCESSED:\r
- case SessionMessage.EVT_SESSION_DELTA: {\r
- synchronized(receivedMessageQueue) {\r
- if(receiverQueue) {\r
- receivedMessageQueue.add(msg);\r
- return ;\r
- }\r
- }\r
- break;\r
- }\r
- default: {\r
- //we didn't queue, do nothing\r
- break;\r
- }\r
- } //switch\r
- \r
- messageReceived(msg, msg.getAddress() != null ? (Member) msg.getAddress() : null);\r
- }\r
- }\r
-\r
- /**\r
- * When the request has been completed, the replication valve will notify\r
- * the manager, and the manager will decide whether any replication is\r
- * needed or not. If there is a need for replication, the manager will\r
- * create a session message and that will be replicated. The cluster\r
- * determines where it gets sent.\r
- * \r
- * @param sessionId -\r
- * the sessionId that just completed.\r
- * @return a SessionMessage to be sent,\r
- */\r
- public ClusterMessage requestCompleted(String sessionId) {\r
- try {\r
- DeltaSession session = (DeltaSession) findSession(sessionId);\r
- DeltaRequest deltaRequest = session.getDeltaRequest();\r
- SessionMessage msg = null;\r
- boolean isDeltaRequest = false ;\r
- synchronized(deltaRequest) {\r
- isDeltaRequest = deltaRequest.getSize() > 0 ;\r
- if (isDeltaRequest) { \r
- counterSend_EVT_SESSION_DELTA++;\r
- byte[] data = serializeDeltaRequest(deltaRequest);\r
- msg = new SessionMessageImpl(getName(),\r
- SessionMessage.EVT_SESSION_DELTA, \r
- data, \r
- sessionId,\r
- sessionId + "-" + System.currentTimeMillis());\r
- session.resetDeltaRequest();\r
- } \r
- }\r
- if(!isDeltaRequest) {\r
- if(!session.isPrimarySession()) { \r
- counterSend_EVT_SESSION_ACCESSED++;\r
- msg = new SessionMessageImpl(getName(),\r
- SessionMessage.EVT_SESSION_ACCESSED, \r
- null, \r
- sessionId,\r
- sessionId + "-" + System.currentTimeMillis());\r
- if (log.isDebugEnabled()) {\r
- log.debug(sm.getString("deltaManager.createMessage.accessChangePrimary",getName(), sessionId));\r
- }\r
- } \r
- } else { // log only outside synch block!\r
- if (log.isDebugEnabled()) {\r
- log.debug(sm.getString("deltaManager.createMessage.delta",getName(), sessionId));\r
- }\r
- }\r
- session.setPrimarySession(true);\r
- //check to see if we need to send out an access message\r
- if ((msg == null)) {\r
- long replDelta = System.currentTimeMillis() - session.getLastTimeReplicated();\r
- if (replDelta > (getMaxInactiveInterval() * 1000)) {\r
- counterSend_EVT_SESSION_ACCESSED++;\r
- msg = new SessionMessageImpl(getName(),\r
- SessionMessage.EVT_SESSION_ACCESSED, \r
- null,\r
- sessionId, \r
- sessionId + "-" + System.currentTimeMillis());\r
- if (log.isDebugEnabled()) {\r
- log.debug(sm.getString("deltaManager.createMessage.access", getName(),sessionId));\r
- }\r
- }\r
-\r
- }\r
-\r
- //update last replicated time\r
- if (msg != null) session.setLastTimeReplicated(System.currentTimeMillis());\r
- return msg;\r
- } catch (IOException x) {\r
- log.error(sm.getString("deltaManager.createMessage.unableCreateDeltaRequest",sessionId), x);\r
- return null;\r
- }\r
-\r
- }\r
- /**\r
- * Reset manager statistics\r
- */\r
- public synchronized void resetStatistics() {\r
- processingTime = 0 ;\r
- expiredSessions = 0 ;\r
- rejectedSessions = 0 ;\r
- sessionReplaceCounter = 0 ;\r
- counterNoStateTransfered = 0 ;\r
- maxActive = getActiveSessions() ;\r
- sessionCounter = getActiveSessions() ;\r
- counterReceive_EVT_ALL_SESSION_DATA = 0;\r
- counterReceive_EVT_GET_ALL_SESSIONS = 0;\r
- counterReceive_EVT_SESSION_ACCESSED = 0 ;\r
- counterReceive_EVT_SESSION_CREATED = 0 ;\r
- counterReceive_EVT_SESSION_DELTA = 0 ;\r
- counterReceive_EVT_SESSION_EXPIRED = 0 ;\r
- counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0;\r
- counterSend_EVT_ALL_SESSION_DATA = 0;\r
- counterSend_EVT_GET_ALL_SESSIONS = 0;\r
- counterSend_EVT_SESSION_ACCESSED = 0 ;\r
- counterSend_EVT_SESSION_CREATED = 0 ;\r
- counterSend_EVT_SESSION_DELTA = 0 ;\r
- counterSend_EVT_SESSION_EXPIRED = 0 ;\r
- counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0;\r
- \r
- }\r
- \r
- // -------------------------------------------------------- persistence handler\r
-\r
- public void load() {\r
-\r
- }\r
-\r
- public void unload() {\r
-\r
- }\r
-\r
- // -------------------------------------------------------- expire\r
-\r
- /**\r
- * send session expired to other cluster nodes\r
- * \r
- * @param id\r
- * session id\r
- */\r
- protected void sessionExpired(String id) {\r
- counterSend_EVT_SESSION_EXPIRED++ ;\r
- SessionMessage msg = new SessionMessageImpl(getName(),SessionMessage.EVT_SESSION_EXPIRED, null, id, id+ "-EXPIRED-MSG");\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.createMessage.expire",getName(), id));\r
- send(msg);\r
- }\r
-\r
- /**\r
- * Exipre all find sessions.\r
- */\r
- public void expireAllLocalSessions()\r
- {\r
- long timeNow = System.currentTimeMillis();\r
- Session sessions[] = findSessions();\r
- int expireDirect = 0 ;\r
- int expireIndirect = 0 ;\r
- \r
- if(log.isDebugEnabled()) log.debug("Start expire all sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);\r
- for (int i = 0; i < sessions.length; i++) {\r
- if (sessions[i] instanceof DeltaSession) {\r
- DeltaSession session = (DeltaSession) sessions[i];\r
- if (session.isPrimarySession()) {\r
- if (session.isValid()) {\r
- session.expire();\r
- expireDirect++;\r
- } else {\r
- expireIndirect++;\r
- }//end if\r
- }//end if\r
- }//end if\r
- }//for\r
- long timeEnd = System.currentTimeMillis();\r
- if(log.isDebugEnabled()) log.debug("End expire sessions " + getName() + " exipre processingTime " + (timeEnd - timeNow) + " expired direct sessions: " + expireDirect + " expired direct sessions: " + expireIndirect);\r
- \r
- }\r
- \r
- /**\r
- * When the manager expires session not tied to a request. The cluster will\r
- * periodically ask for a list of sessions that should expire and that\r
- * should be sent across the wire.\r
- * \r
- * @return The invalidated sessions array\r
- */\r
- public String[] getInvalidatedSessions() {\r
- return new String[0];\r
- }\r
-\r
- // -------------------------------------------------------- message receive\r
-\r
- /**\r
- * Test that sender and local domain is the same\r
- */\r
- protected boolean checkSenderDomain(SessionMessage msg,Member sender) {\r
- boolean sameDomain= true;\r
- if (!sameDomain && log.isWarnEnabled()) {\r
- log.warn(sm.getString("deltaManager.receiveMessage.fromWrongDomain",\r
- new Object[] {getName(), \r
- msg.getEventTypeString(), \r
- sender,\r
- "",\r
- "" }));\r
- }\r
- return sameDomain ;\r
- }\r
-\r
- /**\r
- * This method is called by the received thread when a SessionMessage has\r
- * been received from one of the other nodes in the cluster.\r
- * \r
- * @param msg -\r
- * the message received\r
- * @param sender -\r
- * the sender of the message, this is used if we receive a\r
- * EVT_GET_ALL_SESSION message, so that we only reply to the\r
- * requesting node\r
- */\r
- protected void messageReceived(SessionMessage msg, Member sender) {\r
- if(doDomainReplication() && !checkSenderDomain(msg,sender)) {\r
- return;\r
- }\r
- ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();\r
- try {\r
- \r
- ClassLoader[] loaders = getClassLoaders();\r
- if ( loaders != null && loaders.length > 0) Thread.currentThread().setContextClassLoader(loaders[0]);\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.eventType",getName(), msg.getEventTypeString(), sender));\r
- \r
- switch (msg.getEventType()) {\r
- case SessionMessage.EVT_GET_ALL_SESSIONS: {\r
- handleGET_ALL_SESSIONS(msg,sender);\r
- break;\r
- }\r
- case SessionMessage.EVT_ALL_SESSION_DATA: {\r
- handleALL_SESSION_DATA(msg,sender);\r
- break;\r
- }\r
- case SessionMessage.EVT_ALL_SESSION_TRANSFERCOMPLETE: {\r
- handleALL_SESSION_TRANSFERCOMPLETE(msg,sender);\r
- break;\r
- }\r
- case SessionMessage.EVT_SESSION_CREATED: {\r
- handleSESSION_CREATED(msg,sender);\r
- break;\r
- }\r
- case SessionMessage.EVT_SESSION_EXPIRED: {\r
- handleSESSION_EXPIRED(msg,sender);\r
- break;\r
- }\r
- case SessionMessage.EVT_SESSION_ACCESSED: {\r
- handleSESSION_ACCESSED(msg,sender);\r
- break;\r
- }\r
- case SessionMessage.EVT_SESSION_DELTA: {\r
- handleSESSION_DELTA(msg,sender);\r
- break;\r
- }\r
- default: {\r
- //we didn't recognize the message type, do nothing\r
- break;\r
- }\r
- } //switch\r
- } catch (Exception x) {\r
- log.error(sm.getString("deltaManager.receiveMessage.error",getName()), x);\r
- } finally {\r
- Thread.currentThread().setContextClassLoader(contextLoader);\r
- }\r
- }\r
-\r
- // -------------------------------------------------------- message receiver handler\r
-\r
-\r
- /**\r
- * handle receive session state is complete transfered\r
- * @param msg\r
- * @param sender\r
- */\r
- protected void handleALL_SESSION_TRANSFERCOMPLETE(SessionMessage msg, Member sender) {\r
- counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE++ ;\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.transfercomplete",getName(), sender.getHost(), new Integer(sender.getPort())));\r
- stateTransferCreateSendTime = msg.getTimestamp() ;\r
- stateTransfered = true ;\r
- }\r
-\r
- /**\r
- * handle receive session delta\r
- * @param msg\r
- * @param sender\r
- * @throws IOException\r
- * @throws ClassNotFoundException\r
- */\r
- protected void handleSESSION_DELTA(SessionMessage msg, Member sender) throws IOException, ClassNotFoundException {\r
- counterReceive_EVT_SESSION_DELTA++;\r
- byte[] delta = msg.getSession();\r
- DeltaSession session = (DeltaSession) findSession(msg.getSessionID());\r
- if (session != null) {\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.delta",getName(), msg.getSessionID()));\r
- DeltaRequest dreq = deserializeDeltaRequest(session, delta);\r
- dreq.execute(session, notifyListenersOnReplication);\r
- session.setPrimarySession(false);\r
- }\r
- }\r
-\r
- /**\r
- * handle receive session is access at other node ( primary session is now false)\r
- * @param msg\r
- * @param sender\r
- * @throws IOException\r
- */\r
- protected void handleSESSION_ACCESSED(SessionMessage msg,Member sender) throws IOException {\r
- counterReceive_EVT_SESSION_ACCESSED++;\r
- DeltaSession session = (DeltaSession) findSession(msg.getSessionID());\r
- if (session != null) {\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.accessed",getName(), msg.getSessionID()));\r
- session.access();\r
- session.setPrimarySession(false);\r
- session.endAccess();\r
- }\r
- }\r
-\r
- /**\r
- * handle receive session is expire at other node ( expire session also here)\r
- * @param msg\r
- * @param sender\r
- * @throws IOException\r
- */\r
- protected void handleSESSION_EXPIRED(SessionMessage msg,Member sender) throws IOException {\r
- counterReceive_EVT_SESSION_EXPIRED++;\r
- DeltaSession session = (DeltaSession) findSession(msg.getSessionID());\r
- if (session != null) {\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.expired",getName(), msg.getSessionID()));\r
- session.expire(notifySessionListenersOnReplication, false);\r
- }\r
- }\r
-\r
- /**\r
- * handle receive new session is created at other node (create backup - primary false)\r
- * @param msg\r
- * @param sender\r
- */\r
- protected void handleSESSION_CREATED(SessionMessage msg,Member sender) {\r
- counterReceive_EVT_SESSION_CREATED++;\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.createNewSession",getName(), msg.getSessionID()));\r
- DeltaSession session = (DeltaSession) createEmptySession();\r
- session.setManager(this);\r
- session.setValid(true);\r
- session.setPrimarySession(false);\r
- session.setCreationTime(msg.getTimestamp());\r
- session.access();\r
- if(notifySessionListenersOnReplication)\r
- session.setId(msg.getSessionID());\r
- else\r
- session.setIdInternal(msg.getSessionID());\r
- session.resetDeltaRequest();\r
- session.endAccess();\r
-\r
- }\r
-\r
- /**\r
- * handle receive sessions from other not ( restart )\r
- * @param msg\r
- * @param sender\r
- * @throws ClassNotFoundException\r
- * @throws IOException\r
- */\r
- protected void handleALL_SESSION_DATA(SessionMessage msg,Member sender) throws ClassNotFoundException, IOException {\r
- counterReceive_EVT_ALL_SESSION_DATA++;\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.allSessionDataBegin",getName()));\r
- byte[] data = msg.getSession();\r
- deserializeSessions(data);\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.allSessionDataAfter",getName()));\r
- //stateTransferred = true;\r
- }\r
-\r
- /**\r
- * handle receive that other node want all sessions ( restart )\r
- * a) send all sessions with one message\r
- * b) send session at blocks\r
- * After sending send state is complete transfered\r
- * @param msg\r
- * @param sender\r
- * @throws IOException\r
- */\r
- protected void handleGET_ALL_SESSIONS(SessionMessage msg, Member sender) throws IOException {\r
- counterReceive_EVT_GET_ALL_SESSIONS++;\r
- //get a list of all the session from this manager\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.unloadingBegin", getName()));\r
- // Write the number of active sessions, followed by the details\r
- // get all sessions and serialize without sync\r
- Session[] currentSessions = findSessions();\r
- long findSessionTimestamp = System.currentTimeMillis() ;\r
- if (isSendAllSessions()) {\r
- sendSessions(sender, currentSessions, findSessionTimestamp);\r
- } else {\r
- // send session at blocks\r
- int len = currentSessions.length < getSendAllSessionsSize() ? currentSessions.length : getSendAllSessionsSize();\r
- Session[] sendSessions = new Session[len];\r
- for (int i = 0; i < currentSessions.length; i += getSendAllSessionsSize()) {\r
- len = i + getSendAllSessionsSize() > currentSessions.length ? currentSessions.length - i : getSendAllSessionsSize();\r
- System.arraycopy(currentSessions, i, sendSessions, 0, len);\r
- sendSessions(sender, sendSessions,findSessionTimestamp);\r
- if (getSendAllSessionsWaitTime() > 0) {\r
- try {\r
- Thread.sleep(getSendAllSessionsWaitTime());\r
- } catch (Exception sleep) {\r
- }\r
- }//end if\r
- }//for\r
- }//end if\r
- \r
- SessionMessage newmsg = new SessionMessageImpl(name,SessionMessage.EVT_ALL_SESSION_TRANSFERCOMPLETE, null,"SESSION-STATE-TRANSFERED", "SESSION-STATE-TRANSFERED"+ getName());\r
- newmsg.setTimestamp(findSessionTimestamp);\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.createMessage.allSessionTransfered",getName()));\r
- counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE++;\r
- cluster.send(newmsg, sender);\r
- }\r
-\r
-\r
- /**\r
- * send a block of session to sender\r
- * @param sender\r
- * @param currentSessions\r
- * @param sendTimestamp\r
- * @throws IOException\r
- */\r
- protected void sendSessions(Member sender, Session[] currentSessions,long sendTimestamp) throws IOException {\r
- byte[] data = serializeSessions(currentSessions);\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.unloadingAfter",getName()));\r
- SessionMessage newmsg = new SessionMessageImpl(name,SessionMessage.EVT_ALL_SESSION_DATA, data,"SESSION-STATE", "SESSION-STATE-" + getName());\r
- newmsg.setTimestamp(sendTimestamp);\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.createMessage.allSessionData",getName()));\r
- counterSend_EVT_ALL_SESSION_DATA++;\r
- cluster.send(newmsg, sender);\r
- }\r
-\r
- public ClusterManager cloneFromTemplate() {\r
- DeltaManager result = new DeltaManager();\r
- result.name = "Clone-from-"+name;\r
- result.cluster = cluster;\r
- result.replicationValve = replicationValve;\r
- result.maxActiveSessions = maxActiveSessions;\r
- result.expireSessionsOnShutdown = expireSessionsOnShutdown;\r
- result.notifyListenersOnReplication = notifyListenersOnReplication;\r
- result.notifySessionListenersOnReplication = notifySessionListenersOnReplication;\r
- result.stateTransferTimeout = stateTransferTimeout;\r
- result.sendAllSessions = sendAllSessions;\r
- result.sendClusterDomainOnly = sendClusterDomainOnly ;\r
- result.sendAllSessionsSize = sendAllSessionsSize;\r
- result.sendAllSessionsWaitTime = sendAllSessionsWaitTime ; \r
- result.receiverQueue = receiverQueue ;\r
- result.stateTimestampDrop = stateTimestampDrop ;\r
- result.stateTransferCreateSendTime = stateTransferCreateSendTime; \r
- return result;\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import java.beans.PropertyChangeEvent;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.apache.catalina.Cluster;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Session;
+import org.apache.catalina.Valve;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.ha.tcp.ReplicationValve;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.io.ReplicationStream;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.ha.ClusterManager;
+
+/**
+ * The DeltaManager manages replicated sessions by only replicating the deltas
+ * in data. For applications written to handle this, the DeltaManager is the
+ * optimal way of replicating data.
+ *
+ * This code is almost identical to StandardManager with a difference in how it
+ * persists sessions and some modifications to it.
+ *
+ * <b>IMPLEMENTATION NOTE </b>: Correct behavior of session storing and
+ * reloading depends upon external calls to the <code>start()</code> and
+ * <code>stop()</code> methods of this class at the correct times.
+ *
+ * @author Filip Hanik
+ * @author Craig R. McClanahan
+ * @author Jean-Francois Arcand
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class DeltaManager extends ClusterManagerBase{
+
+ // ---------------------------------------------------- Security Classes
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(DeltaManager.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm = StringManager.getManager(Constants.Package);
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * The descriptive information about this implementation.
+ */
+ private static final String info = "DeltaManager/2.1";
+
+ /**
+ * Has this component been started yet?
+ */
+ private boolean started = false;
+
+ /**
+ * The descriptive name of this Manager implementation (for logging).
+ */
+ protected static String managerName = "DeltaManager";
+ protected String name = null;
+ protected boolean defaultMode = false;
+ private CatalinaCluster cluster = null;
+
+ /**
+ * cached replication valve cluster container!
+ */
+ private ReplicationValve replicationValve = null ;
+
+ /**
+ * The lifecycle event support for this component.
+ */
+ protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /**
+ * The maximum number of active Sessions allowed, or -1 for no limit.
+ */
+ private int maxActiveSessions = -1;
+ private boolean expireSessionsOnShutdown = false;
+ private boolean notifyListenersOnReplication = true;
+ private boolean notifySessionListenersOnReplication = true;
+ private boolean stateTransfered = false ;
+ private int stateTransferTimeout = 60;
+ private boolean sendAllSessions = true;
+ private boolean sendClusterDomainOnly = true ;
+ private int sendAllSessionsSize = 1000 ;
+
+ /**
+ * wait time between send session block (default 2 sec)
+ */
+ private int sendAllSessionsWaitTime = 2 * 1000 ;
+ private ArrayList receivedMessageQueue = new ArrayList() ;
+ private boolean receiverQueue = false ;
+ private boolean stateTimestampDrop = true ;
+ private long stateTransferCreateSendTime;
+
+ // ------------------------------------------------------------------ stats attributes
+
+ int rejectedSessions = 0;
+ private long sessionReplaceCounter = 0 ;
+ long processingTime = 0;
+ private long counterReceive_EVT_GET_ALL_SESSIONS = 0 ;
+ private long counterSend_EVT_ALL_SESSION_DATA = 0 ;
+ private long counterReceive_EVT_ALL_SESSION_DATA = 0 ;
+ private long counterReceive_EVT_SESSION_CREATED = 0 ;
+ private long counterReceive_EVT_SESSION_EXPIRED = 0;
+ private long counterReceive_EVT_SESSION_ACCESSED = 0 ;
+ private long counterReceive_EVT_SESSION_DELTA = 0;
+ private long counterSend_EVT_GET_ALL_SESSIONS = 0 ;
+ private long counterSend_EVT_SESSION_CREATED = 0;
+ private long counterSend_EVT_SESSION_DELTA = 0 ;
+ private long counterSend_EVT_SESSION_ACCESSED = 0;
+ private long counterSend_EVT_SESSION_EXPIRED = 0;
+ private int counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0 ;
+ private int counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0 ;
+ private int counterNoStateTransfered = 0 ;
+
+
+ // ------------------------------------------------------------- Constructor
+ public DeltaManager() {
+ super();
+ }
+
+ // ------------------------------------------------------------- Properties
+
+ /**
+ * Return descriptive information about this Manager implementation and the
+ * corresponding version number, in the format
+ * <code><description>/<version></code>.
+ */
+ public String getInfo() {
+ return info;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Return the descriptive short name of this Manager implementation.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return Returns the counterSend_EVT_GET_ALL_SESSIONS.
+ */
+ public long getCounterSend_EVT_GET_ALL_SESSIONS() {
+ return counterSend_EVT_GET_ALL_SESSIONS;
+ }
+
+ /**
+ * @return Returns the counterSend_EVT_SESSION_ACCESSED.
+ */
+ public long getCounterSend_EVT_SESSION_ACCESSED() {
+ return counterSend_EVT_SESSION_ACCESSED;
+ }
+
+ /**
+ * @return Returns the counterSend_EVT_SESSION_CREATED.
+ */
+ public long getCounterSend_EVT_SESSION_CREATED() {
+ return counterSend_EVT_SESSION_CREATED;
+ }
+
+ /**
+ * @return Returns the counterSend_EVT_SESSION_DELTA.
+ */
+ public long getCounterSend_EVT_SESSION_DELTA() {
+ return counterSend_EVT_SESSION_DELTA;
+ }
+
+ /**
+ * @return Returns the counterSend_EVT_SESSION_EXPIRED.
+ */
+ public long getCounterSend_EVT_SESSION_EXPIRED() {
+ return counterSend_EVT_SESSION_EXPIRED;
+ }
+
+ /**
+ * @return Returns the counterSend_EVT_ALL_SESSION_DATA.
+ */
+ public long getCounterSend_EVT_ALL_SESSION_DATA() {
+ return counterSend_EVT_ALL_SESSION_DATA;
+ }
+
+ /**
+ * @return Returns the counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE.
+ */
+ public int getCounterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE() {
+ return counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE;
+ }
+
+ /**
+ * @return Returns the counterReceive_EVT_ALL_SESSION_DATA.
+ */
+ public long getCounterReceive_EVT_ALL_SESSION_DATA() {
+ return counterReceive_EVT_ALL_SESSION_DATA;
+ }
+
+ /**
+ * @return Returns the counterReceive_EVT_GET_ALL_SESSIONS.
+ */
+ public long getCounterReceive_EVT_GET_ALL_SESSIONS() {
+ return counterReceive_EVT_GET_ALL_SESSIONS;
+ }
+
+ /**
+ * @return Returns the counterReceive_EVT_SESSION_ACCESSED.
+ */
+ public long getCounterReceive_EVT_SESSION_ACCESSED() {
+ return counterReceive_EVT_SESSION_ACCESSED;
+ }
+
+ /**
+ * @return Returns the counterReceive_EVT_SESSION_CREATED.
+ */
+ public long getCounterReceive_EVT_SESSION_CREATED() {
+ return counterReceive_EVT_SESSION_CREATED;
+ }
+
+ /**
+ * @return Returns the counterReceive_EVT_SESSION_DELTA.
+ */
+ public long getCounterReceive_EVT_SESSION_DELTA() {
+ return counterReceive_EVT_SESSION_DELTA;
+ }
+
+ /**
+ * @return Returns the counterReceive_EVT_SESSION_EXPIRED.
+ */
+ public long getCounterReceive_EVT_SESSION_EXPIRED() {
+ return counterReceive_EVT_SESSION_EXPIRED;
+ }
+
+
+ /**
+ * @return Returns the counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE.
+ */
+ public int getCounterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE() {
+ return counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE;
+ }
+
+ /**
+ * @return Returns the processingTime.
+ */
+ public long getProcessingTime() {
+ return processingTime;
+ }
+
+ /**
+ * @return Returns the sessionReplaceCounter.
+ */
+ public long getSessionReplaceCounter() {
+ return sessionReplaceCounter;
+ }
+
+ /**
+ * Number of session creations that failed due to maxActiveSessions
+ *
+ * @return The count
+ */
+ public int getRejectedSessions() {
+ return rejectedSessions;
+ }
+
+ public void setRejectedSessions(int rejectedSessions) {
+ this.rejectedSessions = rejectedSessions;
+ }
+
+ /**
+ * @return Returns the counterNoStateTransfered.
+ */
+ public int getCounterNoStateTransfered() {
+ return counterNoStateTransfered;
+ }
+
+ public int getReceivedQueueSize() {
+ return receivedMessageQueue.size() ;
+ }
+
+ /**
+ * @return Returns the stateTransferTimeout.
+ */
+ public int getStateTransferTimeout() {
+ return stateTransferTimeout;
+ }
+ /**
+ * @param timeoutAllSession The timeout
+ */
+ public void setStateTransferTimeout(int timeoutAllSession) {
+ this.stateTransferTimeout = timeoutAllSession;
+ }
+
+ /**
+ * is session state transfered complete?
+ *
+ */
+ public boolean getStateTransfered() {
+ return stateTransfered;
+ }
+
+ /**
+ * set that state ist complete transfered
+ * @param stateTransfered
+ */
+ public void setStateTransfered(boolean stateTransfered) {
+ this.stateTransfered = stateTransfered;
+ }
+
+ /**
+ * @return Returns the sendAllSessionsWaitTime in msec
+ */
+ public int getSendAllSessionsWaitTime() {
+ return sendAllSessionsWaitTime;
+ }
+
+ /**
+ * @param sendAllSessionsWaitTime The sendAllSessionsWaitTime to set at msec.
+ */
+ public void setSendAllSessionsWaitTime(int sendAllSessionsWaitTime) {
+ this.sendAllSessionsWaitTime = sendAllSessionsWaitTime;
+ }
+
+ /**
+ * @return Returns the sendClusterDomainOnly.
+ */
+ public boolean doDomainReplication() {
+ return sendClusterDomainOnly;
+ }
+
+ /**
+ * @param sendClusterDomainOnly The sendClusterDomainOnly to set.
+ */
+ public void setDomainReplication(boolean sendClusterDomainOnly) {
+ this.sendClusterDomainOnly = sendClusterDomainOnly;
+ }
+
+ /**
+ * @return Returns the stateTimestampDrop.
+ */
+ public boolean isStateTimestampDrop() {
+ return stateTimestampDrop;
+ }
+
+ /**
+ * @param isTimestampDrop The new flag value
+ */
+ public void setStateTimestampDrop(boolean isTimestampDrop) {
+ this.stateTimestampDrop = isTimestampDrop;
+ }
+
+ /**
+ * Return the maximum number of active Sessions allowed, or -1 for no limit.
+ */
+ public int getMaxActiveSessions() {
+ return (this.maxActiveSessions);
+ }
+
+ /**
+ * Set the maximum number of actives Sessions allowed, or -1 for no limit.
+ *
+ * @param max
+ * The new maximum number of sessions
+ */
+ public void setMaxActiveSessions(int max) {
+ int oldMaxActiveSessions = this.maxActiveSessions;
+ this.maxActiveSessions = max;
+ support.firePropertyChange("maxActiveSessions", new Integer(oldMaxActiveSessions), new Integer(this.maxActiveSessions));
+ }
+
+ /**
+ *
+ * @return Returns the sendAllSessions.
+ */
+ public boolean isSendAllSessions() {
+ return sendAllSessions;
+ }
+
+ /**
+ * @param sendAllSessions The sendAllSessions to set.
+ */
+ public void setSendAllSessions(boolean sendAllSessions) {
+ this.sendAllSessions = sendAllSessions;
+ }
+
+ /**
+ * @return Returns the sendAllSessionsSize.
+ */
+ public int getSendAllSessionsSize() {
+ return sendAllSessionsSize;
+ }
+
+ /**
+ * @param sendAllSessionsSize The sendAllSessionsSize to set.
+ */
+ public void setSendAllSessionsSize(int sendAllSessionsSize) {
+ this.sendAllSessionsSize = sendAllSessionsSize;
+ }
+
+ /**
+ * @return Returns the notifySessionListenersOnReplication.
+ */
+ public boolean isNotifySessionListenersOnReplication() {
+ return notifySessionListenersOnReplication;
+ }
+
+ /**
+ * @param notifyListenersCreateSessionOnReplication The notifySessionListenersOnReplication to set.
+ */
+ public void setNotifySessionListenersOnReplication(boolean notifyListenersCreateSessionOnReplication) {
+ this.notifySessionListenersOnReplication = notifyListenersCreateSessionOnReplication;
+ }
+
+
+ public boolean isExpireSessionsOnShutdown() {
+ return expireSessionsOnShutdown;
+ }
+
+ public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown) {
+ this.expireSessionsOnShutdown = expireSessionsOnShutdown;
+ }
+
+ public boolean isNotifyListenersOnReplication() {
+ return notifyListenersOnReplication;
+ }
+
+ public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
+ this.notifyListenersOnReplication = notifyListenersOnReplication;
+ }
+
+
+ /**
+ * @return Returns the defaultMode.
+ */
+ public boolean isDefaultMode() {
+ return defaultMode;
+ }
+ /**
+ * @param defaultMode The defaultMode to set.
+ */
+ public void setDefaultMode(boolean defaultMode) {
+ this.defaultMode = defaultMode;
+ }
+
+ public CatalinaCluster getCluster() {
+ return cluster;
+ }
+
+ public void setCluster(CatalinaCluster cluster) {
+ this.cluster = cluster;
+ }
+
+ /**
+ * Set the Container with which this Manager has been associated. If it is a
+ * Context (the usual case), listen for changes to the session timeout
+ * property.
+ *
+ * @param container
+ * The associated Container
+ */
+ public void setContainer(Container container) {
+ // De-register from the old Container (if any)
+ if ((this.container != null) && (this.container instanceof Context))
+ ((Context) this.container).removePropertyChangeListener(this);
+
+ // Default processing provided by our superclass
+ super.setContainer(container);
+
+ // Register with the new Container (if any)
+ if ((this.container != null) && (this.container instanceof Context)) {
+ setMaxInactiveInterval(((Context) this.container).getSessionTimeout() * 60);
+ ((Context) this.container).addPropertyChangeListener(this);
+ }
+
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * Construct and return a new session object, based on the default settings
+ * specified by this Manager's properties. The session id will be assigned
+ * by this method, and available via the getId() method of the returned
+ * session. If a new session cannot be created for any reason, return
+ * <code>null</code>.
+ *
+ * @exception IllegalStateException
+ * if a new session cannot be instantiated for any reason
+ *
+ * Construct and return a new session object, based on the default settings
+ * specified by this Manager's properties. The session id will be assigned
+ * by this method, and available via the getId() method of the returned
+ * session. If a new session cannot be created for any reason, return
+ * <code>null</code>.
+ *
+ * @exception IllegalStateException
+ * if a new session cannot be instantiated for any reason
+ */
+ public Session createSession(String sessionId) {
+ return createSession(sessionId, true);
+ }
+
+ /**
+ * create new session with check maxActiveSessions and send session creation
+ * to other cluster nodes.
+ *
+ * @param distribute
+ * @return The session
+ */
+ public Session createSession(String sessionId, boolean distribute) {
+ if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) {
+ rejectedSessions++;
+ throw new IllegalStateException(sm.getString("deltaManager.createSession.ise"));
+ }
+ DeltaSession session = (DeltaSession) super.createSession(sessionId) ;
+ if (distribute) {
+ sendCreateSession(session.getId(), session);
+ }
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("deltaManager.createSession.newSession",session.getId(), new Integer(sessions.size())));
+ return (session);
+
+ }
+
+ /**
+ * Send create session evt to all backup node
+ * @param sessionId
+ * @param session
+ */
+ protected void sendCreateSession(String sessionId, DeltaSession session) {
+ if(cluster.getMembers().length > 0 ) {
+ SessionMessage msg =
+ new SessionMessageImpl(getName(),
+ SessionMessage.EVT_SESSION_CREATED,
+ null,
+ sessionId,
+ sessionId + "-" + System.currentTimeMillis());
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.sendMessage.newSession",name, sessionId));
+ msg.setTimestamp(session.getCreationTime());
+ counterSend_EVT_SESSION_CREATED++;
+ send(msg);
+ }
+ }
+
+ /**
+ * Send messages to other backup member (domain or all)
+ * @param msg Session message
+ */
+ protected void send(SessionMessage msg) {
+ if(cluster != null) {
+ if(doDomainReplication())
+ cluster.sendClusterDomain(msg);
+ else
+ cluster.send(msg);
+ }
+ }
+
+ /**
+ * Create DeltaSession
+ * @see org.apache.catalina.Manager#createEmptySession()
+ */
+ public Session createEmptySession() {
+ return getNewDeltaSession() ;
+ }
+
+ /**
+ * Get new session class to be used in the doLoad() method.
+ */
+ protected DeltaSession getNewDeltaSession() {
+ return new DeltaSession(this);
+ }
+
+ /**
+ * Load Deltarequest from external node
+ * Load the Class at container classloader
+ * @see DeltaRequest#readExternal(java.io.ObjectInput)
+ * @param session
+ * @param data message data
+ * @return The request
+ * @throws ClassNotFoundException
+ * @throws IOException
+ */
+ protected DeltaRequest deserializeDeltaRequest(DeltaSession session, byte[] data) throws ClassNotFoundException, IOException {
+ ReplicationStream ois = getReplicationStream(data);
+ session.getDeltaRequest().readExternal(ois);
+ ois.close();
+ return session.getDeltaRequest();
+ }
+
+ /**
+ * serialize DeltaRequest
+ * @see DeltaRequest#writeExternal(java.io.ObjectOutput)
+ *
+ * @param deltaRequest
+ * @return serialized delta request
+ * @throws IOException
+ */
+ protected byte[] serializeDeltaRequest(DeltaRequest deltaRequest) throws IOException {
+ return deltaRequest.serialize();
+ }
+
+ /**
+ * Load sessions from other cluster node.
+ * FIXME replace currently sessions with same id without notifcation.
+ * FIXME SSO handling is not really correct with the session replacement!
+ * @exception ClassNotFoundException
+ * if a serialized class cannot be found during the reload
+ * @exception IOException
+ * if an input/output error occurs
+ */
+ protected void deserializeSessions(byte[] data) throws ClassNotFoundException,IOException {
+
+ // Initialize our internal data structures
+ //sessions.clear(); //should not do this
+ // Open an input stream to the specified pathname, if any
+ ClassLoader originalLoader = Thread.currentThread().getContextClassLoader();
+ ObjectInputStream ois = null;
+ // Load the previously unloaded active sessions
+ try {
+ ois = getReplicationStream(data);
+ Integer count = (Integer) ois.readObject();
+ int n = count.intValue();
+ for (int i = 0; i < n; i++) {
+ DeltaSession session = (DeltaSession) createEmptySession();
+ session.readObjectData(ois);
+ session.setManager(this);
+ session.setValid(true);
+ session.setPrimarySession(false);
+ //in case the nodes in the cluster are out of
+ //time synch, this will make sure that we have the
+ //correct timestamp, isValid returns true, cause
+ // accessCount=1
+ session.access();
+ //make sure that the session gets ready to expire if
+ // needed
+ session.setAccessCount(0);
+ session.resetDeltaRequest();
+ // FIXME How inform other session id cache like SingleSignOn
+ // increment sessionCounter to correct stats report
+ if (findSession(session.getIdInternal()) == null ) {
+ sessionCounter++;
+ } else {
+ sessionReplaceCounter++;
+ // FIXME better is to grap this sessions again !
+ if (log.isWarnEnabled()) log.warn(sm.getString("deltaManager.loading.existing.session",session.getIdInternal()));
+ }
+ add(session);
+ }
+ } catch (ClassNotFoundException e) {
+ log.error(sm.getString("deltaManager.loading.cnfe", e), e);
+ throw e;
+ } catch (IOException e) {
+ log.error(sm.getString("deltaManager.loading.ioe", e), e);
+ throw e;
+ } finally {
+ // Close the input stream
+ try {
+ if (ois != null) ois.close();
+ } catch (IOException f) {
+ // ignored
+ }
+ ois = null;
+ if (originalLoader != null) Thread.currentThread().setContextClassLoader(originalLoader);
+ }
+
+ }
+
+
+
+ /**
+ * Save any currently active sessions in the appropriate persistence
+ * mechanism, if any. If persistence is not supported, this method returns
+ * without doing anything.
+ *
+ * @exception IOException
+ * if an input/output error occurs
+ */
+ protected byte[] serializeSessions(Session[] currentSessions) throws IOException {
+
+ // Open an output stream to the specified pathname, if any
+ ByteArrayOutputStream fos = null;
+ ObjectOutputStream oos = null;
+
+ try {
+ fos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(new BufferedOutputStream(fos));
+ oos.writeObject(new Integer(currentSessions.length));
+ for(int i=0 ; i < currentSessions.length;i++) {
+ ((DeltaSession)currentSessions[i]).writeObjectData(oos);
+ }
+ // Flush and close the output stream
+ oos.flush();
+ } catch (IOException e) {
+ log.error(sm.getString("deltaManager.unloading.ioe", e), e);
+ throw e;
+ } finally {
+ if (oos != null) {
+ try {
+ oos.close();
+ } catch (IOException f) {
+ ;
+ }
+ oos = null;
+ }
+ }
+ // send object data as byte[]
+ return fos.toByteArray();
+ }
+
+ // ------------------------------------------------------ Lifecycle Methods
+
+ /**
+ * Add a lifecycle event listener to this component.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener) {
+ lifecycle.addLifecycleListener(listener);
+ }
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners() {
+ return lifecycle.findLifecycleListeners();
+ }
+
+ /**
+ * Remove a lifecycle event listener from this component.
+ *
+ * @param listener
+ * The listener to remove
+ */
+ public void removeLifecycleListener(LifecycleListener listener) {
+ lifecycle.removeLifecycleListener(listener);
+ }
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized.
+ *
+ * @exception LifecycleException
+ * if this component detects a fatal error that prevents this
+ * component from being used
+ */
+ public void start() throws LifecycleException {
+ if (!initialized) init();
+
+ // Validate and update our current component state
+ if (started) {
+ return;
+ }
+ started = true;
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+
+ // Force initialization of the random number generator
+ generateSessionId();
+
+ // Load unloaded sessions, if any
+ try {
+ //the channel is already running
+ Cluster cluster = getCluster() ;
+ // stop remove cluster binding
+ //wow, how many nested levels of if statements can we have ;)
+ if(cluster == null) {
+ Container context = getContainer() ;
+ if(context != null && context instanceof Context) {
+ Container host = context.getParent() ;
+ if(host != null && host instanceof Host) {
+ cluster = host.getCluster();
+ if(cluster != null && cluster instanceof CatalinaCluster) {
+ setCluster((CatalinaCluster) cluster) ;
+ } else {
+ Container engine = host.getParent() ;
+ if(engine != null && engine instanceof Engine) {
+ cluster = engine.getCluster();
+ if(cluster != null && cluster instanceof CatalinaCluster) {
+ setCluster((CatalinaCluster) cluster) ;
+ }
+ } else {
+ cluster = null ;
+ }
+ }
+ }
+ }
+ }
+ if (cluster == null) {
+ log.error(sm.getString("deltaManager.noCluster", getName()));
+ return;
+ } else {
+ if (log.isInfoEnabled()) {
+ String type = "unknown" ;
+ if( cluster.getContainer() instanceof Host){
+ type = "Host" ;
+ } else if( cluster.getContainer() instanceof Engine){
+ type = "Engine" ;
+ }
+ log.info(sm.getString("deltaManager.registerCluster", getName(), type, cluster.getClusterName()));
+ }
+ }
+ if (log.isInfoEnabled()) log.info(sm.getString("deltaManager.startClustering", getName()));
+ //to survice context reloads, as only a stop/start is called, not
+ // createManager
+ cluster.registerManager(this);
+
+ getAllClusterSessions();
+
+ } catch (Throwable t) {
+ log.error(sm.getString("deltaManager.managerLoad"), t);
+ }
+ }
+
+ /**
+ * get from first session master the backup from all clustered sessions
+ * @see #findSessionMasterMember()
+ */
+ public synchronized void getAllClusterSessions() {
+ if (cluster != null && cluster.getMembers().length > 0) {
+ long beforeSendTime = System.currentTimeMillis();
+ Member mbr = findSessionMasterMember();
+ if(mbr == null) { // No domain member found
+ return;
+ }
+ SessionMessage msg = new SessionMessageImpl(this.getName(),SessionMessage.EVT_GET_ALL_SESSIONS, null, "GET-ALL","GET-ALL-" + getName());
+ // set reference time
+ stateTransferCreateSendTime = beforeSendTime ;
+ // request session state
+ counterSend_EVT_GET_ALL_SESSIONS++;
+ stateTransfered = false ;
+ // FIXME This send call block the deploy thread, when sender waitForAck is enabled
+ try {
+ synchronized(receivedMessageQueue) {
+ receiverQueue = true ;
+ }
+ cluster.send(msg, mbr);
+ if (log.isWarnEnabled()) log.warn(sm.getString("deltaManager.waitForSessionState",getName(), mbr));
+ // FIXME At sender ack mode this method check only the state transfer and resend is a problem!
+ waitForSendAllSessions(beforeSendTime);
+ } finally {
+ synchronized(receivedMessageQueue) {
+ for (Iterator iter = receivedMessageQueue.iterator(); iter.hasNext();) {
+ SessionMessage smsg = (SessionMessage) iter.next();
+ if (!stateTimestampDrop) {
+ messageReceived(smsg, smsg.getAddress() != null ? (Member) smsg.getAddress() : null);
+ } else {
+ if (smsg.getEventType() != SessionMessage.EVT_GET_ALL_SESSIONS && smsg.getTimestamp() >= stateTransferCreateSendTime) {
+ // FIXME handle EVT_GET_ALL_SESSIONS later
+ messageReceived(smsg,smsg.getAddress() != null ? (Member) smsg.getAddress() : null);
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(sm.getString("deltaManager.dropMessage",getName(), smsg.getEventTypeString(),new Date(stateTransferCreateSendTime), new Date(smsg.getTimestamp())));
+ }
+ }
+ }
+ }
+ receivedMessageQueue.clear();
+ receiverQueue = false ;
+ }
+ }
+ } else {
+ if (log.isInfoEnabled()) log.info(sm.getString("deltaManager.noMembers", getName()));
+ }
+ }
+
+ /**
+ * Register cross context session at replication valve thread local
+ * @param session cross context session
+ */
+ protected void registerSessionAtReplicationValve(DeltaSession session) {
+ if(replicationValve == null) {
+ if(container instanceof StandardContext && ((StandardContext)container).getCrossContext()) {
+ Cluster cluster = getCluster() ;
+ if(cluster != null && cluster instanceof CatalinaCluster) {
+ Valve[] valves = ((CatalinaCluster)cluster).getValves();
+ if(valves != null && valves.length > 0) {
+ for(int i=0; replicationValve == null && i < valves.length ; i++ ){
+ if(valves[i] instanceof ReplicationValve) replicationValve = (ReplicationValve)valves[i] ;
+ }//for
+
+ if(replicationValve == null && log.isDebugEnabled()) {
+ log.debug("no ReplicationValve found for CrossContext Support");
+ }//endif
+ }//end if
+ }//endif
+ }//end if
+ }//end if
+ if(replicationValve != null) {
+ replicationValve.registerReplicationSession(session);
+ }
+ }
+
+ /**
+ * Find the master of the session state
+ * @return master member of sessions
+ */
+ protected Member findSessionMasterMember() {
+ Member mbr = null;
+ Member mbrs[] = cluster.getMembers();
+ if(mbrs.length != 0 ) mbr = mbrs[0];
+ if(mbr == null && log.isWarnEnabled()) log.warn(sm.getString("deltaManager.noMasterMember",getName(), ""));
+ if(mbr != null && log.isDebugEnabled()) log.warn(sm.getString("deltaManager.foundMasterMember",getName(), mbr));
+ return mbr;
+ }
+
+ /**
+ * Wait that cluster session state is transfer or timeout after 60 Sec
+ * With stateTransferTimeout == -1 wait that backup is transfered (forever mode)
+ */
+ protected void waitForSendAllSessions(long beforeSendTime) {
+ long reqStart = System.currentTimeMillis();
+ long reqNow = reqStart ;
+ boolean isTimeout = false;
+ if(getStateTransferTimeout() > 0) {
+ // wait that state is transfered with timeout check
+ do {
+ try {
+ Thread.sleep(100);
+ } catch (Exception sleep) {
+ //
+ }
+ reqNow = System.currentTimeMillis();
+ isTimeout = ((reqNow - reqStart) > (1000 * getStateTransferTimeout()));
+ } while ((!getStateTransfered()) && (!isTimeout));
+ } else {
+ if(getStateTransferTimeout() == -1) {
+ // wait that state is transfered
+ do {
+ try {
+ Thread.sleep(100);
+ } catch (Exception sleep) {
+ }
+ } while ((!getStateTransfered()));
+ reqNow = System.currentTimeMillis();
+ }
+ }
+ if (isTimeout || (!getStateTransfered())) {
+ counterNoStateTransfered++ ;
+ log.error(sm.getString("deltaManager.noSessionState",getName(),new Date(beforeSendTime),new Long(reqNow - beforeSendTime)));
+ } else {
+ if (log.isInfoEnabled())
+ log.info(sm.getString("deltaManager.sessionReceived",getName(), new Date(beforeSendTime), new Long(reqNow - beforeSendTime)));
+ }
+ }
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given instance
+ * of this component.
+ *
+ * @exception LifecycleException
+ * if this component detects a fatal error that needs to be
+ * reported
+ */
+ public void stop() throws LifecycleException {
+
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("deltaManager.stopped", getName()));
+
+
+ // Validate and update our current component state
+ if (!started)
+ throw new LifecycleException(sm.getString("deltaManager.notStarted"));
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ started = false;
+
+ // Expire all active sessions
+ if (log.isInfoEnabled()) log.info(sm.getString("deltaManager.expireSessions", getName()));
+ Session sessions[] = findSessions();
+ for (int i = 0; i < sessions.length; i++) {
+ DeltaSession session = (DeltaSession) sessions[i];
+ if (!session.isValid())
+ continue;
+ try {
+ session.expire(true, isExpireSessionsOnShutdown());
+ } catch (Throwable ignore) {
+ ;
+ }
+ }
+
+ // Require a new random number generator if we are restarted
+ this.random = null;
+ getCluster().removeManager(this);
+ replicationValve = null;
+ if (initialized) {
+ destroy();
+ }
+ }
+
+ // ----------------------------------------- PropertyChangeListener Methods
+
+ /**
+ * Process property change events from our associated Context.
+ *
+ * @param event
+ * The property change event that has occurred
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+
+ // Validate the source of this event
+ if (!(event.getSource() instanceof Context))
+ return;
+ // Process a relevant property change
+ if (event.getPropertyName().equals("sessionTimeout")) {
+ try {
+ setMaxInactiveInterval(((Integer) event.getNewValue()).intValue() * 60);
+ } catch (NumberFormatException e) {
+ log.error(sm.getString("deltaManager.sessionTimeout", event.getNewValue()));
+ }
+ }
+
+ }
+
+ // -------------------------------------------------------- Replication
+ // Methods
+
+ /**
+ * A message was received from another node, this is the callback method to
+ * implement if you are interested in receiving replication messages.
+ *
+ * @param cmsg -
+ * the message received.
+ */
+ public void messageDataReceived(ClusterMessage cmsg) {
+ if (cmsg != null && cmsg instanceof SessionMessage) {
+ SessionMessage msg = (SessionMessage) cmsg;
+ switch (msg.getEventType()) {
+ case SessionMessage.EVT_GET_ALL_SESSIONS:
+ case SessionMessage.EVT_SESSION_CREATED:
+ case SessionMessage.EVT_SESSION_EXPIRED:
+ case SessionMessage.EVT_SESSION_ACCESSED:
+ case SessionMessage.EVT_SESSION_DELTA: {
+ synchronized(receivedMessageQueue) {
+ if(receiverQueue) {
+ receivedMessageQueue.add(msg);
+ return ;
+ }
+ }
+ break;
+ }
+ default: {
+ //we didn't queue, do nothing
+ break;
+ }
+ } //switch
+
+ messageReceived(msg, msg.getAddress() != null ? (Member) msg.getAddress() : null);
+ }
+ }
+
+ /**
+ * When the request has been completed, the replication valve will notify
+ * the manager, and the manager will decide whether any replication is
+ * needed or not. If there is a need for replication, the manager will
+ * create a session message and that will be replicated. The cluster
+ * determines where it gets sent.
+ *
+ * @param sessionId -
+ * the sessionId that just completed.
+ * @return a SessionMessage to be sent,
+ */
+ public ClusterMessage requestCompleted(String sessionId) {
+ try {
+ DeltaSession session = (DeltaSession) findSession(sessionId);
+ DeltaRequest deltaRequest = session.getDeltaRequest();
+ SessionMessage msg = null;
+ boolean isDeltaRequest = false ;
+ synchronized(deltaRequest) {
+ isDeltaRequest = deltaRequest.getSize() > 0 ;
+ if (isDeltaRequest) {
+ counterSend_EVT_SESSION_DELTA++;
+ byte[] data = serializeDeltaRequest(deltaRequest);
+ msg = new SessionMessageImpl(getName(),
+ SessionMessage.EVT_SESSION_DELTA,
+ data,
+ sessionId,
+ sessionId + "-" + System.currentTimeMillis());
+ session.resetDeltaRequest();
+ }
+ }
+ if(!isDeltaRequest) {
+ if(!session.isPrimarySession()) {
+ counterSend_EVT_SESSION_ACCESSED++;
+ msg = new SessionMessageImpl(getName(),
+ SessionMessage.EVT_SESSION_ACCESSED,
+ null,
+ sessionId,
+ sessionId + "-" + System.currentTimeMillis());
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("deltaManager.createMessage.accessChangePrimary",getName(), sessionId));
+ }
+ }
+ } else { // log only outside synch block!
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("deltaManager.createMessage.delta",getName(), sessionId));
+ }
+ }
+ session.setPrimarySession(true);
+ //check to see if we need to send out an access message
+ if ((msg == null)) {
+ long replDelta = System.currentTimeMillis() - session.getLastTimeReplicated();
+ if (replDelta > (getMaxInactiveInterval() * 1000)) {
+ counterSend_EVT_SESSION_ACCESSED++;
+ msg = new SessionMessageImpl(getName(),
+ SessionMessage.EVT_SESSION_ACCESSED,
+ null,
+ sessionId,
+ sessionId + "-" + System.currentTimeMillis());
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("deltaManager.createMessage.access", getName(),sessionId));
+ }
+ }
+
+ }
+
+ //update last replicated time
+ if (msg != null) session.setLastTimeReplicated(System.currentTimeMillis());
+ return msg;
+ } catch (IOException x) {
+ log.error(sm.getString("deltaManager.createMessage.unableCreateDeltaRequest",sessionId), x);
+ return null;
+ }
+
+ }
+ /**
+ * Reset manager statistics
+ */
+ public synchronized void resetStatistics() {
+ processingTime = 0 ;
+ expiredSessions = 0 ;
+ rejectedSessions = 0 ;
+ sessionReplaceCounter = 0 ;
+ counterNoStateTransfered = 0 ;
+ maxActive = getActiveSessions() ;
+ sessionCounter = getActiveSessions() ;
+ counterReceive_EVT_ALL_SESSION_DATA = 0;
+ counterReceive_EVT_GET_ALL_SESSIONS = 0;
+ counterReceive_EVT_SESSION_ACCESSED = 0 ;
+ counterReceive_EVT_SESSION_CREATED = 0 ;
+ counterReceive_EVT_SESSION_DELTA = 0 ;
+ counterReceive_EVT_SESSION_EXPIRED = 0 ;
+ counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0;
+ counterSend_EVT_ALL_SESSION_DATA = 0;
+ counterSend_EVT_GET_ALL_SESSIONS = 0;
+ counterSend_EVT_SESSION_ACCESSED = 0 ;
+ counterSend_EVT_SESSION_CREATED = 0 ;
+ counterSend_EVT_SESSION_DELTA = 0 ;
+ counterSend_EVT_SESSION_EXPIRED = 0 ;
+ counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0;
+
+ }
+
+ // -------------------------------------------------------- persistence handler
+
+ public void load() {
+
+ }
+
+ public void unload() {
+
+ }
+
+ // -------------------------------------------------------- expire
+
+ /**
+ * send session expired to other cluster nodes
+ *
+ * @param id
+ * session id
+ */
+ protected void sessionExpired(String id) {
+ counterSend_EVT_SESSION_EXPIRED++ ;
+ SessionMessage msg = new SessionMessageImpl(getName(),SessionMessage.EVT_SESSION_EXPIRED, null, id, id+ "-EXPIRED-MSG");
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.createMessage.expire",getName(), id));
+ send(msg);
+ }
+
+ /**
+ * Exipre all find sessions.
+ */
+ public void expireAllLocalSessions()
+ {
+ long timeNow = System.currentTimeMillis();
+ Session sessions[] = findSessions();
+ int expireDirect = 0 ;
+ int expireIndirect = 0 ;
+
+ if(log.isDebugEnabled()) log.debug("Start expire all sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
+ for (int i = 0; i < sessions.length; i++) {
+ if (sessions[i] instanceof DeltaSession) {
+ DeltaSession session = (DeltaSession) sessions[i];
+ if (session.isPrimarySession()) {
+ if (session.isValid()) {
+ session.expire();
+ expireDirect++;
+ } else {
+ expireIndirect++;
+ }//end if
+ }//end if
+ }//end if
+ }//for
+ long timeEnd = System.currentTimeMillis();
+ if(log.isDebugEnabled()) log.debug("End expire sessions " + getName() + " exipre processingTime " + (timeEnd - timeNow) + " expired direct sessions: " + expireDirect + " expired direct sessions: " + expireIndirect);
+
+ }
+
+ /**
+ * When the manager expires session not tied to a request. The cluster will
+ * periodically ask for a list of sessions that should expire and that
+ * should be sent across the wire.
+ *
+ * @return The invalidated sessions array
+ */
+ public String[] getInvalidatedSessions() {
+ return new String[0];
+ }
+
+ // -------------------------------------------------------- message receive
+
+ /**
+ * Test that sender and local domain is the same
+ */
+ protected boolean checkSenderDomain(SessionMessage msg,Member sender) {
+ boolean sameDomain= true;
+ if (!sameDomain && log.isWarnEnabled()) {
+ log.warn(sm.getString("deltaManager.receiveMessage.fromWrongDomain",
+ new Object[] {getName(),
+ msg.getEventTypeString(),
+ sender,
+ "",
+ "" }));
+ }
+ return sameDomain ;
+ }
+
+ /**
+ * This method is called by the received thread when a SessionMessage has
+ * been received from one of the other nodes in the cluster.
+ *
+ * @param msg -
+ * the message received
+ * @param sender -
+ * the sender of the message, this is used if we receive a
+ * EVT_GET_ALL_SESSION message, so that we only reply to the
+ * requesting node
+ */
+ protected void messageReceived(SessionMessage msg, Member sender) {
+ if(doDomainReplication() && !checkSenderDomain(msg,sender)) {
+ return;
+ }
+ ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+ try {
+
+ ClassLoader[] loaders = getClassLoaders();
+ if ( loaders != null && loaders.length > 0) Thread.currentThread().setContextClassLoader(loaders[0]);
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.eventType",getName(), msg.getEventTypeString(), sender));
+
+ switch (msg.getEventType()) {
+ case SessionMessage.EVT_GET_ALL_SESSIONS: {
+ handleGET_ALL_SESSIONS(msg,sender);
+ break;
+ }
+ case SessionMessage.EVT_ALL_SESSION_DATA: {
+ handleALL_SESSION_DATA(msg,sender);
+ break;
+ }
+ case SessionMessage.EVT_ALL_SESSION_TRANSFERCOMPLETE: {
+ handleALL_SESSION_TRANSFERCOMPLETE(msg,sender);
+ break;
+ }
+ case SessionMessage.EVT_SESSION_CREATED: {
+ handleSESSION_CREATED(msg,sender);
+ break;
+ }
+ case SessionMessage.EVT_SESSION_EXPIRED: {
+ handleSESSION_EXPIRED(msg,sender);
+ break;
+ }
+ case SessionMessage.EVT_SESSION_ACCESSED: {
+ handleSESSION_ACCESSED(msg,sender);
+ break;
+ }
+ case SessionMessage.EVT_SESSION_DELTA: {
+ handleSESSION_DELTA(msg,sender);
+ break;
+ }
+ default: {
+ //we didn't recognize the message type, do nothing
+ break;
+ }
+ } //switch
+ } catch (Exception x) {
+ log.error(sm.getString("deltaManager.receiveMessage.error",getName()), x);
+ } finally {
+ Thread.currentThread().setContextClassLoader(contextLoader);
+ }
+ }
+
+ // -------------------------------------------------------- message receiver handler
+
+
+ /**
+ * handle receive session state is complete transfered
+ * @param msg
+ * @param sender
+ */
+ protected void handleALL_SESSION_TRANSFERCOMPLETE(SessionMessage msg, Member sender) {
+ counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE++ ;
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.transfercomplete",getName(), sender.getHost(), new Integer(sender.getPort())));
+ stateTransferCreateSendTime = msg.getTimestamp() ;
+ stateTransfered = true ;
+ }
+
+ /**
+ * handle receive session delta
+ * @param msg
+ * @param sender
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ protected void handleSESSION_DELTA(SessionMessage msg, Member sender) throws IOException, ClassNotFoundException {
+ counterReceive_EVT_SESSION_DELTA++;
+ byte[] delta = msg.getSession();
+ DeltaSession session = (DeltaSession) findSession(msg.getSessionID());
+ if (session != null) {
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.delta",getName(), msg.getSessionID()));
+ DeltaRequest dreq = deserializeDeltaRequest(session, delta);
+ dreq.execute(session, notifyListenersOnReplication);
+ session.setPrimarySession(false);
+ }
+ }
+
+ /**
+ * handle receive session is access at other node ( primary session is now false)
+ * @param msg
+ * @param sender
+ * @throws IOException
+ */
+ protected void handleSESSION_ACCESSED(SessionMessage msg,Member sender) throws IOException {
+ counterReceive_EVT_SESSION_ACCESSED++;
+ DeltaSession session = (DeltaSession) findSession(msg.getSessionID());
+ if (session != null) {
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.accessed",getName(), msg.getSessionID()));
+ session.access();
+ session.setPrimarySession(false);
+ session.endAccess();
+ }
+ }
+
+ /**
+ * handle receive session is expire at other node ( expire session also here)
+ * @param msg
+ * @param sender
+ * @throws IOException
+ */
+ protected void handleSESSION_EXPIRED(SessionMessage msg,Member sender) throws IOException {
+ counterReceive_EVT_SESSION_EXPIRED++;
+ DeltaSession session = (DeltaSession) findSession(msg.getSessionID());
+ if (session != null) {
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.expired",getName(), msg.getSessionID()));
+ session.expire(notifySessionListenersOnReplication, false);
+ }
+ }
+
+ /**
+ * handle receive new session is created at other node (create backup - primary false)
+ * @param msg
+ * @param sender
+ */
+ protected void handleSESSION_CREATED(SessionMessage msg,Member sender) {
+ counterReceive_EVT_SESSION_CREATED++;
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.createNewSession",getName(), msg.getSessionID()));
+ DeltaSession session = (DeltaSession) createEmptySession();
+ session.setManager(this);
+ session.setValid(true);
+ session.setPrimarySession(false);
+ session.setCreationTime(msg.getTimestamp());
+ session.access();
+ if(notifySessionListenersOnReplication)
+ session.setId(msg.getSessionID());
+ else
+ session.setIdInternal(msg.getSessionID());
+ session.resetDeltaRequest();
+ session.endAccess();
+
+ }
+
+ /**
+ * handle receive sessions from other not ( restart )
+ * @param msg
+ * @param sender
+ * @throws ClassNotFoundException
+ * @throws IOException
+ */
+ protected void handleALL_SESSION_DATA(SessionMessage msg,Member sender) throws ClassNotFoundException, IOException {
+ counterReceive_EVT_ALL_SESSION_DATA++;
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.allSessionDataBegin",getName()));
+ byte[] data = msg.getSession();
+ deserializeSessions(data);
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.allSessionDataAfter",getName()));
+ //stateTransferred = true;
+ }
+
+ /**
+ * handle receive that other node want all sessions ( restart )
+ * a) send all sessions with one message
+ * b) send session at blocks
+ * After sending send state is complete transfered
+ * @param msg
+ * @param sender
+ * @throws IOException
+ */
+ protected void handleGET_ALL_SESSIONS(SessionMessage msg, Member sender) throws IOException {
+ counterReceive_EVT_GET_ALL_SESSIONS++;
+ //get a list of all the session from this manager
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.unloadingBegin", getName()));
+ // Write the number of active sessions, followed by the details
+ // get all sessions and serialize without sync
+ Session[] currentSessions = findSessions();
+ long findSessionTimestamp = System.currentTimeMillis() ;
+ if (isSendAllSessions()) {
+ sendSessions(sender, currentSessions, findSessionTimestamp);
+ } else {
+ // send session at blocks
+ int len = currentSessions.length < getSendAllSessionsSize() ? currentSessions.length : getSendAllSessionsSize();
+ Session[] sendSessions = new Session[len];
+ for (int i = 0; i < currentSessions.length; i += getSendAllSessionsSize()) {
+ len = i + getSendAllSessionsSize() > currentSessions.length ? currentSessions.length - i : getSendAllSessionsSize();
+ System.arraycopy(currentSessions, i, sendSessions, 0, len);
+ sendSessions(sender, sendSessions,findSessionTimestamp);
+ if (getSendAllSessionsWaitTime() > 0) {
+ try {
+ Thread.sleep(getSendAllSessionsWaitTime());
+ } catch (Exception sleep) {
+ }
+ }//end if
+ }//for
+ }//end if
+
+ SessionMessage newmsg = new SessionMessageImpl(name,SessionMessage.EVT_ALL_SESSION_TRANSFERCOMPLETE, null,"SESSION-STATE-TRANSFERED", "SESSION-STATE-TRANSFERED"+ getName());
+ newmsg.setTimestamp(findSessionTimestamp);
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.createMessage.allSessionTransfered",getName()));
+ counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE++;
+ cluster.send(newmsg, sender);
+ }
+
+
+ /**
+ * send a block of session to sender
+ * @param sender
+ * @param currentSessions
+ * @param sendTimestamp
+ * @throws IOException
+ */
+ protected void sendSessions(Member sender, Session[] currentSessions,long sendTimestamp) throws IOException {
+ byte[] data = serializeSessions(currentSessions);
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.receiveMessage.unloadingAfter",getName()));
+ SessionMessage newmsg = new SessionMessageImpl(name,SessionMessage.EVT_ALL_SESSION_DATA, data,"SESSION-STATE", "SESSION-STATE-" + getName());
+ newmsg.setTimestamp(sendTimestamp);
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaManager.createMessage.allSessionData",getName()));
+ counterSend_EVT_ALL_SESSION_DATA++;
+ cluster.send(newmsg, sender);
+ }
+
+ public ClusterManager cloneFromTemplate() {
+ DeltaManager result = new DeltaManager();
+ result.name = "Clone-from-"+name;
+ result.cluster = cluster;
+ result.replicationValve = replicationValve;
+ result.maxActiveSessions = maxActiveSessions;
+ result.expireSessionsOnShutdown = expireSessionsOnShutdown;
+ result.notifyListenersOnReplication = notifyListenersOnReplication;
+ result.notifySessionListenersOnReplication = notifySessionListenersOnReplication;
+ result.stateTransferTimeout = stateTransferTimeout;
+ result.sendAllSessions = sendAllSessions;
+ result.sendClusterDomainOnly = sendClusterDomainOnly ;
+ result.sendAllSessionsSize = sendAllSessionsSize;
+ result.sendAllSessionsWaitTime = sendAllSessionsWaitTime ;
+ result.receiverQueue = receiverQueue ;
+ result.stateTimestampDrop = stateTimestampDrop ;
+ result.stateTransferCreateSendTime = stateTransferCreateSendTime;
+ return result;
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-/**\r
- * This class is used to track the series of actions that happens when\r
- * a request is executed. These actions will then translate into invokations of methods \r
- * on the actual session.\r
- * This class is NOT thread safe. One DeltaRequest per session\r
- * @author <a href="mailto:fhanik@apache.org">Filip Hanik</a>\r
- * @version 1.0\r
- */\r
-\r
-import java.io.Externalizable;\r
-import java.security.Principal;\r
-import java.util.LinkedList;\r
-\r
-import org.apache.catalina.realm.GenericPrincipal;\r
-import org.apache.catalina.util.StringManager;\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.IOException;\r
-import java.io.ObjectOutputStream;\r
-\r
-\r
-public class DeltaRequest implements Externalizable {\r
-\r
- public static org.apache.juli.logging.Log log =\r
- org.apache.juli.logging.LogFactory.getLog( DeltaRequest.class );\r
-\r
- /**\r
- * The string manager for this package.\r
- */\r
- protected static StringManager sm = StringManager\r
- .getManager(Constants.Package);\r
-\r
- public static final int TYPE_ATTRIBUTE = 0;\r
- public static final int TYPE_PRINCIPAL = 1;\r
- public static final int TYPE_ISNEW = 2;\r
- public static final int TYPE_MAXINTERVAL = 3;\r
-\r
- public static final int ACTION_SET = 0;\r
- public static final int ACTION_REMOVE = 1;\r
-\r
- public static final String NAME_PRINCIPAL = "__SET__PRINCIPAL__";\r
- public static final String NAME_MAXINTERVAL = "__SET__MAXINTERVAL__";\r
- public static final String NAME_ISNEW = "__SET__ISNEW__";\r
-\r
- private String sessionId;\r
- private LinkedList actions = new LinkedList();\r
- private LinkedList actionPool = new LinkedList();\r
- \r
- private boolean recordAllActions = false;\r
-\r
- public DeltaRequest() {\r
- \r
- }\r
- \r
- public DeltaRequest(String sessionId, boolean recordAllActions) {\r
- this.recordAllActions=recordAllActions;\r
- if(sessionId != null)\r
- setSessionId(sessionId);\r
- }\r
-\r
-\r
- public void setAttribute(String name, Object value) {\r
- int action = (value==null)?ACTION_REMOVE:ACTION_SET;\r
- addAction(TYPE_ATTRIBUTE,action,name,value);\r
- }\r
-\r
- public void removeAttribute(String name) {\r
- int action = ACTION_REMOVE;\r
- addAction(TYPE_ATTRIBUTE,action,name,null);\r
- }\r
-\r
- public void setMaxInactiveInterval(int interval) {\r
- int action = ACTION_SET;\r
- addAction(TYPE_MAXINTERVAL,action,NAME_MAXINTERVAL,new Integer(interval));\r
- }\r
- \r
- /**\r
- * convert principal at SerializablePrincipal for backup nodes.\r
- * Only support principals from type {@link GenericPrincipal GenericPrincipal}\r
- * @param p Session principal\r
- * @see GenericPrincipal\r
- */\r
- public void setPrincipal(Principal p) {\r
- int action = (p==null)?ACTION_REMOVE:ACTION_SET;\r
- SerializablePrincipal sp = null;\r
- if ( p != null ) {\r
- if(p instanceof GenericPrincipal) {\r
- sp = SerializablePrincipal.createPrincipal((GenericPrincipal)p);\r
- if(log.isDebugEnabled())\r
- log.debug(sm.getString("deltaRequest.showPrincipal", p.getName() , getSessionId()));\r
- } else\r
- log.error(sm.getString("deltaRequest.wrongPrincipalClass",p.getClass().getName()));\r
- }\r
- addAction(TYPE_PRINCIPAL,action,NAME_PRINCIPAL,sp);\r
- }\r
-\r
- public void setNew(boolean n) {\r
- int action = ACTION_SET;\r
- addAction(TYPE_ISNEW,action,NAME_ISNEW,new Boolean(n));\r
- }\r
-\r
- protected synchronized void addAction(int type,\r
- int action,\r
- String name,\r
- Object value) {\r
- AttributeInfo info = null;\r
- if ( this.actionPool.size() > 0 ) {\r
- try {\r
- info = (AttributeInfo) actionPool.removeFirst();\r
- }catch ( Exception x ) {\r
- log.error("Unable to remove element:",x);\r
- info = new AttributeInfo(type, action, name, value);\r
- }\r
- info.init(type,action,name,value);\r
- } else {\r
- info = new AttributeInfo(type, action, name, value);\r
- }\r
- //if we have already done something to this attribute, make sure\r
- //we don't send multiple actions across the wire\r
- if ( !recordAllActions) {\r
- try {\r
- actions.remove(info);\r
- } catch (java.util.NoSuchElementException x) {\r
- //do nothing, we wanted to remove it anyway\r
- }\r
- }\r
- //add the action\r
- actions.addLast(info);\r
- }\r
- \r
- public void execute(DeltaSession session) {\r
- execute(session,true);\r
- }\r
-\r
- public synchronized void execute(DeltaSession session, boolean notifyListeners) {\r
- if ( !this.sessionId.equals( session.getId() ) )\r
- throw new java.lang.IllegalArgumentException("Session id mismatch, not executing the delta request");\r
- session.access();\r
- for ( int i=0; i<actions.size(); i++ ) {\r
- AttributeInfo info = (AttributeInfo)actions.get(i);\r
- switch ( info.getType() ) {\r
- case TYPE_ATTRIBUTE: {\r
- if ( info.getAction() == ACTION_SET ) {\r
- if ( log.isTraceEnabled() ) log.trace("Session.setAttribute('"+info.getName()+"', '"+info.getValue()+"')");\r
- session.setAttribute(info.getName(), info.getValue(),notifyListeners,false);\r
- } else {\r
- if ( log.isTraceEnabled() ) log.trace("Session.removeAttribute('"+info.getName()+"')");\r
- session.removeAttribute(info.getName(),notifyListeners,false);\r
- }\r
- \r
- break;\r
- }//case\r
- case TYPE_ISNEW: {\r
- if ( log.isTraceEnabled() ) log.trace("Session.setNew('"+info.getValue()+"')");\r
- session.setNew(((Boolean)info.getValue()).booleanValue(),false);\r
- break;\r
- }//case\r
- case TYPE_MAXINTERVAL: {\r
- if ( log.isTraceEnabled() ) log.trace("Session.setMaxInactiveInterval('"+info.getValue()+"')");\r
- session.setMaxInactiveInterval(((Integer)info.getValue()).intValue(),false);\r
- break;\r
- }//case\r
- case TYPE_PRINCIPAL: {\r
- Principal p = null;\r
- if ( info.getAction() == ACTION_SET ) {\r
- SerializablePrincipal sp = (SerializablePrincipal)info.getValue();\r
- p = (Principal)sp.getPrincipal(session.getManager().getContainer().getRealm());\r
- }\r
- session.setPrincipal(p,false);\r
- break;\r
- }//case\r
- default : throw new java.lang.IllegalArgumentException("Invalid attribute info type="+info);\r
- }//switch\r
- }//for\r
- session.endAccess();\r
- reset();\r
- }\r
-\r
- public synchronized void reset() {\r
- while ( actions.size() > 0 ) {\r
- try {\r
- AttributeInfo info = (AttributeInfo) actions.removeFirst();\r
- info.recycle();\r
- actionPool.addLast(info);\r
- }catch ( Exception x ) {\r
- log.error("Unable to remove element",x);\r
- }\r
- }\r
- actions.clear();\r
- }\r
- \r
- public String getSessionId() {\r
- return sessionId;\r
- }\r
- public void setSessionId(String sessionId) {\r
- this.sessionId = sessionId;\r
- if ( sessionId == null ) {\r
- new Exception("Session Id is null for setSessionId").fillInStackTrace().printStackTrace();\r
- }\r
- }\r
- public int getSize() {\r
- return actions.size();\r
- }\r
- \r
- public synchronized void clear() {\r
- actions.clear();\r
- actionPool.clear();\r
- }\r
- \r
- public synchronized void readExternal(java.io.ObjectInput in) throws IOException,ClassNotFoundException {\r
- //sessionId - String\r
- //recordAll - boolean\r
- //size - int\r
- //AttributeInfo - in an array\r
- reset();\r
- sessionId = in.readUTF();\r
- recordAllActions = in.readBoolean();\r
- int cnt = in.readInt();\r
- if (actions == null)\r
- actions = new LinkedList();\r
- else\r
- actions.clear();\r
- for (int i = 0; i < cnt; i++) {\r
- AttributeInfo info = null;\r
- if (this.actionPool.size() > 0) {\r
- try {\r
- info = (AttributeInfo) actionPool.removeFirst();\r
- } catch ( Exception x ) {\r
- log.error("Unable to remove element",x);\r
- info = new AttributeInfo(-1,-1,null,null);\r
- }\r
- }\r
- else {\r
- info = new AttributeInfo(-1,-1,null,null);\r
- }\r
- info.readExternal(in);\r
- actions.addLast(info);\r
- }//for\r
- }\r
- \r
-\r
-\r
- public synchronized void writeExternal(java.io.ObjectOutput out ) throws java.io.IOException {\r
- //sessionId - String\r
- //recordAll - boolean\r
- //size - int\r
- //AttributeInfo - in an array\r
- out.writeUTF(getSessionId());\r
- out.writeBoolean(recordAllActions);\r
- out.writeInt(getSize());\r
- for ( int i=0; i<getSize(); i++ ) {\r
- AttributeInfo info = (AttributeInfo)actions.get(i);\r
- info.writeExternal(out);\r
- }\r
- }\r
- \r
- /**\r
- * serialize DeltaRequest\r
- * @see DeltaRequest#writeExternal(java.io.ObjectOutput)\r
- * \r
- * @param deltaRequest\r
- * @return serialized delta request\r
- * @throws IOException\r
- */\r
- protected byte[] serialize() throws IOException {\r
- ByteArrayOutputStream bos = new ByteArrayOutputStream();\r
- ObjectOutputStream oos = new ObjectOutputStream(bos);\r
- writeExternal(oos);\r
- oos.flush();\r
- oos.close();\r
- return bos.toByteArray();\r
- }\r
- \r
- private static class AttributeInfo implements java.io.Externalizable {\r
- private String name = null;\r
- private Object value = null;\r
- private int action;\r
- private int type;\r
-\r
- public AttributeInfo() {}\r
-\r
- public AttributeInfo(int type,\r
- int action,\r
- String name,\r
- Object value) {\r
- super();\r
- init(type,action,name,value);\r
- }\r
-\r
- public void init(int type,\r
- int action,\r
- String name,\r
- Object value) {\r
- this.name = name;\r
- this.value = value;\r
- this.action = action;\r
- this.type = type;\r
- }\r
-\r
- public int getType() {\r
- return type;\r
- }\r
-\r
- public int getAction() {\r
- return action;\r
- }\r
-\r
- public Object getValue() {\r
- return value;\r
- }\r
- public int hashCode() {\r
- return name.hashCode();\r
- }\r
-\r
- public String getName() {\r
- return name;\r
- }\r
- \r
- public void recycle() {\r
- name = null;\r
- value = null;\r
- type=-1;\r
- action=-1;\r
- }\r
-\r
- public boolean equals(Object o) {\r
- if ( ! (o instanceof AttributeInfo ) ) return false;\r
- AttributeInfo other = (AttributeInfo)o;\r
- return other.getName().equals(this.getName());\r
- }\r
- \r
- public synchronized void readExternal(java.io.ObjectInput in ) throws IOException,ClassNotFoundException {\r
- //type - int\r
- //action - int\r
- //name - String\r
- //hasvalue - boolean\r
- //value - object\r
- type = in.readInt();\r
- action = in.readInt();\r
- name = in.readUTF();\r
- boolean hasValue = in.readBoolean();\r
- if ( hasValue ) value = in.readObject();\r
- }\r
-\r
- public synchronized void writeExternal(java.io.ObjectOutput out) throws IOException {\r
- //type - int\r
- //action - int\r
- //name - String\r
- //hasvalue - boolean\r
- //value - object\r
- out.writeInt(getType());\r
- out.writeInt(getAction());\r
- out.writeUTF(getName());\r
- out.writeBoolean(getValue()!=null);\r
- if (getValue()!=null) out.writeObject(getValue());\r
- }\r
- \r
- public String toString() {\r
- StringBuffer buf = new StringBuffer("AttributeInfo[type=");\r
- buf.append(getType()).append(", action=").append(getAction());\r
- buf.append(", name=").append(getName()).append(", value=").append(getValue());\r
- buf.append(", addr=").append(super.toString()).append("]");\r
- return buf.toString();\r
- }\r
-\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+/**
+ * This class is used to track the series of actions that happens when
+ * a request is executed. These actions will then translate into invokations of methods
+ * on the actual session.
+ * This class is NOT thread safe. One DeltaRequest per session
+ * @author <a href="mailto:fhanik@apache.org">Filip Hanik</a>
+ * @version 1.0
+ */
+
+import java.io.Externalizable;
+import java.security.Principal;
+import java.util.LinkedList;
+
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.util.StringManager;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+
+
+public class DeltaRequest implements Externalizable {
+
+ public static org.apache.juli.logging.Log log =
+ org.apache.juli.logging.LogFactory.getLog( DeltaRequest.class );
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm = StringManager
+ .getManager(Constants.Package);
+
+ public static final int TYPE_ATTRIBUTE = 0;
+ public static final int TYPE_PRINCIPAL = 1;
+ public static final int TYPE_ISNEW = 2;
+ public static final int TYPE_MAXINTERVAL = 3;
+
+ public static final int ACTION_SET = 0;
+ public static final int ACTION_REMOVE = 1;
+
+ public static final String NAME_PRINCIPAL = "__SET__PRINCIPAL__";
+ public static final String NAME_MAXINTERVAL = "__SET__MAXINTERVAL__";
+ public static final String NAME_ISNEW = "__SET__ISNEW__";
+
+ private String sessionId;
+ private LinkedList actions = new LinkedList();
+ private LinkedList actionPool = new LinkedList();
+
+ private boolean recordAllActions = false;
+
+ public DeltaRequest() {
+
+ }
+
+ public DeltaRequest(String sessionId, boolean recordAllActions) {
+ this.recordAllActions=recordAllActions;
+ if(sessionId != null)
+ setSessionId(sessionId);
+ }
+
+
+ public void setAttribute(String name, Object value) {
+ int action = (value==null)?ACTION_REMOVE:ACTION_SET;
+ addAction(TYPE_ATTRIBUTE,action,name,value);
+ }
+
+ public void removeAttribute(String name) {
+ int action = ACTION_REMOVE;
+ addAction(TYPE_ATTRIBUTE,action,name,null);
+ }
+
+ public void setMaxInactiveInterval(int interval) {
+ int action = ACTION_SET;
+ addAction(TYPE_MAXINTERVAL,action,NAME_MAXINTERVAL,new Integer(interval));
+ }
+
+ /**
+ * convert principal at SerializablePrincipal for backup nodes.
+ * Only support principals from type {@link GenericPrincipal GenericPrincipal}
+ * @param p Session principal
+ * @see GenericPrincipal
+ */
+ public void setPrincipal(Principal p) {
+ int action = (p==null)?ACTION_REMOVE:ACTION_SET;
+ SerializablePrincipal sp = null;
+ if ( p != null ) {
+ if(p instanceof GenericPrincipal) {
+ sp = SerializablePrincipal.createPrincipal((GenericPrincipal)p);
+ if(log.isDebugEnabled())
+ log.debug(sm.getString("deltaRequest.showPrincipal", p.getName() , getSessionId()));
+ } else
+ log.error(sm.getString("deltaRequest.wrongPrincipalClass",p.getClass().getName()));
+ }
+ addAction(TYPE_PRINCIPAL,action,NAME_PRINCIPAL,sp);
+ }
+
+ public void setNew(boolean n) {
+ int action = ACTION_SET;
+ addAction(TYPE_ISNEW,action,NAME_ISNEW,new Boolean(n));
+ }
+
+ protected synchronized void addAction(int type,
+ int action,
+ String name,
+ Object value) {
+ AttributeInfo info = null;
+ if ( this.actionPool.size() > 0 ) {
+ try {
+ info = (AttributeInfo) actionPool.removeFirst();
+ }catch ( Exception x ) {
+ log.error("Unable to remove element:",x);
+ info = new AttributeInfo(type, action, name, value);
+ }
+ info.init(type,action,name,value);
+ } else {
+ info = new AttributeInfo(type, action, name, value);
+ }
+ //if we have already done something to this attribute, make sure
+ //we don't send multiple actions across the wire
+ if ( !recordAllActions) {
+ try {
+ actions.remove(info);
+ } catch (java.util.NoSuchElementException x) {
+ //do nothing, we wanted to remove it anyway
+ }
+ }
+ //add the action
+ actions.addLast(info);
+ }
+
+ public void execute(DeltaSession session) {
+ execute(session,true);
+ }
+
+ public synchronized void execute(DeltaSession session, boolean notifyListeners) {
+ if ( !this.sessionId.equals( session.getId() ) )
+ throw new java.lang.IllegalArgumentException("Session id mismatch, not executing the delta request");
+ session.access();
+ for ( int i=0; i<actions.size(); i++ ) {
+ AttributeInfo info = (AttributeInfo)actions.get(i);
+ switch ( info.getType() ) {
+ case TYPE_ATTRIBUTE: {
+ if ( info.getAction() == ACTION_SET ) {
+ if ( log.isTraceEnabled() ) log.trace("Session.setAttribute('"+info.getName()+"', '"+info.getValue()+"')");
+ session.setAttribute(info.getName(), info.getValue(),notifyListeners,false);
+ } else {
+ if ( log.isTraceEnabled() ) log.trace("Session.removeAttribute('"+info.getName()+"')");
+ session.removeAttribute(info.getName(),notifyListeners,false);
+ }
+
+ break;
+ }//case
+ case TYPE_ISNEW: {
+ if ( log.isTraceEnabled() ) log.trace("Session.setNew('"+info.getValue()+"')");
+ session.setNew(((Boolean)info.getValue()).booleanValue(),false);
+ break;
+ }//case
+ case TYPE_MAXINTERVAL: {
+ if ( log.isTraceEnabled() ) log.trace("Session.setMaxInactiveInterval('"+info.getValue()+"')");
+ session.setMaxInactiveInterval(((Integer)info.getValue()).intValue(),false);
+ break;
+ }//case
+ case TYPE_PRINCIPAL: {
+ Principal p = null;
+ if ( info.getAction() == ACTION_SET ) {
+ SerializablePrincipal sp = (SerializablePrincipal)info.getValue();
+ p = (Principal)sp.getPrincipal(session.getManager().getContainer().getRealm());
+ }
+ session.setPrincipal(p,false);
+ break;
+ }//case
+ default : throw new java.lang.IllegalArgumentException("Invalid attribute info type="+info);
+ }//switch
+ }//for
+ session.endAccess();
+ reset();
+ }
+
+ public synchronized void reset() {
+ while ( actions.size() > 0 ) {
+ try {
+ AttributeInfo info = (AttributeInfo) actions.removeFirst();
+ info.recycle();
+ actionPool.addLast(info);
+ }catch ( Exception x ) {
+ log.error("Unable to remove element",x);
+ }
+ }
+ actions.clear();
+ }
+
+ public String getSessionId() {
+ return sessionId;
+ }
+ public void setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ if ( sessionId == null ) {
+ new Exception("Session Id is null for setSessionId").fillInStackTrace().printStackTrace();
+ }
+ }
+ public int getSize() {
+ return actions.size();
+ }
+
+ public synchronized void clear() {
+ actions.clear();
+ actionPool.clear();
+ }
+
+ public synchronized void readExternal(java.io.ObjectInput in) throws IOException,ClassNotFoundException {
+ //sessionId - String
+ //recordAll - boolean
+ //size - int
+ //AttributeInfo - in an array
+ reset();
+ sessionId = in.readUTF();
+ recordAllActions = in.readBoolean();
+ int cnt = in.readInt();
+ if (actions == null)
+ actions = new LinkedList();
+ else
+ actions.clear();
+ for (int i = 0; i < cnt; i++) {
+ AttributeInfo info = null;
+ if (this.actionPool.size() > 0) {
+ try {
+ info = (AttributeInfo) actionPool.removeFirst();
+ } catch ( Exception x ) {
+ log.error("Unable to remove element",x);
+ info = new AttributeInfo(-1,-1,null,null);
+ }
+ }
+ else {
+ info = new AttributeInfo(-1,-1,null,null);
+ }
+ info.readExternal(in);
+ actions.addLast(info);
+ }//for
+ }
+
+
+
+ public synchronized void writeExternal(java.io.ObjectOutput out ) throws java.io.IOException {
+ //sessionId - String
+ //recordAll - boolean
+ //size - int
+ //AttributeInfo - in an array
+ out.writeUTF(getSessionId());
+ out.writeBoolean(recordAllActions);
+ out.writeInt(getSize());
+ for ( int i=0; i<getSize(); i++ ) {
+ AttributeInfo info = (AttributeInfo)actions.get(i);
+ info.writeExternal(out);
+ }
+ }
+
+ /**
+ * serialize DeltaRequest
+ * @see DeltaRequest#writeExternal(java.io.ObjectOutput)
+ *
+ * @param deltaRequest
+ * @return serialized delta request
+ * @throws IOException
+ */
+ protected byte[] serialize() throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ writeExternal(oos);
+ oos.flush();
+ oos.close();
+ return bos.toByteArray();
+ }
+
+ private static class AttributeInfo implements java.io.Externalizable {
+ private String name = null;
+ private Object value = null;
+ private int action;
+ private int type;
+
+ public AttributeInfo() {}
+
+ public AttributeInfo(int type,
+ int action,
+ String name,
+ Object value) {
+ super();
+ init(type,action,name,value);
+ }
+
+ public void init(int type,
+ int action,
+ String name,
+ Object value) {
+ this.name = name;
+ this.value = value;
+ this.action = action;
+ this.type = type;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public int getAction() {
+ return action;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void recycle() {
+ name = null;
+ value = null;
+ type=-1;
+ action=-1;
+ }
+
+ public boolean equals(Object o) {
+ if ( ! (o instanceof AttributeInfo ) ) return false;
+ AttributeInfo other = (AttributeInfo)o;
+ return other.getName().equals(this.getName());
+ }
+
+ public synchronized void readExternal(java.io.ObjectInput in ) throws IOException,ClassNotFoundException {
+ //type - int
+ //action - int
+ //name - String
+ //hasvalue - boolean
+ //value - object
+ type = in.readInt();
+ action = in.readInt();
+ name = in.readUTF();
+ boolean hasValue = in.readBoolean();
+ if ( hasValue ) value = in.readObject();
+ }
+
+ public synchronized void writeExternal(java.io.ObjectOutput out) throws IOException {
+ //type - int
+ //action - int
+ //name - String
+ //hasvalue - boolean
+ //value - object
+ out.writeInt(getType());
+ out.writeInt(getAction());
+ out.writeUTF(getName());
+ out.writeBoolean(getValue()!=null);
+ if (getValue()!=null) out.writeObject(getValue());
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer("AttributeInfo[type=");
+ buf.append(getType()).append(", action=").append(getAction());
+ buf.append(", name=").append(getName()).append(", value=").append(getValue());
+ buf.append(", addr=").append(super.toString()).append("]");
+ return buf.toString();
+ }
+
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-import java.io.Externalizable;\r
-import java.io.IOException;\r
-import java.io.NotSerializableException;\r
-import java.io.ObjectInput;\r
-import java.io.ObjectOutput;\r
-import java.io.Serializable;\r
-import java.security.Principal;\r
-import java.util.ArrayList;\r
-import java.util.Enumeration;\r
-import java.util.HashMap;\r
-import java.util.Hashtable;\r
-import java.util.concurrent.locks.Lock;\r
-import java.util.concurrent.locks.ReentrantReadWriteLock;\r
-import javax.servlet.http.HttpSession;\r
-import javax.servlet.http.HttpSessionContext;\r
-\r
-import org.apache.catalina.Manager;\r
-import org.apache.catalina.ha.ClusterManager;\r
-import org.apache.catalina.ha.ClusterSession;\r
-import org.apache.catalina.realm.GenericPrincipal;\r
-import org.apache.catalina.session.StandardSession;\r
-import org.apache.catalina.tribes.io.ReplicationStream;\r
-import org.apache.catalina.tribes.tipis.ReplicatedMapEntry;\r
-import org.apache.catalina.util.Enumerator;\r
-import org.apache.catalina.util.StringManager;\r
-import org.apache.catalina.session.StandardManager;\r
-import org.apache.catalina.session.ManagerBase;\r
-import java.util.concurrent.atomic.AtomicInteger;\r
-\r
-/**\r
- *\r
- * Similar to the StandardSession except that this session will keep\r
- * track of deltas during a request.\r
- *\r
- * @author Filip Hanik\r
- * @version $Revision: 372887 $ $Date: 2006-01-27 09:58:58 -0600 (Fri, 27 Jan 2006) $\r
- */\r
-\r
-public class DeltaSession extends StandardSession implements Externalizable,ClusterSession,ReplicatedMapEntry {\r
-\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(DeltaSession.class);\r
-\r
- /**\r
- * The string manager for this package.\r
- */\r
- protected static StringManager sm = StringManager.getManager(Constants.Package);\r
-\r
- // ----------------------------------------------------- Instance Variables\r
-\r
- /**\r
- * only the primary session will expire, or be able to expire due to\r
- * inactivity. This is set to false as soon as I receive this session over\r
- * the wire in a session message. That means that someone else has made a\r
- * request on another server.\r
- */\r
- private transient boolean isPrimarySession = true;\r
-\r
- /**\r
- * The delta request contains all the action info\r
- *\r
- */\r
- private transient DeltaRequest deltaRequest = null;\r
-\r
- /**\r
- * Last time the session was replicatd, used for distributed expiring of\r
- * session\r
- */\r
- private transient long lastTimeReplicated = System.currentTimeMillis();\r
-\r
-\r
- protected Lock diffLock = new ReentrantReadWriteLock().writeLock();\r
-\r
- private long version;\r
-\r
- // ----------------------------------------------------------- Constructors\r
-\r
- /**\r
- * Construct a new Session associated with the specified Manager.\r
- *\r
- * @param manager\r
- * The manager with which this Session is associated\r
- */\r
- public DeltaSession() {\r
- this(null);\r
- }\r
-\r
- public DeltaSession(Manager manager) {\r
- super(manager);\r
- this.resetDeltaRequest();\r
- }\r
-\r
- // ----------------------------------------------------- ReplicatedMapEntry\r
-\r
- /**\r
- * Has the object changed since last replication\r
- * and is not in a locked state\r
- * @return boolean\r
- */\r
- public boolean isDirty() {\r
- return getDeltaRequest().getSize()>0;\r
- }\r
-\r
- /**\r
- * If this returns true, the map will extract the diff using getDiff()\r
- * Otherwise it will serialize the entire object.\r
- * @return boolean\r
- */\r
- public boolean isDiffable() {\r
- return true;\r
- }\r
-\r
- /**\r
- * Returns a diff and sets the dirty map to false\r
- * @return byte[]\r
- * @throws IOException\r
- */\r
- public byte[] getDiff() throws IOException {\r
- return getDeltaRequest().serialize();\r
- }\r
-\r
- public ClassLoader[] getClassLoaders() {\r
- if ( manager instanceof BackupManager ) return ((BackupManager)manager).getClassLoaders();\r
- else if ( manager instanceof ClusterManagerBase ) return ((ClusterManagerBase)manager).getClassLoaders();\r
- else if ( manager instanceof StandardManager ) {\r
- StandardManager sm = (StandardManager)manager;\r
- return ClusterManagerBase.getClassLoaders(sm.getContainer());\r
- } else if ( manager instanceof ManagerBase ) {\r
- ManagerBase mb = (ManagerBase)manager;\r
- return ClusterManagerBase.getClassLoaders(mb.getContainer());\r
- }//end if\r
- return null;\r
- }\r
-\r
- /**\r
- * Applies a diff to an existing object.\r
- * @param diff byte[]\r
- * @param offset int\r
- * @param length int\r
- * @throws IOException\r
- */\r
- public void applyDiff(byte[] diff, int offset, int length) throws IOException, ClassNotFoundException {\r
- ReplicationStream stream = ((ClusterManager)getManager()).getReplicationStream(diff,offset,length);\r
- getDeltaRequest().readExternal(stream);\r
- ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();\r
- try {\r
- ClassLoader[] loaders = getClassLoaders();\r
- if ( loaders != null && loaders.length >0 ) Thread.currentThread().setContextClassLoader(loaders[0]);\r
- getDeltaRequest().execute(this);\r
- }finally {\r
- Thread.currentThread().setContextClassLoader(contextLoader);\r
- }\r
- }\r
-\r
- /**\r
- * Resets the current diff state and resets the dirty flag\r
- */\r
- public void resetDiff() {\r
- resetDeltaRequest();\r
- }\r
-\r
- /**\r
- * Lock during serialization\r
- */\r
- public void lock() {\r
- diffLock.lock();\r
- }\r
-\r
- /**\r
- * Unlock after serialization\r
- */\r
- public void unlock() {\r
- diffLock.unlock();\r
- }\r
-\r
- public void setOwner(Object owner) {\r
- if ( owner instanceof ClusterManager && getManager()==null) {\r
- ClusterManager cm = (ClusterManager)owner;\r
- this.setManager(cm);\r
- this.setValid(true);\r
- this.setPrimarySession(false);\r
- this.access();\r
- this.resetDeltaRequest();\r
- this.endAccess();\r
- }\r
- }\r
- // ----------------------------------------------------- Session Properties\r
-\r
- /**\r
- * returns true if this session is the primary session, if that is the case,\r
- * the manager can expire it upon timeout.\r
- */\r
- public boolean isPrimarySession() {\r
- return isPrimarySession;\r
- }\r
-\r
- /**\r
- * Sets whether this is the primary session or not.\r
- *\r
- * @param primarySession\r
- * Flag value\r
- */\r
- public void setPrimarySession(boolean primarySession) {\r
- this.isPrimarySession = primarySession;\r
- }\r
-\r
- /**\r
- * Set the session identifier for this session without notify listeners.\r
- *\r
- * @param id\r
- * The new session identifier\r
- */\r
- public void setIdInternal(String id) {\r
- this.id = id;\r
- resetDeltaRequest();\r
- }\r
-\r
- /**\r
- * Set the session identifier for this session.\r
- *\r
- * @param id\r
- * The new session identifier\r
- */\r
- public void setId(String id) {\r
- super.setId(id);\r
- resetDeltaRequest();\r
- }\r
-\r
- \r
-\r
- /**\r
- * Return the last client access time without invalidation check\r
- * @see #getLastAccessedTime().\r
- */\r
- public long getLastAccessedTimeInternal() {\r
- return (this.lastAccessedTime);\r
- }\r
-\r
-\r
- \r
- public void setMaxInactiveInterval(int interval, boolean addDeltaRequest) {\r
- super.maxInactiveInterval = interval;\r
- if (isValid && interval == 0) {\r
- expire();\r
- } else {\r
- if (addDeltaRequest && (deltaRequest != null))\r
- deltaRequest.setMaxInactiveInterval(interval);\r
- }\r
- }\r
-\r
- /**\r
- * Set the <code>isNew</code> flag for this session.\r
- *\r
- * @param isNew\r
- * The new value for the <code>isNew</code> flag\r
- */\r
- public void setNew(boolean isNew) {\r
- setNew(isNew, true);\r
- }\r
-\r
- public void setNew(boolean isNew, boolean addDeltaRequest) {\r
- super.setNew(isNew);\r
- if (addDeltaRequest && (deltaRequest != null))\r
- deltaRequest.setNew(isNew);\r
- }\r
-\r
- /**\r
- * Set the authenticated Principal that is associated with this Session.\r
- * This provides an <code>Authenticator</code> with a means to cache a\r
- * previously authenticated Principal, and avoid potentially expensive\r
- * <code>Realm.authenticate()</code> calls on every request.\r
- *\r
- * @param principal\r
- * The new Principal, or <code>null</code> if none\r
- */\r
- public void setPrincipal(Principal principal) {\r
- setPrincipal(principal, true);\r
- }\r
-\r
- public void setPrincipal(Principal principal, boolean addDeltaRequest) {\r
- try { \r
- lock();\r
- super.setPrincipal(principal);\r
- if (addDeltaRequest && (deltaRequest != null))\r
- deltaRequest.setPrincipal(principal);\r
- } finally {\r
- unlock();\r
- }\r
- }\r
-\r
- /**\r
- * Return the <code>isValid</code> flag for this session.\r
- */\r
- public boolean isValid() {\r
- if (this.expiring) {\r
- return true;\r
- }\r
- if (!this.isValid) {\r
- return false;\r
- }\r
- if (ACTIVITY_CHECK && accessCount.get() > 0) {\r
- return true;\r
- }\r
- if (maxInactiveInterval >= 0) {\r
- long timeNow = System.currentTimeMillis();\r
- int timeIdle = (int) ( (timeNow - thisAccessedTime) / 1000L);\r
- if (isPrimarySession()) {\r
- if (timeIdle >= maxInactiveInterval) {\r
- expire(true);\r
- }\r
- } else {\r
- if (timeIdle >= (2 * maxInactiveInterval)) {\r
- //if the session has been idle twice as long as allowed,\r
- //the primary session has probably crashed, and no other\r
- //requests are coming in. that is why we do this. otherwise\r
- //we would have a memory leak\r
- expire(true, false);\r
- }\r
- }\r
- }\r
- return (this.isValid);\r
- }\r
-\r
- // ------------------------------------------------- Session Public Methods\r
-\r
- /**\r
- * Perform the internal processing required to invalidate this session,\r
- * without triggering an exception if the session has already expired.\r
- *\r
- * @param notify\r
- * Should we notify listeners about the demise of this session?\r
- */\r
- public void expire(boolean notify) {\r
- expire(notify, true);\r
- }\r
-\r
- public void expire(boolean notify, boolean notifyCluster) {\r
- String expiredId = getIdInternal();\r
- super.expire(notify);\r
-\r
- if (notifyCluster) {\r
- if (log.isDebugEnabled())\r
- log.debug(sm.getString("deltaSession.notifying",\r
- ((DeltaManager)manager).getName(), \r
- new Boolean(isPrimarySession()), \r
- expiredId));\r
- if ( manager instanceof DeltaManager ) {\r
- ( (DeltaManager) manager).sessionExpired(expiredId);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Release all object references, and initialize instance variables, in\r
- * preparation for reuse of this object.\r
- */\r
- public void recycle() {\r
- super.recycle();\r
- deltaRequest.clear();\r
- }\r
-\r
-\r
- /**\r
- * Return a string representation of this object.\r
- */\r
- public String toString() {\r
- StringBuffer sb = new StringBuffer();\r
- sb.append("DeltaSession[");\r
- sb.append(id);\r
- sb.append("]");\r
- return (sb.toString());\r
- }\r
-\r
- // ------------------------------------------------ Session Package Methods\r
-\r
- public synchronized void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {\r
- readObjectData(in);\r
- }\r
-\r
-\r
- /**\r
- * Read a serialized version of the contents of this session object from the\r
- * specified object input stream, without requiring that the StandardSession\r
- * itself have been serialized.\r
- *\r
- * @param stream\r
- * The object input stream to read from\r
- *\r
- * @exception ClassNotFoundException\r
- * if an unknown class is specified\r
- * @exception IOException\r
- * if an input/output error occurs\r
- */\r
- public void readObjectData(ObjectInput stream) throws ClassNotFoundException, IOException {\r
- readObject(stream);\r
- }\r
-\r
- /**\r
- * Write a serialized version of the contents of this session object to the\r
- * specified object output stream, without requiring that the\r
- * StandardSession itself have been serialized.\r
- *\r
- * @param stream\r
- * The object output stream to write to\r
- *\r
- * @exception IOException\r
- * if an input/output error occurs\r
- */\r
- public void writeObjectData(ObjectOutput stream) throws IOException {\r
- writeObject(stream);\r
- }\r
-\r
- public void resetDeltaRequest() {\r
- if (deltaRequest == null) {\r
- deltaRequest = new DeltaRequest(getIdInternal(), false);\r
- } else {\r
- deltaRequest.reset();\r
- deltaRequest.setSessionId(getIdInternal());\r
- }\r
- }\r
-\r
- public DeltaRequest getDeltaRequest() {\r
- if (deltaRequest == null) resetDeltaRequest();\r
- return deltaRequest;\r
- }\r
-\r
- // ------------------------------------------------- HttpSession Properties\r
-\r
- // ----------------------------------------------HttpSession Public Methods\r
-\r
-\r
-\r
- /**\r
- * Remove the object bound with the specified name from this session. If the\r
- * session does not have an object bound with this name, this method does\r
- * nothing.\r
- * <p>\r
- * After this method executes, and if the object implements\r
- * <code>HttpSessionBindingListener</code>, the container calls\r
- * <code>valueUnbound()</code> on the object.\r
- *\r
- * @param name\r
- * Name of the object to remove from this session.\r
- * @param notify\r
- * Should we notify interested listeners that this attribute is\r
- * being removed?\r
- *\r
- * @exception IllegalStateException\r
- * if this method is called on an invalidated session\r
- */\r
- public void removeAttribute(String name, boolean notify) {\r
- removeAttribute(name, notify, true);\r
- }\r
-\r
- public void removeAttribute(String name, boolean notify,boolean addDeltaRequest) {\r
- // Validate our current state\r
- if (!isValid()) throw new IllegalStateException(sm.getString("standardSession.removeAttribute.ise"));\r
- removeAttributeInternal(name, notify, addDeltaRequest);\r
- }\r
-\r
- /**\r
- * Bind an object to this session, using the specified name. If an object of\r
- * the same name is already bound to this session, the object is replaced.\r
- * <p>\r
- * After this method executes, and if the object implements\r
- * <code>HttpSessionBindingListener</code>, the container calls\r
- * <code>valueBound()</code> on the object.\r
- *\r
- * @param name\r
- * Name to which the object is bound, cannot be null\r
- * @param value\r
- * Object to be bound, cannot be null\r
- *\r
- * @exception IllegalArgumentException\r
- * if an attempt is made to add a non-serializable object in\r
- * an environment marked distributable.\r
- * @exception IllegalStateException\r
- * if this method is called on an invalidated session\r
- */\r
- public void setAttribute(String name, Object value) {\r
- setAttribute(name, value, true, true);\r
- }\r
-\r
- public void setAttribute(String name, Object value, boolean notify,boolean addDeltaRequest) {\r
-\r
- // Name cannot be null\r
- if (name == null) throw new IllegalArgumentException(sm.getString("standardSession.setAttribute.namenull"));\r
-\r
- // Null value is the same as removeAttribute()\r
- if (value == null) {\r
- removeAttribute(name);\r
- return;\r
- }\r
-\r
- try {\r
- lock();\r
- super.setAttribute(name,value, notify);\r
- if (addDeltaRequest && (deltaRequest != null)) deltaRequest.setAttribute(name, value);\r
- } finally {\r
- unlock();\r
- }\r
- }\r
-\r
- // -------------------------------------------- HttpSession Private Methods\r
-\r
- /**\r
- * Read a serialized version of this session object from the specified\r
- * object input stream.\r
- * <p>\r
- * <b>IMPLEMENTATION NOTE </b>: The reference to the owning Manager is not\r
- * restored by this method, and must be set explicitly.\r
- *\r
- * @param stream\r
- * The input stream to read from\r
- *\r
- * @exception ClassNotFoundException\r
- * if an unknown class is specified\r
- * @exception IOException\r
- * if an input/output error occurs\r
- */\r
- private void readObject(ObjectInput stream) throws ClassNotFoundException, IOException {\r
-\r
- // Deserialize the scalar instance variables (except Manager)\r
- authType = null; // Transient only\r
- creationTime = ( (Long) stream.readObject()).longValue();\r
- lastAccessedTime = ( (Long) stream.readObject()).longValue();\r
- maxInactiveInterval = ( (Integer) stream.readObject()).intValue();\r
- isNew = ( (Boolean) stream.readObject()).booleanValue();\r
- isValid = ( (Boolean) stream.readObject()).booleanValue();\r
- thisAccessedTime = ( (Long) stream.readObject()).longValue();\r
- version = ( (Long) stream.readObject()).longValue();\r
- boolean hasPrincipal = stream.readBoolean();\r
- principal = null;\r
- if (hasPrincipal) {\r
- principal = SerializablePrincipal.readPrincipal(stream,getManager().getContainer().getRealm());\r
- }\r
-\r
- // setId((String) stream.readObject());\r
- id = (String) stream.readObject();\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaSession.readSession", id));\r
-\r
- // Deserialize the attribute count and attribute values\r
- if (attributes == null) attributes = new Hashtable();\r
- int n = ( (Integer) stream.readObject()).intValue();\r
- boolean isValidSave = isValid;\r
- isValid = true;\r
- for (int i = 0; i < n; i++) {\r
- String name = (String) stream.readObject();\r
- Object value = (Object) stream.readObject();\r
- if ( (value instanceof String) && (value.equals(NOT_SERIALIZED)))\r
- continue;\r
- attributes.put(name, value);\r
- }\r
- isValid = isValidSave;\r
-\r
- if (listeners == null) {\r
- listeners = new ArrayList();\r
- }\r
-\r
- if (notes == null) {\r
- notes = new Hashtable();\r
- }\r
- activate();\r
- }\r
-\r
- public synchronized void writeExternal(ObjectOutput out ) throws java.io.IOException {\r
- writeObject(out);\r
- }\r
-\r
-\r
- /**\r
- * Write a serialized version of this session object to the specified object\r
- * output stream.\r
- * <p>\r
- * <b>IMPLEMENTATION NOTE </b>: The owning Manager will not be stored in the\r
- * serialized representation of this Session. After calling\r
- * <code>readObject()</code>, you must set the associated Manager\r
- * explicitly.\r
- * <p>\r
- * <b>IMPLEMENTATION NOTE </b>: Any attribute that is not Serializable will\r
- * be unbound from the session, with appropriate actions if it implements\r
- * HttpSessionBindingListener. If you do not want any such attributes, be\r
- * sure the <code>distributable</code> property of the associated Manager\r
- * is set to <code>true</code>.\r
- *\r
- * @param stream\r
- * The output stream to write to\r
- *\r
- * @exception IOException\r
- * if an input/output error occurs\r
- */\r
- private void writeObject(ObjectOutput stream) throws IOException {\r
- // Write the scalar instance variables (except Manager)\r
- stream.writeObject(new Long(creationTime));\r
- stream.writeObject(new Long(lastAccessedTime));\r
- stream.writeObject(new Integer(maxInactiveInterval));\r
- stream.writeObject(new Boolean(isNew));\r
- stream.writeObject(new Boolean(isValid));\r
- stream.writeObject(new Long(thisAccessedTime));\r
- stream.writeObject(new Long(version));\r
- stream.writeBoolean(getPrincipal() != null);\r
- if (getPrincipal() != null) {\r
- SerializablePrincipal.writePrincipal((GenericPrincipal) principal,stream);\r
- }\r
-\r
- stream.writeObject(id);\r
- if (log.isDebugEnabled()) log.debug(sm.getString("deltaSession.writeSession", id));\r
-\r
- // Accumulate the names of serializable and non-serializable attributes\r
- String keys[] = keys();\r
- ArrayList saveNames = new ArrayList();\r
- ArrayList saveValues = new ArrayList();\r
- for (int i = 0; i < keys.length; i++) {\r
- Object value = null;\r
- value = attributes.get(keys[i]);\r
- if (value == null)\r
- continue;\r
- else if (value instanceof Serializable) {\r
- saveNames.add(keys[i]);\r
- saveValues.add(value);\r
- }\r
- }\r
-\r
- // Serialize the attribute count and the Serializable attributes\r
- int n = saveNames.size();\r
- stream.writeObject(new Integer(n));\r
- for (int i = 0; i < n; i++) {\r
- stream.writeObject( (String) saveNames.get(i));\r
- try {\r
- stream.writeObject(saveValues.get(i));\r
- } catch (NotSerializableException e) {\r
- log.error(sm.getString("standardSession.notSerializable",saveNames.get(i), id), e);\r
- stream.writeObject(NOT_SERIALIZED);\r
- log.error(" storing attribute '" + saveNames.get(i)+ "' with value NOT_SERIALIZED");\r
- }\r
- }\r
-\r
- }\r
-\r
- // -------------------------------------------------------- Private Methods\r
-\r
- \r
-\r
- /**\r
- * Return the value of an attribute without a check for validity.\r
- */\r
- protected Object getAttributeInternal(String name) {\r
- return (attributes.get(name));\r
- }\r
-\r
- protected void removeAttributeInternal(String name, boolean notify,\r
- boolean addDeltaRequest) {\r
- try {\r
- lock();\r
- // Remove this attribute from our collection\r
- Object value = attributes.get(name);\r
- if (value == null) return;\r
-\r
- super.removeAttributeInternal(name,notify);\r
- if (addDeltaRequest && (deltaRequest != null)) deltaRequest.removeAttribute(name);\r
-\r
- }finally {\r
- unlock();\r
- }\r
- }\r
-\r
- protected long getLastTimeReplicated() {\r
- return lastTimeReplicated;\r
- }\r
-\r
- public long getVersion() {\r
- return version;\r
- }\r
-\r
- protected void setLastTimeReplicated(long lastTimeReplicated) {\r
- this.lastTimeReplicated = lastTimeReplicated;\r
- }\r
-\r
- public void setVersion(long version) {\r
- this.version = version;\r
- }\r
-\r
- protected void setAccessCount(int count) {\r
- if ( accessCount == null && ACTIVITY_CHECK ) accessCount = new AtomicInteger();\r
- if ( accessCount != null ) super.accessCount.set(count);\r
- }\r
-}\r
-\r
-// -------------------------------------------------------------- Private Class\r
-\r
-/**\r
- * This class is a dummy implementation of the <code>HttpSessionContext</code>\r
- * interface, to conform to the requirement that such an object be returned when\r
- * <code>HttpSession.getSessionContext()</code> is called.\r
- *\r
- * @author Craig R. McClanahan\r
- *\r
- * @deprecated As of Java Servlet API 2.1 with no replacement. The interface\r
- * will be removed in a future version of this API.\r
- */\r
-\r
-final class StandardSessionContext\r
- implements HttpSessionContext {\r
-\r
- private HashMap dummy = new HashMap();\r
-\r
- /**\r
- * Return the session identifiers of all sessions defined within this\r
- * context.\r
- *\r
- * @deprecated As of Java Servlet API 2.1 with no replacement. This method\r
- * must return an empty <code>Enumeration</code> and will be\r
- * removed in a future version of the API.\r
- */\r
- public Enumeration getIds() {\r
- return (new Enumerator(dummy));\r
- }\r
-\r
- /**\r
- * Return the <code>HttpSession</code> associated with the specified\r
- * session identifier.\r
- *\r
- * @param id\r
- * Session identifier for which to look up a session\r
- *\r
- * @deprecated As of Java Servlet API 2.1 with no replacement. This method\r
- * must return null and will be removed in a future version of\r
- * the API.\r
- */\r
- public HttpSession getSession(String id) {\r
- return (null);\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+import org.apache.catalina.Manager;
+import org.apache.catalina.ha.ClusterManager;
+import org.apache.catalina.ha.ClusterSession;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.session.StandardSession;
+import org.apache.catalina.tribes.io.ReplicationStream;
+import org.apache.catalina.tribes.tipis.ReplicatedMapEntry;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.session.ManagerBase;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * Similar to the StandardSession except that this session will keep
+ * track of deltas during a request.
+ *
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+
+public class DeltaSession extends StandardSession implements Externalizable,ClusterSession,ReplicatedMapEntry {
+
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(DeltaSession.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm = StringManager.getManager(Constants.Package);
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * only the primary session will expire, or be able to expire due to
+ * inactivity. This is set to false as soon as I receive this session over
+ * the wire in a session message. That means that someone else has made a
+ * request on another server.
+ */
+ private transient boolean isPrimarySession = true;
+
+ /**
+ * The delta request contains all the action info
+ *
+ */
+ private transient DeltaRequest deltaRequest = null;
+
+ /**
+ * Last time the session was replicatd, used for distributed expiring of
+ * session
+ */
+ private transient long lastTimeReplicated = System.currentTimeMillis();
+
+
+ protected Lock diffLock = new ReentrantReadWriteLock().writeLock();
+
+ private long version;
+
+ // ----------------------------------------------------------- Constructors
+
+ /**
+ * Construct a new Session associated with the specified Manager.
+ *
+ * @param manager
+ * The manager with which this Session is associated
+ */
+ public DeltaSession() {
+ this(null);
+ }
+
+ public DeltaSession(Manager manager) {
+ super(manager);
+ this.resetDeltaRequest();
+ }
+
+ // ----------------------------------------------------- ReplicatedMapEntry
+
+ /**
+ * Has the object changed since last replication
+ * and is not in a locked state
+ * @return boolean
+ */
+ public boolean isDirty() {
+ return getDeltaRequest().getSize()>0;
+ }
+
+ /**
+ * If this returns true, the map will extract the diff using getDiff()
+ * Otherwise it will serialize the entire object.
+ * @return boolean
+ */
+ public boolean isDiffable() {
+ return true;
+ }
+
+ /**
+ * Returns a diff and sets the dirty map to false
+ * @return byte[]
+ * @throws IOException
+ */
+ public byte[] getDiff() throws IOException {
+ return getDeltaRequest().serialize();
+ }
+
+ public ClassLoader[] getClassLoaders() {
+ if ( manager instanceof BackupManager ) return ((BackupManager)manager).getClassLoaders();
+ else if ( manager instanceof ClusterManagerBase ) return ((ClusterManagerBase)manager).getClassLoaders();
+ else if ( manager instanceof StandardManager ) {
+ StandardManager sm = (StandardManager)manager;
+ return ClusterManagerBase.getClassLoaders(sm.getContainer());
+ } else if ( manager instanceof ManagerBase ) {
+ ManagerBase mb = (ManagerBase)manager;
+ return ClusterManagerBase.getClassLoaders(mb.getContainer());
+ }//end if
+ return null;
+ }
+
+ /**
+ * Applies a diff to an existing object.
+ * @param diff byte[]
+ * @param offset int
+ * @param length int
+ * @throws IOException
+ */
+ public void applyDiff(byte[] diff, int offset, int length) throws IOException, ClassNotFoundException {
+ ReplicationStream stream = ((ClusterManager)getManager()).getReplicationStream(diff,offset,length);
+ getDeltaRequest().readExternal(stream);
+ ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ ClassLoader[] loaders = getClassLoaders();
+ if ( loaders != null && loaders.length >0 ) Thread.currentThread().setContextClassLoader(loaders[0]);
+ getDeltaRequest().execute(this);
+ }finally {
+ Thread.currentThread().setContextClassLoader(contextLoader);
+ }
+ }
+
+ /**
+ * Resets the current diff state and resets the dirty flag
+ */
+ public void resetDiff() {
+ resetDeltaRequest();
+ }
+
+ /**
+ * Lock during serialization
+ */
+ public void lock() {
+ diffLock.lock();
+ }
+
+ /**
+ * Unlock after serialization
+ */
+ public void unlock() {
+ diffLock.unlock();
+ }
+
+ public void setOwner(Object owner) {
+ if ( owner instanceof ClusterManager && getManager()==null) {
+ ClusterManager cm = (ClusterManager)owner;
+ this.setManager(cm);
+ this.setValid(true);
+ this.setPrimarySession(false);
+ this.access();
+ this.resetDeltaRequest();
+ this.endAccess();
+ }
+ }
+ // ----------------------------------------------------- Session Properties
+
+ /**
+ * returns true if this session is the primary session, if that is the case,
+ * the manager can expire it upon timeout.
+ */
+ public boolean isPrimarySession() {
+ return isPrimarySession;
+ }
+
+ /**
+ * Sets whether this is the primary session or not.
+ *
+ * @param primarySession
+ * Flag value
+ */
+ public void setPrimarySession(boolean primarySession) {
+ this.isPrimarySession = primarySession;
+ }
+
+ /**
+ * Set the session identifier for this session without notify listeners.
+ *
+ * @param id
+ * The new session identifier
+ */
+ public void setIdInternal(String id) {
+ this.id = id;
+ resetDeltaRequest();
+ }
+
+ /**
+ * Set the session identifier for this session.
+ *
+ * @param id
+ * The new session identifier
+ */
+ public void setId(String id) {
+ super.setId(id);
+ resetDeltaRequest();
+ }
+
+
+
+ /**
+ * Return the last client access time without invalidation check
+ * @see #getLastAccessedTime().
+ */
+ public long getLastAccessedTimeInternal() {
+ return (this.lastAccessedTime);
+ }
+
+
+
+ public void setMaxInactiveInterval(int interval, boolean addDeltaRequest) {
+ super.maxInactiveInterval = interval;
+ if (isValid && interval == 0) {
+ expire();
+ } else {
+ if (addDeltaRequest && (deltaRequest != null))
+ deltaRequest.setMaxInactiveInterval(interval);
+ }
+ }
+
+ /**
+ * Set the <code>isNew</code> flag for this session.
+ *
+ * @param isNew
+ * The new value for the <code>isNew</code> flag
+ */
+ public void setNew(boolean isNew) {
+ setNew(isNew, true);
+ }
+
+ public void setNew(boolean isNew, boolean addDeltaRequest) {
+ super.setNew(isNew);
+ if (addDeltaRequest && (deltaRequest != null))
+ deltaRequest.setNew(isNew);
+ }
+
+ /**
+ * Set the authenticated Principal that is associated with this Session.
+ * This provides an <code>Authenticator</code> with a means to cache a
+ * previously authenticated Principal, and avoid potentially expensive
+ * <code>Realm.authenticate()</code> calls on every request.
+ *
+ * @param principal
+ * The new Principal, or <code>null</code> if none
+ */
+ public void setPrincipal(Principal principal) {
+ setPrincipal(principal, true);
+ }
+
+ public void setPrincipal(Principal principal, boolean addDeltaRequest) {
+ try {
+ lock();
+ super.setPrincipal(principal);
+ if (addDeltaRequest && (deltaRequest != null))
+ deltaRequest.setPrincipal(principal);
+ } finally {
+ unlock();
+ }
+ }
+
+ /**
+ * Return the <code>isValid</code> flag for this session.
+ */
+ public boolean isValid() {
+ if (this.expiring) {
+ return true;
+ }
+ if (!this.isValid) {
+ return false;
+ }
+ if (ACTIVITY_CHECK && accessCount.get() > 0) {
+ return true;
+ }
+ if (maxInactiveInterval >= 0) {
+ long timeNow = System.currentTimeMillis();
+ int timeIdle = (int) ( (timeNow - thisAccessedTime) / 1000L);
+ if (isPrimarySession()) {
+ if (timeIdle >= maxInactiveInterval) {
+ expire(true);
+ }
+ } else {
+ if (timeIdle >= (2 * maxInactiveInterval)) {
+ //if the session has been idle twice as long as allowed,
+ //the primary session has probably crashed, and no other
+ //requests are coming in. that is why we do this. otherwise
+ //we would have a memory leak
+ expire(true, false);
+ }
+ }
+ }
+ return (this.isValid);
+ }
+
+ // ------------------------------------------------- Session Public Methods
+
+ /**
+ * Perform the internal processing required to invalidate this session,
+ * without triggering an exception if the session has already expired.
+ *
+ * @param notify
+ * Should we notify listeners about the demise of this session?
+ */
+ public void expire(boolean notify) {
+ expire(notify, true);
+ }
+
+ public void expire(boolean notify, boolean notifyCluster) {
+ String expiredId = getIdInternal();
+ super.expire(notify);
+
+ if (notifyCluster) {
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("deltaSession.notifying",
+ ((DeltaManager)manager).getName(),
+ new Boolean(isPrimarySession()),
+ expiredId));
+ if ( manager instanceof DeltaManager ) {
+ ( (DeltaManager) manager).sessionExpired(expiredId);
+ }
+ }
+ }
+
+ /**
+ * Release all object references, and initialize instance variables, in
+ * preparation for reuse of this object.
+ */
+ public void recycle() {
+ super.recycle();
+ deltaRequest.clear();
+ }
+
+
+ /**
+ * Return a string representation of this object.
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("DeltaSession[");
+ sb.append(id);
+ sb.append("]");
+ return (sb.toString());
+ }
+
+ // ------------------------------------------------ Session Package Methods
+
+ public synchronized void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {
+ readObjectData(in);
+ }
+
+
+ /**
+ * Read a serialized version of the contents of this session object from the
+ * specified object input stream, without requiring that the StandardSession
+ * itself have been serialized.
+ *
+ * @param stream
+ * The object input stream to read from
+ *
+ * @exception ClassNotFoundException
+ * if an unknown class is specified
+ * @exception IOException
+ * if an input/output error occurs
+ */
+ public void readObjectData(ObjectInput stream) throws ClassNotFoundException, IOException {
+ readObject(stream);
+ }
+
+ /**
+ * Write a serialized version of the contents of this session object to the
+ * specified object output stream, without requiring that the
+ * StandardSession itself have been serialized.
+ *
+ * @param stream
+ * The object output stream to write to
+ *
+ * @exception IOException
+ * if an input/output error occurs
+ */
+ public void writeObjectData(ObjectOutput stream) throws IOException {
+ writeObject(stream);
+ }
+
+ public void resetDeltaRequest() {
+ if (deltaRequest == null) {
+ deltaRequest = new DeltaRequest(getIdInternal(), false);
+ } else {
+ deltaRequest.reset();
+ deltaRequest.setSessionId(getIdInternal());
+ }
+ }
+
+ public DeltaRequest getDeltaRequest() {
+ if (deltaRequest == null) resetDeltaRequest();
+ return deltaRequest;
+ }
+
+ // ------------------------------------------------- HttpSession Properties
+
+ // ----------------------------------------------HttpSession Public Methods
+
+
+
+ /**
+ * Remove the object bound with the specified name from this session. If the
+ * session does not have an object bound with this name, this method does
+ * nothing.
+ * <p>
+ * After this method executes, and if the object implements
+ * <code>HttpSessionBindingListener</code>, the container calls
+ * <code>valueUnbound()</code> on the object.
+ *
+ * @param name
+ * Name of the object to remove from this session.
+ * @param notify
+ * Should we notify interested listeners that this attribute is
+ * being removed?
+ *
+ * @exception IllegalStateException
+ * if this method is called on an invalidated session
+ */
+ public void removeAttribute(String name, boolean notify) {
+ removeAttribute(name, notify, true);
+ }
+
+ public void removeAttribute(String name, boolean notify,boolean addDeltaRequest) {
+ // Validate our current state
+ if (!isValid()) throw new IllegalStateException(sm.getString("standardSession.removeAttribute.ise"));
+ removeAttributeInternal(name, notify, addDeltaRequest);
+ }
+
+ /**
+ * Bind an object to this session, using the specified name. If an object of
+ * the same name is already bound to this session, the object is replaced.
+ * <p>
+ * After this method executes, and if the object implements
+ * <code>HttpSessionBindingListener</code>, the container calls
+ * <code>valueBound()</code> on the object.
+ *
+ * @param name
+ * Name to which the object is bound, cannot be null
+ * @param value
+ * Object to be bound, cannot be null
+ *
+ * @exception IllegalArgumentException
+ * if an attempt is made to add a non-serializable object in
+ * an environment marked distributable.
+ * @exception IllegalStateException
+ * if this method is called on an invalidated session
+ */
+ public void setAttribute(String name, Object value) {
+ setAttribute(name, value, true, true);
+ }
+
+ public void setAttribute(String name, Object value, boolean notify,boolean addDeltaRequest) {
+
+ // Name cannot be null
+ if (name == null) throw new IllegalArgumentException(sm.getString("standardSession.setAttribute.namenull"));
+
+ // Null value is the same as removeAttribute()
+ if (value == null) {
+ removeAttribute(name);
+ return;
+ }
+
+ try {
+ lock();
+ super.setAttribute(name,value, notify);
+ if (addDeltaRequest && (deltaRequest != null)) deltaRequest.setAttribute(name, value);
+ } finally {
+ unlock();
+ }
+ }
+
+ // -------------------------------------------- HttpSession Private Methods
+
+ /**
+ * Read a serialized version of this session object from the specified
+ * object input stream.
+ * <p>
+ * <b>IMPLEMENTATION NOTE </b>: The reference to the owning Manager is not
+ * restored by this method, and must be set explicitly.
+ *
+ * @param stream
+ * The input stream to read from
+ *
+ * @exception ClassNotFoundException
+ * if an unknown class is specified
+ * @exception IOException
+ * if an input/output error occurs
+ */
+ private void readObject(ObjectInput stream) throws ClassNotFoundException, IOException {
+
+ // Deserialize the scalar instance variables (except Manager)
+ authType = null; // Transient only
+ creationTime = ( (Long) stream.readObject()).longValue();
+ lastAccessedTime = ( (Long) stream.readObject()).longValue();
+ maxInactiveInterval = ( (Integer) stream.readObject()).intValue();
+ isNew = ( (Boolean) stream.readObject()).booleanValue();
+ isValid = ( (Boolean) stream.readObject()).booleanValue();
+ thisAccessedTime = ( (Long) stream.readObject()).longValue();
+ version = ( (Long) stream.readObject()).longValue();
+ boolean hasPrincipal = stream.readBoolean();
+ principal = null;
+ if (hasPrincipal) {
+ principal = SerializablePrincipal.readPrincipal(stream,getManager().getContainer().getRealm());
+ }
+
+ // setId((String) stream.readObject());
+ id = (String) stream.readObject();
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaSession.readSession", id));
+
+ // Deserialize the attribute count and attribute values
+ if (attributes == null) attributes = new Hashtable();
+ int n = ( (Integer) stream.readObject()).intValue();
+ boolean isValidSave = isValid;
+ isValid = true;
+ for (int i = 0; i < n; i++) {
+ String name = (String) stream.readObject();
+ Object value = (Object) stream.readObject();
+ if ( (value instanceof String) && (value.equals(NOT_SERIALIZED)))
+ continue;
+ attributes.put(name, value);
+ }
+ isValid = isValidSave;
+
+ if (listeners == null) {
+ listeners = new ArrayList();
+ }
+
+ if (notes == null) {
+ notes = new Hashtable();
+ }
+ activate();
+ }
+
+ public synchronized void writeExternal(ObjectOutput out ) throws java.io.IOException {
+ writeObject(out);
+ }
+
+
+ /**
+ * Write a serialized version of this session object to the specified object
+ * output stream.
+ * <p>
+ * <b>IMPLEMENTATION NOTE </b>: The owning Manager will not be stored in the
+ * serialized representation of this Session. After calling
+ * <code>readObject()</code>, you must set the associated Manager
+ * explicitly.
+ * <p>
+ * <b>IMPLEMENTATION NOTE </b>: Any attribute that is not Serializable will
+ * be unbound from the session, with appropriate actions if it implements
+ * HttpSessionBindingListener. If you do not want any such attributes, be
+ * sure the <code>distributable</code> property of the associated Manager
+ * is set to <code>true</code>.
+ *
+ * @param stream
+ * The output stream to write to
+ *
+ * @exception IOException
+ * if an input/output error occurs
+ */
+ private void writeObject(ObjectOutput stream) throws IOException {
+ // Write the scalar instance variables (except Manager)
+ stream.writeObject(new Long(creationTime));
+ stream.writeObject(new Long(lastAccessedTime));
+ stream.writeObject(new Integer(maxInactiveInterval));
+ stream.writeObject(new Boolean(isNew));
+ stream.writeObject(new Boolean(isValid));
+ stream.writeObject(new Long(thisAccessedTime));
+ stream.writeObject(new Long(version));
+ stream.writeBoolean(getPrincipal() != null);
+ if (getPrincipal() != null) {
+ SerializablePrincipal.writePrincipal((GenericPrincipal) principal,stream);
+ }
+
+ stream.writeObject(id);
+ if (log.isDebugEnabled()) log.debug(sm.getString("deltaSession.writeSession", id));
+
+ // Accumulate the names of serializable and non-serializable attributes
+ String keys[] = keys();
+ ArrayList saveNames = new ArrayList();
+ ArrayList saveValues = new ArrayList();
+ for (int i = 0; i < keys.length; i++) {
+ Object value = null;
+ value = attributes.get(keys[i]);
+ if (value == null)
+ continue;
+ else if (value instanceof Serializable) {
+ saveNames.add(keys[i]);
+ saveValues.add(value);
+ }
+ }
+
+ // Serialize the attribute count and the Serializable attributes
+ int n = saveNames.size();
+ stream.writeObject(new Integer(n));
+ for (int i = 0; i < n; i++) {
+ stream.writeObject( (String) saveNames.get(i));
+ try {
+ stream.writeObject(saveValues.get(i));
+ } catch (NotSerializableException e) {
+ log.error(sm.getString("standardSession.notSerializable",saveNames.get(i), id), e);
+ stream.writeObject(NOT_SERIALIZED);
+ log.error(" storing attribute '" + saveNames.get(i)+ "' with value NOT_SERIALIZED");
+ }
+ }
+
+ }
+
+ // -------------------------------------------------------- Private Methods
+
+
+
+ /**
+ * Return the value of an attribute without a check for validity.
+ */
+ protected Object getAttributeInternal(String name) {
+ return (attributes.get(name));
+ }
+
+ protected void removeAttributeInternal(String name, boolean notify,
+ boolean addDeltaRequest) {
+ try {
+ lock();
+ // Remove this attribute from our collection
+ Object value = attributes.get(name);
+ if (value == null) return;
+
+ super.removeAttributeInternal(name,notify);
+ if (addDeltaRequest && (deltaRequest != null)) deltaRequest.removeAttribute(name);
+
+ }finally {
+ unlock();
+ }
+ }
+
+ protected long getLastTimeReplicated() {
+ return lastTimeReplicated;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ protected void setLastTimeReplicated(long lastTimeReplicated) {
+ this.lastTimeReplicated = lastTimeReplicated;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+ protected void setAccessCount(int count) {
+ if ( accessCount == null && ACTIVITY_CHECK ) accessCount = new AtomicInteger();
+ if ( accessCount != null ) super.accessCount.set(count);
+ }
+}
+
+// -------------------------------------------------------------- Private Class
+
+/**
+ * This class is a dummy implementation of the <code>HttpSessionContext</code>
+ * interface, to conform to the requirement that such an object be returned when
+ * <code>HttpSession.getSessionContext()</code> is called.
+ *
+ * @author Craig R. McClanahan
+ *
+ * @deprecated As of Java Servlet API 2.1 with no replacement. The interface
+ * will be removed in a future version of this API.
+ */
+
+final class StandardSessionContext
+ implements HttpSessionContext {
+
+ private HashMap dummy = new HashMap();
+
+ /**
+ * Return the session identifiers of all sessions defined within this
+ * context.
+ *
+ * @deprecated As of Java Servlet API 2.1 with no replacement. This method
+ * must return an empty <code>Enumeration</code> and will be
+ * removed in a future version of the API.
+ */
+ public Enumeration getIds() {
+ return (new Enumerator(dummy));
+ }
+
+ /**
+ * Return the <code>HttpSession</code> associated with the specified
+ * session identifier.
+ *
+ * @param id
+ * Session identifier for which to look up a session
+ *
+ * @deprecated As of Java Servlet API 2.1 with no replacement. This method
+ * must return null and will be removed in a future version of
+ * the API.
+ */
+ public HttpSession getSession(String id) {
+ return (null);
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha.session;\r
-\r
-import java.io.IOException;\r
-\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.Cookie;\r
-\r
-import org.apache.catalina.Container;\r
-import org.apache.catalina.Context;\r
-import org.apache.catalina.Engine;\r
-import org.apache.catalina.Globals;\r
-import org.apache.catalina.Host;\r
-import org.apache.catalina.Lifecycle;\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.LifecycleListener;\r
-import org.apache.catalina.Manager;\r
-import org.apache.catalina.Session;\r
-import org.apache.catalina.ha.CatalinaCluster;\r
-import org.apache.catalina.ha.ClusterManager;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.ha.ClusterValve;\r
-import org.apache.catalina.connector.Request;\r
-import org.apache.catalina.connector.Response;\r
-import org.apache.catalina.session.ManagerBase;\r
-import org.apache.catalina.util.LifecycleSupport;\r
-import org.apache.catalina.util.StringManager;\r
-import org.apache.catalina.valves.ValveBase;\r
-\r
-/**\r
- * Valve to handle Tomcat jvmRoute takeover using mod_jk module after node\r
- * failure. After a node crashed the next request going to other cluster node.\r
- * Now the answering from apache is slower ( make some error handshaking. Very\r
- * bad with apache at my windows.). We rewrite now the cookie jsessionid\r
- * information to the backup cluster node. After the next response all client\r
- * request goes direct to the backup node. The change sessionid send also to all\r
- * other cluster nodes. Well, now the session stickyness work directly to the\r
- * backup node and traffic don't go back too restarted cluster nodes!\r
- * \r
- * At all cluster node you must configure the as ClusterListener since 5.5.10\r
- * {@link org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener JvmRouteSessionIDBinderListener}\r
- * or before with\r
- * org.apache.catalina.ha.session.JvmRouteSessionIDBinderListenerLifecycle.\r
- * \r
- * Add this Valve to your host definition at conf/server.xml .\r
- * \r
- * Since 5.5.10 as direct cluster valve:<br/>\r
- * <pre>\r
- * <Cluster>\r
- * <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" /> \r
- * </Cluster>\r
- * </pre>\r
- * <br />\r
- * Before 5.5.10 as Host element:<br/>\r
- * <pre>\r
- * <Hostr>\r
- * <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" /> \r
- * </Hostr>\r
- * </pre>\r
- * \r
- * Trick:<br/>\r
- * You can enable this mod_jk turnover mode via JMX before you drop a node to all backup nodes!\r
- * Set enable true on all JvmRouteBinderValve backups, disable worker at mod_jk \r
- * and then drop node and restart it! Then enable mod_jk Worker and disable JvmRouteBinderValves again. \r
- * This use case means that only requested session are migrated.\r
- * \r
- * @author Peter Rossbach\r
- * @version $Revision: 326110 $ $Date: 2005-10-18 09:08:36 -0500 (Tue, 18 Oct 2005) $\r
- */\r
-public class JvmRouteBinderValve extends ValveBase implements ClusterValve, Lifecycle {\r
-\r
- /*--Static Variables----------------------------------------*/\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory\r
- .getLog(JvmRouteBinderValve.class);\r
-\r
- /**\r
- * The descriptive information about this implementation.\r
- */\r
- protected static final String info = "org.apache.catalina.ha.session.JvmRouteBinderValve/1.2";\r
-\r
- /*--Instance Variables--------------------------------------*/\r
-\r
- /**\r
- * the cluster\r
- */\r
- protected CatalinaCluster cluster;\r
-\r
- /**\r
- * The string manager for this package.\r
- */\r
- protected StringManager sm = StringManager.getManager(Constants.Package);\r
-\r
- /**\r
- * Has this component been started yet?\r
- */\r
- protected boolean started = false;\r
-\r
- /**\r
- * enabled this component\r
- */\r
- protected boolean enabled = true;\r
-\r
- /**\r
- * number of session that no at this tomcat instanz hosted\r
- */\r
- protected long numberOfSessions = 0;\r
-\r
- protected String sessionIdAttribute = "org.apache.catalina.ha.session.JvmRouteOrignalSessionID";\r
-\r
- /**\r
- * The lifecycle event support for this component.\r
- */\r
- protected LifecycleSupport lifecycle = new LifecycleSupport(this);\r
-\r
- /*--Logic---------------------------------------------------*/\r
-\r
- /**\r
- * Return descriptive information about this implementation.\r
- */\r
- public String getInfo() {\r
-\r
- return (info);\r
-\r
- }\r
-\r
- /**\r
- * set session id attribute to failed node for request.\r
- * \r
- * @return Returns the sessionIdAttribute.\r
- */\r
- public String getSessionIdAttribute() {\r
- return sessionIdAttribute;\r
- }\r
-\r
- /**\r
- * get name of failed reqeust session attribute\r
- * \r
- * @param sessionIdAttribute\r
- * The sessionIdAttribute to set.\r
- */\r
- public void setSessionIdAttribute(String sessionIdAttribute) {\r
- this.sessionIdAttribute = sessionIdAttribute;\r
- }\r
-\r
- /**\r
- * @return Returns the number of migrated sessions.\r
- */\r
- public long getNumberOfSessions() {\r
- return numberOfSessions;\r
- }\r
-\r
- /**\r
- * @return Returns the enabled.\r
- */\r
- public boolean getEnabled() {\r
- return enabled;\r
- }\r
-\r
- /**\r
- * @param enabled\r
- * The enabled to set.\r
- */\r
- public void setEnabled(boolean enabled) {\r
- this.enabled = enabled;\r
- }\r
-\r
- /**\r
- * Detect possible the JVMRoute change at cluster backup node..\r
- * \r
- * @param request\r
- * tomcat request being processed\r
- * @param response\r
- * tomcat response being processed\r
- * @exception IOException\r
- * if an input/output error has occurred\r
- * @exception ServletException\r
- * if a servlet error has occurred\r
- */\r
- public void invoke(Request request, Response response) throws IOException,\r
- ServletException {\r
-\r
- if (getEnabled() \r
- && getCluster() != null\r
- && request.getContext() != null\r
- && request.getContext().getDistributable() ) {\r
- // valve cluster can access manager - other cluster handle turnover \r
- // at host level - hopefully!\r
- Manager manager = request.getContext().getManager();\r
- if (manager != null && manager instanceof ClusterManager\r
- && getCluster().getManager(((ClusterManager)manager).getName()) != null)\r
- handlePossibleTurnover(request, response);\r
- }\r
- // Pass this request on to the next valve in our pipeline\r
- getNext().invoke(request, response);\r
- }\r
-\r
- /**\r
- * handle possible session turn over.\r
- * \r
- * @see JvmRouteBinderValve#handleJvmRoute(Request, Response, String, String)\r
- * @param request current request\r
- * @param response current response\r
- */\r
- protected void handlePossibleTurnover(Request request, Response response) {\r
- Session session = request.getSessionInternal(false);\r
- if (session != null) {\r
- long t1 = System.currentTimeMillis();\r
- String jvmRoute = getLocalJvmRoute(request);\r
- if (jvmRoute == null) {\r
- if (log.isDebugEnabled())\r
- log.debug(sm.getString("jvmRoute.missingJvmRouteAttribute"));\r
- return;\r
- }\r
- handleJvmRoute( request, response,session.getIdInternal(), jvmRoute);\r
- if (log.isDebugEnabled()) {\r
- long t2 = System.currentTimeMillis();\r
- long time = t2 - t1;\r
- log.debug(sm.getString("jvmRoute.turnoverInfo", new Long(time)));\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * get jvmroute from engine\r
- * \r
- * @param request current request\r
- * @return return jvmRoute from ManagerBase or null\r
- */\r
- protected String getLocalJvmRoute(Request request) {\r
- Manager manager = getManager(request);\r
- if(manager instanceof ManagerBase)\r
- return ((ManagerBase) manager).getJvmRoute();\r
- return null ;\r
- }\r
-\r
- /**\r
- * get Cluster DeltaManager\r
- * \r
- * @param request current request\r
- * @return manager or null\r
- */\r
- protected Manager getManager(Request request) {\r
- Manager manager = request.getContext().getManager();\r
- if (log.isDebugEnabled()) {\r
- if(manager != null)\r
- log.debug(sm.getString("jvmRoute.foundManager", manager, request.getContext().getName()));\r
- else \r
- log.debug(sm.getString("jvmRoute.notFoundManager", manager, request.getContext().getName()));\r
- }\r
- return manager;\r
- }\r
-\r
- /**\r
- * @return Returns the cluster.\r
- */\r
- public CatalinaCluster getCluster() {\r
- return cluster;\r
- }\r
- \r
- /**\r
- * @param cluster The cluster to set.\r
- */\r
- public void setCluster(CatalinaCluster cluster) {\r
- this.cluster = cluster;\r
- }\r
- \r
- /**\r
- * Handle jvmRoute stickyness after tomcat instance failed. After this\r
- * correction a new Cookie send to client with new jvmRoute and the\r
- * SessionID change propage to the other cluster nodes.\r
- * \r
- * @param request current request\r
- * @param response\r
- * Tomcat Response\r
- * @param sessionId\r
- * request SessionID from Cookie\r
- * @param localJvmRoute\r
- * local jvmRoute\r
- */\r
- protected void handleJvmRoute(\r
- Request request, Response response,String sessionId, String localJvmRoute) {\r
- // get requested jvmRoute.\r
- String requestJvmRoute = null;\r
- int index = sessionId.indexOf(".");\r
- if (index > 0) {\r
- requestJvmRoute = sessionId\r
- .substring(index + 1, sessionId.length());\r
- }\r
- if (requestJvmRoute != null && !requestJvmRoute.equals(localJvmRoute)) {\r
- if (log.isDebugEnabled()) {\r
- log.debug(sm.getString("jvmRoute.failover", requestJvmRoute,\r
- localJvmRoute, sessionId));\r
- }\r
- // OK - turnover the session ?\r
- String newSessionID = sessionId.substring(0, index) + "."\r
- + localJvmRoute;\r
- Session catalinaSession = null;\r
- try {\r
- catalinaSession = getManager(request).findSession(sessionId);\r
- } catch (IOException e) {\r
- // Hups!\r
- }\r
- if (catalinaSession != null) {\r
- changeSessionID(request, response, sessionId, newSessionID,\r
- catalinaSession);\r
- numberOfSessions++;\r
- } else {\r
- if (log.isDebugEnabled()) {\r
- log.debug(sm.getString("jvmRoute.cannotFindSession",\r
- sessionId));\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * change session id and send to all cluster nodes\r
- * \r
- * @param request current request\r
- * @param response current response\r
- * @param sessionId\r
- * original session id\r
- * @param newSessionID\r
- * new session id for node migration\r
- * @param catalinaSession\r
- * current session with original session id\r
- */\r
- protected void changeSessionID(Request request,\r
- Response response, String sessionId, String newSessionID, Session catalinaSession) {\r
- lifecycle.fireLifecycleEvent("Before session migration",\r
- catalinaSession);\r
- request.setRequestedSessionId(newSessionID);\r
- catalinaSession.setId(newSessionID);\r
- if (catalinaSession instanceof DeltaSession)\r
- ((DeltaSession) catalinaSession).resetDeltaRequest();\r
- if(request.isRequestedSessionIdFromCookie()) setNewSessionCookie(request, response,newSessionID);\r
- // set orginal sessionid at request, to allow application detect the\r
- // change\r
- if (sessionIdAttribute != null && !"".equals(sessionIdAttribute)) {\r
- if (log.isDebugEnabled()) {\r
- log.debug(sm.getString("jvmRoute.set.orignalsessionid",sessionIdAttribute,sessionId));\r
- }\r
- request.setAttribute(sessionIdAttribute, sessionId);\r
- }\r
- // now sending the change to all other clusternode!\r
- ClusterManager manager = (ClusterManager)catalinaSession.getManager();\r
- sendSessionIDClusterBackup(manager,request,sessionId, newSessionID);\r
- lifecycle\r
- .fireLifecycleEvent("After session migration", catalinaSession);\r
- if (log.isDebugEnabled()) {\r
- log.debug(sm.getString("jvmRoute.changeSession", sessionId,\r
- newSessionID));\r
- }\r
- }\r
-\r
- /**\r
- * Send the changed Sessionid to all clusternodes.\r
- * \r
- * @see JvmRouteSessionIDBinderListener#messageReceived(ClusterMessage)\r
- * @param manager\r
- * ClusterManager\r
- * @param sessionId\r
- * current failed sessionid\r
- * @param newSessionID\r
- * new session id, bind to the new cluster node\r
- */\r
- protected void sendSessionIDClusterBackup(ClusterManager manager,Request request,String sessionId,\r
- String newSessionID) {\r
- SessionIDMessage msg = new SessionIDMessage();\r
- msg.setOrignalSessionID(sessionId);\r
- msg.setBackupSessionID(newSessionID);\r
- Context context = request.getContext();\r
- msg.setContextPath(context.getPath());\r
- msg.setHost(context.getParent().getName());\r
- if(manager.doDomainReplication())\r
- cluster.sendClusterDomain(msg);\r
- else\r
- cluster.send(msg);\r
- }\r
-\r
- /**\r
- * Sets a new cookie for the given session id and response and see\r
- * {@link org.apache.catalina.connector.Request#configureSessionCookie(javax.servlet.http.Cookie)}\r
- * \r
- * @param request current request\r
- * @param response Tomcat Response\r
- * @param sessionId The session id\r
- */\r
- protected void setNewSessionCookie(Request request,\r
- Response response, String sessionId) {\r
- if (response != null) {\r
- Context context = request.getContext();\r
- if (context.getCookies()) {\r
- // set a new session cookie\r
- Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME,\r
- sessionId);\r
- newCookie.setMaxAge(-1);\r
- String contextPath = null;\r
- if (!response.getConnector().getEmptySessionPath()\r
- && (context != null)) {\r
- contextPath = context.getEncodedPath();\r
- }\r
- if ((contextPath != null) && (contextPath.length() > 0)) {\r
- newCookie.setPath(contextPath);\r
- } else {\r
- newCookie.setPath("/");\r
- }\r
- if (request.isSecure()) {\r
- newCookie.setSecure(true);\r
- }\r
- if (log.isDebugEnabled()) {\r
- log.debug(sm.getString("jvmRoute.newSessionCookie",\r
- sessionId, Globals.SESSION_COOKIE_NAME, newCookie\r
- .getPath(), new Boolean(newCookie\r
- .getSecure())));\r
- }\r
- response.addCookie(newCookie);\r
- }\r
- }\r
- }\r
-\r
- // ------------------------------------------------------ Lifecycle Methods\r
-\r
- /**\r
- * Add a lifecycle event listener to this component.\r
- * \r
- * @param listener\r
- * The listener to add\r
- */\r
- public void addLifecycleListener(LifecycleListener listener) {\r
-\r
- lifecycle.addLifecycleListener(listener);\r
-\r
- }\r
-\r
- /**\r
- * Get the lifecycle listeners associated with this lifecycle. If this\r
- * Lifecycle has no listeners registered, a zero-length array is returned.\r
- */\r
- public LifecycleListener[] findLifecycleListeners() {\r
-\r
- return lifecycle.findLifecycleListeners();\r
-\r
- }\r
-\r
- /**\r
- * Remove a lifecycle event listener from this component.\r
- * \r
- * @param listener\r
- * The listener to add\r
- */\r
- public void removeLifecycleListener(LifecycleListener listener) {\r
-\r
- lifecycle.removeLifecycleListener(listener);\r
-\r
- }\r
-\r
- /**\r
- * Prepare for the beginning of active use of the public methods of this\r
- * component. This method should be called after <code>configure()</code>,\r
- * and before any of the public methods of the component are utilized.\r
- * \r
- * @exception LifecycleException\r
- * if this component detects a fatal error that prevents this\r
- * component from being used\r
- */\r
- public void start() throws LifecycleException {\r
-\r
- // Validate and update our current component state\r
- if (started)\r
- throw new LifecycleException(sm\r
- .getString("jvmRoute.valve.alreadyStarted"));\r
- lifecycle.fireLifecycleEvent(START_EVENT, null);\r
- started = true;\r
- if (cluster == null) {\r
- Container hostContainer = getContainer();\r
- // compatibility with JvmRouteBinderValve version 1.1\r
- // ( setup at context.xml or context.xml.default )\r
- if (!(hostContainer instanceof Host)) {\r
- if (log.isWarnEnabled())\r
- log.warn(sm.getString("jvmRoute.configure.warn"));\r
- hostContainer = hostContainer.getParent();\r
- }\r
- if (hostContainer instanceof Host\r
- && ((Host) hostContainer).getCluster() != null) {\r
- cluster = (CatalinaCluster) ((Host) hostContainer).getCluster();\r
- } else {\r
- Container engine = hostContainer.getParent() ;\r
- if (engine instanceof Engine\r
- && ((Engine) engine).getCluster() != null) {\r
- cluster = (CatalinaCluster) ((Engine) engine).getCluster();\r
- }\r
- }\r
- }\r
- if (cluster == null) {\r
- throw new RuntimeException("No clustering support at container "\r
- + container.getName());\r
- }\r
- \r
- if (log.isInfoEnabled())\r
- log.info(sm.getString("jvmRoute.valve.started"));\r
-\r
- }\r
-\r
- /**\r
- * Gracefully terminate the active use of the public methods of this\r
- * component. This method should be the last one called on a given instance\r
- * of this component.\r
- * \r
- * @exception LifecycleException\r
- * if this component detects a fatal error that needs to be\r
- * reported\r
- */\r
- public void stop() throws LifecycleException {\r
-\r
- // Validate and update our current component state\r
- if (!started)\r
- throw new LifecycleException(sm\r
- .getString("jvmRoute.valve.notStarted"));\r
- lifecycle.fireLifecycleEvent(STOP_EVENT, null);\r
- started = false;\r
- cluster = null;\r
- numberOfSessions = 0;\r
- if (log.isInfoEnabled())\r
- log.info(sm.getString("jvmRoute.valve.stopped"));\r
-\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.catalina.ha.ClusterManager;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.ha.ClusterValve;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.session.ManagerBase;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+
+/**
+ * Valve to handle Tomcat jvmRoute takeover using mod_jk module after node
+ * failure. After a node crashed the next request going to other cluster node.
+ * Now the answering from apache is slower ( make some error handshaking. Very
+ * bad with apache at my windows.). We rewrite now the cookie jsessionid
+ * information to the backup cluster node. After the next response all client
+ * request goes direct to the backup node. The change sessionid send also to all
+ * other cluster nodes. Well, now the session stickyness work directly to the
+ * backup node and traffic don't go back too restarted cluster nodes!
+ *
+ * At all cluster node you must configure the as ClusterListener since 5.5.10
+ * {@link org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener JvmRouteSessionIDBinderListener}
+ * or before with
+ * org.apache.catalina.ha.session.JvmRouteSessionIDBinderListenerLifecycle.
+ *
+ * Add this Valve to your host definition at conf/server.xml .
+ *
+ * Since 5.5.10 as direct cluster valve:<br/>
+ * <pre>
+ * <Cluster>
+ * <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
+ * </Cluster>
+ * </pre>
+ * <br />
+ * Before 5.5.10 as Host element:<br/>
+ * <pre>
+ * <Hostr>
+ * <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
+ * </Hostr>
+ * </pre>
+ *
+ * Trick:<br/>
+ * You can enable this mod_jk turnover mode via JMX before you drop a node to all backup nodes!
+ * Set enable true on all JvmRouteBinderValve backups, disable worker at mod_jk
+ * and then drop node and restart it! Then enable mod_jk Worker and disable JvmRouteBinderValves again.
+ * This use case means that only requested session are migrated.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class JvmRouteBinderValve extends ValveBase implements ClusterValve, Lifecycle {
+
+ /*--Static Variables----------------------------------------*/
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+ .getLog(JvmRouteBinderValve.class);
+
+ /**
+ * The descriptive information about this implementation.
+ */
+ protected static final String info = "org.apache.catalina.ha.session.JvmRouteBinderValve/1.2";
+
+ /*--Instance Variables--------------------------------------*/
+
+ /**
+ * the cluster
+ */
+ protected CatalinaCluster cluster;
+
+ /**
+ * The string manager for this package.
+ */
+ protected StringManager sm = StringManager.getManager(Constants.Package);
+
+ /**
+ * Has this component been started yet?
+ */
+ protected boolean started = false;
+
+ /**
+ * enabled this component
+ */
+ protected boolean enabled = true;
+
+ /**
+ * number of session that no at this tomcat instanz hosted
+ */
+ protected long numberOfSessions = 0;
+
+ protected String sessionIdAttribute = "org.apache.catalina.ha.session.JvmRouteOrignalSessionID";
+
+ /**
+ * The lifecycle event support for this component.
+ */
+ protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /*--Logic---------------------------------------------------*/
+
+ /**
+ * Return descriptive information about this implementation.
+ */
+ public String getInfo() {
+
+ return (info);
+
+ }
+
+ /**
+ * set session id attribute to failed node for request.
+ *
+ * @return Returns the sessionIdAttribute.
+ */
+ public String getSessionIdAttribute() {
+ return sessionIdAttribute;
+ }
+
+ /**
+ * get name of failed reqeust session attribute
+ *
+ * @param sessionIdAttribute
+ * The sessionIdAttribute to set.
+ */
+ public void setSessionIdAttribute(String sessionIdAttribute) {
+ this.sessionIdAttribute = sessionIdAttribute;
+ }
+
+ /**
+ * @return Returns the number of migrated sessions.
+ */
+ public long getNumberOfSessions() {
+ return numberOfSessions;
+ }
+
+ /**
+ * @return Returns the enabled.
+ */
+ public boolean getEnabled() {
+ return enabled;
+ }
+
+ /**
+ * @param enabled
+ * The enabled to set.
+ */
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * Detect possible the JVMRoute change at cluster backup node..
+ *
+ * @param request
+ * tomcat request being processed
+ * @param response
+ * tomcat response being processed
+ * @exception IOException
+ * if an input/output error has occurred
+ * @exception ServletException
+ * if a servlet error has occurred
+ */
+ public void invoke(Request request, Response response) throws IOException,
+ ServletException {
+
+ if (getEnabled()
+ && getCluster() != null
+ && request.getContext() != null
+ && request.getContext().getDistributable() ) {
+ // valve cluster can access manager - other cluster handle turnover
+ // at host level - hopefully!
+ Manager manager = request.getContext().getManager();
+ if (manager != null && manager instanceof ClusterManager
+ && getCluster().getManager(((ClusterManager)manager).getName()) != null)
+ handlePossibleTurnover(request, response);
+ }
+ // Pass this request on to the next valve in our pipeline
+ getNext().invoke(request, response);
+ }
+
+ /**
+ * handle possible session turn over.
+ *
+ * @see JvmRouteBinderValve#handleJvmRoute(Request, Response, String, String)
+ * @param request current request
+ * @param response current response
+ */
+ protected void handlePossibleTurnover(Request request, Response response) {
+ Session session = request.getSessionInternal(false);
+ if (session != null) {
+ long t1 = System.currentTimeMillis();
+ String jvmRoute = getLocalJvmRoute(request);
+ if (jvmRoute == null) {
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("jvmRoute.missingJvmRouteAttribute"));
+ return;
+ }
+ handleJvmRoute( request, response,session.getIdInternal(), jvmRoute);
+ if (log.isDebugEnabled()) {
+ long t2 = System.currentTimeMillis();
+ long time = t2 - t1;
+ log.debug(sm.getString("jvmRoute.turnoverInfo", new Long(time)));
+ }
+ }
+ }
+
+ /**
+ * get jvmroute from engine
+ *
+ * @param request current request
+ * @return return jvmRoute from ManagerBase or null
+ */
+ protected String getLocalJvmRoute(Request request) {
+ Manager manager = getManager(request);
+ if(manager instanceof ManagerBase)
+ return ((ManagerBase) manager).getJvmRoute();
+ return null ;
+ }
+
+ /**
+ * get Cluster DeltaManager
+ *
+ * @param request current request
+ * @return manager or null
+ */
+ protected Manager getManager(Request request) {
+ Manager manager = request.getContext().getManager();
+ if (log.isDebugEnabled()) {
+ if(manager != null)
+ log.debug(sm.getString("jvmRoute.foundManager", manager, request.getContext().getName()));
+ else
+ log.debug(sm.getString("jvmRoute.notFoundManager", manager, request.getContext().getName()));
+ }
+ return manager;
+ }
+
+ /**
+ * @return Returns the cluster.
+ */
+ public CatalinaCluster getCluster() {
+ return cluster;
+ }
+
+ /**
+ * @param cluster The cluster to set.
+ */
+ public void setCluster(CatalinaCluster cluster) {
+ this.cluster = cluster;
+ }
+
+ /**
+ * Handle jvmRoute stickyness after tomcat instance failed. After this
+ * correction a new Cookie send to client with new jvmRoute and the
+ * SessionID change propage to the other cluster nodes.
+ *
+ * @param request current request
+ * @param response
+ * Tomcat Response
+ * @param sessionId
+ * request SessionID from Cookie
+ * @param localJvmRoute
+ * local jvmRoute
+ */
+ protected void handleJvmRoute(
+ Request request, Response response,String sessionId, String localJvmRoute) {
+ // get requested jvmRoute.
+ String requestJvmRoute = null;
+ int index = sessionId.indexOf(".");
+ if (index > 0) {
+ requestJvmRoute = sessionId
+ .substring(index + 1, sessionId.length());
+ }
+ if (requestJvmRoute != null && !requestJvmRoute.equals(localJvmRoute)) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("jvmRoute.failover", requestJvmRoute,
+ localJvmRoute, sessionId));
+ }
+ // OK - turnover the session ?
+ String newSessionID = sessionId.substring(0, index) + "."
+ + localJvmRoute;
+ Session catalinaSession = null;
+ try {
+ catalinaSession = getManager(request).findSession(sessionId);
+ } catch (IOException e) {
+ // Hups!
+ }
+ if (catalinaSession != null) {
+ changeSessionID(request, response, sessionId, newSessionID,
+ catalinaSession);
+ numberOfSessions++;
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("jvmRoute.cannotFindSession",
+ sessionId));
+ }
+ }
+ }
+ }
+
+ /**
+ * change session id and send to all cluster nodes
+ *
+ * @param request current request
+ * @param response current response
+ * @param sessionId
+ * original session id
+ * @param newSessionID
+ * new session id for node migration
+ * @param catalinaSession
+ * current session with original session id
+ */
+ protected void changeSessionID(Request request,
+ Response response, String sessionId, String newSessionID, Session catalinaSession) {
+ lifecycle.fireLifecycleEvent("Before session migration",
+ catalinaSession);
+ request.setRequestedSessionId(newSessionID);
+ catalinaSession.setId(newSessionID);
+ if (catalinaSession instanceof DeltaSession)
+ ((DeltaSession) catalinaSession).resetDeltaRequest();
+ if(request.isRequestedSessionIdFromCookie()) setNewSessionCookie(request, response,newSessionID);
+ // set orginal sessionid at request, to allow application detect the
+ // change
+ if (sessionIdAttribute != null && !"".equals(sessionIdAttribute)) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("jvmRoute.set.orignalsessionid",sessionIdAttribute,sessionId));
+ }
+ request.setAttribute(sessionIdAttribute, sessionId);
+ }
+ // now sending the change to all other clusternode!
+ ClusterManager manager = (ClusterManager)catalinaSession.getManager();
+ sendSessionIDClusterBackup(manager,request,sessionId, newSessionID);
+ lifecycle
+ .fireLifecycleEvent("After session migration", catalinaSession);
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("jvmRoute.changeSession", sessionId,
+ newSessionID));
+ }
+ }
+
+ /**
+ * Send the changed Sessionid to all clusternodes.
+ *
+ * @see JvmRouteSessionIDBinderListener#messageReceived(ClusterMessage)
+ * @param manager
+ * ClusterManager
+ * @param sessionId
+ * current failed sessionid
+ * @param newSessionID
+ * new session id, bind to the new cluster node
+ */
+ protected void sendSessionIDClusterBackup(ClusterManager manager,Request request,String sessionId,
+ String newSessionID) {
+ SessionIDMessage msg = new SessionIDMessage();
+ msg.setOrignalSessionID(sessionId);
+ msg.setBackupSessionID(newSessionID);
+ Context context = request.getContext();
+ msg.setContextPath(context.getPath());
+ msg.setHost(context.getParent().getName());
+ if(manager.doDomainReplication())
+ cluster.sendClusterDomain(msg);
+ else
+ cluster.send(msg);
+ }
+
+ /**
+ * Sets a new cookie for the given session id and response and see
+ * {@link org.apache.catalina.connector.Request#configureSessionCookie(javax.servlet.http.Cookie)}
+ *
+ * @param request current request
+ * @param response Tomcat Response
+ * @param sessionId The session id
+ */
+ protected void setNewSessionCookie(Request request,
+ Response response, String sessionId) {
+ if (response != null) {
+ Context context = request.getContext();
+ if (context.getCookies()) {
+ // set a new session cookie
+ Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME,
+ sessionId);
+ newCookie.setMaxAge(-1);
+ String contextPath = null;
+ if (!response.getConnector().getEmptySessionPath()
+ && (context != null)) {
+ contextPath = context.getEncodedPath();
+ }
+ if ((contextPath != null) && (contextPath.length() > 0)) {
+ newCookie.setPath(contextPath);
+ } else {
+ newCookie.setPath("/");
+ }
+ if (request.isSecure()) {
+ newCookie.setSecure(true);
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("jvmRoute.newSessionCookie",
+ sessionId, Globals.SESSION_COOKIE_NAME, newCookie
+ .getPath(), new Boolean(newCookie
+ .getSecure())));
+ }
+ response.addCookie(newCookie);
+ }
+ }
+ }
+
+ // ------------------------------------------------------ Lifecycle Methods
+
+ /**
+ * Add a lifecycle event listener to this component.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener) {
+
+ lifecycle.addLifecycleListener(listener);
+
+ }
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners() {
+
+ return lifecycle.findLifecycleListeners();
+
+ }
+
+ /**
+ * Remove a lifecycle event listener from this component.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public void removeLifecycleListener(LifecycleListener listener) {
+
+ lifecycle.removeLifecycleListener(listener);
+
+ }
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized.
+ *
+ * @exception LifecycleException
+ * if this component detects a fatal error that prevents this
+ * component from being used
+ */
+ public void start() throws LifecycleException {
+
+ // Validate and update our current component state
+ if (started)
+ throw new LifecycleException(sm
+ .getString("jvmRoute.valve.alreadyStarted"));
+ lifecycle.fireLifecycleEvent(START_EVENT, null);
+ started = true;
+ if (cluster == null) {
+ Container hostContainer = getContainer();
+ // compatibility with JvmRouteBinderValve version 1.1
+ // ( setup at context.xml or context.xml.default )
+ if (!(hostContainer instanceof Host)) {
+ if (log.isWarnEnabled())
+ log.warn(sm.getString("jvmRoute.configure.warn"));
+ hostContainer = hostContainer.getParent();
+ }
+ if (hostContainer instanceof Host
+ && ((Host) hostContainer).getCluster() != null) {
+ cluster = (CatalinaCluster) ((Host) hostContainer).getCluster();
+ } else {
+ Container engine = hostContainer.getParent() ;
+ if (engine instanceof Engine
+ && ((Engine) engine).getCluster() != null) {
+ cluster = (CatalinaCluster) ((Engine) engine).getCluster();
+ }
+ }
+ }
+ if (cluster == null) {
+ throw new RuntimeException("No clustering support at container "
+ + container.getName());
+ }
+
+ if (log.isInfoEnabled())
+ log.info(sm.getString("jvmRoute.valve.started"));
+
+ }
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given instance
+ * of this component.
+ *
+ * @exception LifecycleException
+ * if this component detects a fatal error that needs to be
+ * reported
+ */
+ public void stop() throws LifecycleException {
+
+ // Validate and update our current component state
+ if (!started)
+ throw new LifecycleException(sm
+ .getString("jvmRoute.valve.notStarted"));
+ lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ started = false;
+ cluster = null;
+ numberOfSessions = 0;
+ if (log.isInfoEnabled())
+ log.info(sm.getString("jvmRoute.valve.stopped"));
+
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-import java.io.IOException;\r
-\r
-import org.apache.catalina.Container;\r
-import org.apache.catalina.Context;\r
-import org.apache.catalina.Engine;\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.Session;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.core.StandardEngine;\r
-import org.apache.catalina.ha.*;\r
-\r
-/**\r
- * Receive SessionID cluster change from other backup node after primary session\r
- * node is failed.\r
- * \r
- * @author Peter Rossbach\r
- * @version $Revision: 378258 $ $Date: 2006-02-16 08:42:35 -0600 (Thu, 16 Feb 2006) $\r
- */\r
-public class JvmRouteSessionIDBinderListener extends ClusterListener {\r
- \r
- /**\r
- * The descriptive information about this implementation.\r
- */\r
- protected static final String info = "org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener/1.1";\r
-\r
- //--Instance Variables--------------------------------------\r
-\r
-\r
- protected boolean started = false;\r
-\r
- /**\r
- * number of session that goes to this cluster node\r
- */\r
- private long numberOfSessions = 0;\r
-\r
- //--Constructor---------------------------------------------\r
-\r
- public JvmRouteSessionIDBinderListener() {\r
- }\r
-\r
- //--Logic---------------------------------------------------\r
-\r
- /**\r
- * Return descriptive information about this implementation.\r
- */\r
- public String getInfo() {\r
-\r
- return (info);\r
-\r
- }\r
-\r
- /**\r
- * @return Returns the numberOfSessions.\r
- */\r
- public long getNumberOfSessions() {\r
- return numberOfSessions;\r
- }\r
-\r
- /**\r
- * Add this Mover as Cluster Listener ( receiver)\r
- * \r
- * @throws LifecycleException\r
- */\r
- public void start() throws LifecycleException {\r
- if (started)\r
- return;\r
- getCluster().addClusterListener(this);\r
- started = true;\r
- if (log.isInfoEnabled())\r
- log.info(sm.getString("jvmRoute.clusterListener.started"));\r
- }\r
-\r
- /**\r
- * Remove this from Cluster Listener\r
- * \r
- * @throws LifecycleException\r
- */\r
- public void stop() throws LifecycleException {\r
- started = false;\r
- getCluster().removeClusterListener(this);\r
- if (log.isInfoEnabled())\r
- log.info(sm.getString("jvmRoute.clusterListener.stopped"));\r
- }\r
-\r
- /**\r
- * Callback from the cluster, when a message is received, The cluster will\r
- * broadcast it invoking the messageReceived on the receiver.\r
- * \r
- * @param msg\r
- * ClusterMessage - the message received from the cluster\r
- */\r
- public void messageReceived(ClusterMessage msg) {\r
- if (msg instanceof SessionIDMessage && msg != null) {\r
- SessionIDMessage sessionmsg = (SessionIDMessage) msg;\r
- if (log.isDebugEnabled())\r
- log.debug(sm.getString(\r
- "jvmRoute.receiveMessage.sessionIDChanged", sessionmsg\r
- .getOrignalSessionID(), sessionmsg\r
- .getBackupSessionID(), sessionmsg\r
- .getContextPath()));\r
- Container container = getCluster().getContainer();\r
- Container host = null ;\r
- if(container instanceof Engine) {\r
- host = container.findChild(sessionmsg.getHost());\r
- } else {\r
- host = container ;\r
- }\r
- if (host != null) {\r
- Context context = (Context) host.findChild(sessionmsg\r
- .getContextPath());\r
- if (context != null) {\r
- try {\r
- Session session = context.getManager().findSession(\r
- sessionmsg.getOrignalSessionID());\r
- if (session != null) {\r
- session.setId(sessionmsg.getBackupSessionID());\r
- } else if (log.isInfoEnabled())\r
- log.info(sm.getString("jvmRoute.lostSession",\r
- sessionmsg.getOrignalSessionID(),\r
- sessionmsg.getContextPath()));\r
- } catch (IOException e) {\r
- log.error(e);\r
- }\r
-\r
- } else if (log.isErrorEnabled())\r
- log.error(sm.getString("jvmRoute.contextNotFound",\r
- sessionmsg.getContextPath(), ((StandardEngine) host\r
- .getParent()).getJvmRoute()));\r
- } else if (log.isErrorEnabled())\r
- log.error(sm.getString("jvmRoute.hostNotFound", sessionmsg.getContextPath()));\r
- }\r
- return;\r
- }\r
-\r
- /**\r
- * Accept only SessionIDMessages\r
- * \r
- * @param msg\r
- * ClusterMessage\r
- * @return boolean - returns true to indicate that messageReceived should be\r
- * invoked. If false is returned, the messageReceived method will\r
- * not be invoked.\r
- */\r
- public boolean accept(ClusterMessage msg) {\r
- return (msg instanceof SessionIDMessage);\r
- }\r
-}\r
-\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import java.io.IOException;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.ha.*;
+
+/**
+ * Receive SessionID cluster change from other backup node after primary session
+ * node is failed.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class JvmRouteSessionIDBinderListener extends ClusterListener {
+
+ /**
+ * The descriptive information about this implementation.
+ */
+ protected static final String info = "org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener/1.1";
+
+ //--Instance Variables--------------------------------------
+
+
+ protected boolean started = false;
+
+ /**
+ * number of session that goes to this cluster node
+ */
+ private long numberOfSessions = 0;
+
+ //--Constructor---------------------------------------------
+
+ public JvmRouteSessionIDBinderListener() {
+ }
+
+ //--Logic---------------------------------------------------
+
+ /**
+ * Return descriptive information about this implementation.
+ */
+ public String getInfo() {
+
+ return (info);
+
+ }
+
+ /**
+ * @return Returns the numberOfSessions.
+ */
+ public long getNumberOfSessions() {
+ return numberOfSessions;
+ }
+
+ /**
+ * Add this Mover as Cluster Listener ( receiver)
+ *
+ * @throws LifecycleException
+ */
+ public void start() throws LifecycleException {
+ if (started)
+ return;
+ getCluster().addClusterListener(this);
+ started = true;
+ if (log.isInfoEnabled())
+ log.info(sm.getString("jvmRoute.clusterListener.started"));
+ }
+
+ /**
+ * Remove this from Cluster Listener
+ *
+ * @throws LifecycleException
+ */
+ public void stop() throws LifecycleException {
+ started = false;
+ getCluster().removeClusterListener(this);
+ if (log.isInfoEnabled())
+ log.info(sm.getString("jvmRoute.clusterListener.stopped"));
+ }
+
+ /**
+ * Callback from the cluster, when a message is received, The cluster will
+ * broadcast it invoking the messageReceived on the receiver.
+ *
+ * @param msg
+ * ClusterMessage - the message received from the cluster
+ */
+ public void messageReceived(ClusterMessage msg) {
+ if (msg instanceof SessionIDMessage && msg != null) {
+ SessionIDMessage sessionmsg = (SessionIDMessage) msg;
+ if (log.isDebugEnabled())
+ log.debug(sm.getString(
+ "jvmRoute.receiveMessage.sessionIDChanged", sessionmsg
+ .getOrignalSessionID(), sessionmsg
+ .getBackupSessionID(), sessionmsg
+ .getContextPath()));
+ Container container = getCluster().getContainer();
+ Container host = null ;
+ if(container instanceof Engine) {
+ host = container.findChild(sessionmsg.getHost());
+ } else {
+ host = container ;
+ }
+ if (host != null) {
+ Context context = (Context) host.findChild(sessionmsg
+ .getContextPath());
+ if (context != null) {
+ try {
+ Session session = context.getManager().findSession(
+ sessionmsg.getOrignalSessionID());
+ if (session != null) {
+ session.setId(sessionmsg.getBackupSessionID());
+ } else if (log.isInfoEnabled())
+ log.info(sm.getString("jvmRoute.lostSession",
+ sessionmsg.getOrignalSessionID(),
+ sessionmsg.getContextPath()));
+ } catch (IOException e) {
+ log.error(e);
+ }
+
+ } else if (log.isErrorEnabled())
+ log.error(sm.getString("jvmRoute.contextNotFound",
+ sessionmsg.getContextPath(), ((StandardEngine) host
+ .getParent()).getJvmRoute()));
+ } else if (log.isErrorEnabled())
+ log.error(sm.getString("jvmRoute.hostNotFound", sessionmsg.getContextPath()));
+ }
+ return;
+ }
+
+ /**
+ * Accept only SessionIDMessages
+ *
+ * @param msg
+ * ClusterMessage
+ * @return boolean - returns true to indicate that messageReceived should be
+ * invoked. If false is returned, the messageReceived method will
+ * not be invoked.
+ */
+ public boolean accept(ClusterMessage msg) {
+ return (msg instanceof SessionIDMessage);
+ }
+}
+
-\r
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-/**\r
- * Title: Tomcat Session Replication for Tomcat 4.0 <BR>\r
- * Description: A very simple straight forward implementation of\r
- * session replication of servers in a cluster.<BR>\r
- * This session replication is implemented "live". By live\r
- * I mean, when a session attribute is added into a session on Node A\r
- * a message is broadcasted to other messages and setAttribute is called on the replicated\r
- * sessions.<BR>\r
- * A full description of this implementation can be found under\r
- * <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a><BR>\r
- *\r
- * Copyright: See apache license\r
- * @author Filip Hanik\r
- * @version $Revision: 303842 $ $Date: 2005-04-10 11:20:46 -0500 (Sun, 10 Apr 2005) $\r
- * Description:<BR>\r
- * The ReplicatedSession class is a simple extension of the StandardSession class\r
- * It overrides a few methods (setAttribute, removeAttribute, expire, access) and has\r
- * hooks into the InMemoryReplicationManager to broadcast and receive events from the cluster.<BR>\r
- * This class inherits the readObjectData and writeObject data methods from the StandardSession\r
- * and does not contain any serializable elements in addition to the inherited ones from the StandardSession\r
- *\r
- */\r
-import org.apache.catalina.Manager;\r
-import java.io.IOException;\r
-import java.io.ObjectInputStream;\r
-import java.io.ObjectOutputStream;\r
-import java.security.Principal;\r
-\r
-public class ReplicatedSession extends org.apache.catalina.session.StandardSession\r
-implements org.apache.catalina.ha.ClusterSession{\r
-\r
- private transient Manager mManager = null;\r
- protected boolean isDirty = false;\r
- private transient long lastAccessWasDistributed = System.currentTimeMillis();\r
- private boolean isPrimarySession=true;\r
- \r
-\r
- public ReplicatedSession(Manager manager) {\r
- super(manager);\r
- mManager = manager;\r
- }\r
-\r
-\r
- public boolean isDirty()\r
- {\r
- return isDirty;\r
- }\r
-\r
- public void setIsDirty(boolean dirty)\r
- {\r
- isDirty = dirty;\r
- }\r
-\r
-\r
- public void setLastAccessWasDistributed(long time) {\r
- lastAccessWasDistributed = time;\r
- }\r
-\r
- public long getLastAccessWasDistributed() {\r
- return lastAccessWasDistributed;\r
- }\r
-\r
-\r
- public void removeAttribute(String name) {\r
- setIsDirty(true);\r
- super.removeAttribute(name);\r
- }\r
-\r
- /**\r
- * see parent description,\r
- * plus we also notify other nodes in the cluster\r
- */\r
- public void removeAttribute(String name, boolean notify) {\r
- setIsDirty(true);\r
- super.removeAttribute(name,notify);\r
- }\r
-\r
-\r
- /**\r
- * Sets an attribute and notifies the other nodes in the cluster\r
- */\r
- public void setAttribute(String name, Object value)\r
- {\r
- if ( value == null ) {\r
- removeAttribute(name);\r
- return;\r
- }\r
- if (!(value instanceof java.io.Serializable))\r
- throw new java.lang.IllegalArgumentException("Value for attribute "+name+" is not serializable.");\r
- setIsDirty(true);\r
- super.setAttribute(name,value);\r
- }\r
-\r
- public void setMaxInactiveInterval(int interval) {\r
- setIsDirty(true);\r
- super.setMaxInactiveInterval(interval);\r
- }\r
-\r
-\r
- /**\r
- * Sets the manager for this session\r
- * @param mgr - the servers InMemoryReplicationManager\r
- */\r
- public void setManager(SimpleTcpReplicationManager mgr)\r
- {\r
- mManager = mgr;\r
- super.setManager(mgr);\r
- }\r
-\r
-\r
- /**\r
- * Set the authenticated Principal that is associated with this Session.\r
- * This provides an <code>Authenticator</code> with a means to cache a\r
- * previously authenticated Principal, and avoid potentially expensive\r
- * <code>Realm.authenticate()</code> calls on every request.\r
- *\r
- * @param principal The new Principal, or <code>null</code> if none\r
- */\r
- public void setPrincipal(Principal principal) {\r
- super.setPrincipal(principal);\r
- setIsDirty(true);\r
- }\r
-\r
- public void expire() {\r
- SimpleTcpReplicationManager mgr =(SimpleTcpReplicationManager)getManager();\r
- mgr.sessionInvalidated(getIdInternal());\r
- setIsDirty(true);\r
- super.expire();\r
- }\r
-\r
- public void invalidate() {\r
- SimpleTcpReplicationManager mgr =(SimpleTcpReplicationManager)getManager();\r
- mgr.sessionInvalidated(getIdInternal());\r
- setIsDirty(true);\r
- super.invalidate();\r
- }\r
-\r
-\r
- /**\r
- * Read a serialized version of the contents of this session object from\r
- * the specified object input stream, without requiring that the\r
- * StandardSession itself have been serialized.\r
- *\r
- * @param stream The object input stream to read from\r
- *\r
- * @exception ClassNotFoundException if an unknown class is specified\r
- * @exception IOException if an input/output error occurs\r
- */\r
- public void readObjectData(ObjectInputStream stream)\r
- throws ClassNotFoundException, IOException {\r
-\r
- super.readObjectData(stream);\r
-\r
- }\r
-\r
-\r
- /**\r
- * Write a serialized version of the contents of this session object to\r
- * the specified object output stream, without requiring that the\r
- * StandardSession itself have been serialized.\r
- *\r
- * @param stream The object output stream to write to\r
- *\r
- * @exception IOException if an input/output error occurs\r
- */\r
- public void writeObjectData(ObjectOutputStream stream)\r
- throws IOException {\r
-\r
- super.writeObjectData(stream);\r
-\r
- }\r
- \r
- public void setId(String id, boolean tellNew) {\r
-\r
- if ((this.id != null) && (manager != null))\r
- manager.remove(this);\r
-\r
- this.id = id;\r
-\r
- if (manager != null)\r
- manager.add(this);\r
- if (tellNew) tellNew();\r
- }\r
- \r
- \r
-\r
-\r
-\r
-\r
-\r
-\r
- /**\r
- * returns true if this session is the primary session, if that is the\r
- * case, the manager can expire it upon timeout.\r
- */\r
- public boolean isPrimarySession() {\r
- return isPrimarySession;\r
- }\r
-\r
- /**\r
- * Sets whether this is the primary session or not.\r
- * @param primarySession Flag value\r
- */\r
- public void setPrimarySession(boolean primarySession) {\r
- this.isPrimarySession=primarySession;\r
- }\r
-\r
-\r
-\r
-\r
- /**\r
- * Implements a log method to log through the manager\r
- */\r
- protected void log(String message) {\r
-\r
- if ((mManager != null) && (mManager instanceof SimpleTcpReplicationManager)) {\r
- ((SimpleTcpReplicationManager) mManager).log.debug("ReplicatedSession: " + message);\r
- } else {\r
- System.out.println("ReplicatedSession: " + message);\r
- }\r
-\r
- }\r
-\r
- protected void log(String message, Throwable x) {\r
-\r
- if ((mManager != null) && (mManager instanceof SimpleTcpReplicationManager)) {\r
- ((SimpleTcpReplicationManager) mManager).log.error("ReplicatedSession: " + message,x);\r
- } else {\r
- System.out.println("ReplicatedSession: " + message);\r
- x.printStackTrace();\r
- }\r
-\r
- }\r
-\r
- public String toString() {\r
- StringBuffer buf = new StringBuffer("ReplicatedSession id=");\r
- buf.append(getIdInternal()).append(" ref=").append(super.toString()).append("\n");\r
- java.util.Enumeration e = getAttributeNames();\r
- while ( e.hasMoreElements() ) {\r
- String name = (String)e.nextElement();\r
- Object value = getAttribute(name);\r
- buf.append("\tname=").append(name).append("; value=").append(value).append("\n");\r
- }\r
- buf.append("\tLastAccess=").append(getLastAccessedTime()).append("\n");\r
- return buf.toString();\r
- }\r
- public int getAccessCount() {\r
- return accessCount.get();\r
- }\r
- public void setAccessCount(int accessCount) {\r
- this.accessCount.set(accessCount);\r
- }\r
- public long getLastAccessedTime() {\r
- return lastAccessedTime;\r
- }\r
- public void setLastAccessedTime(long lastAccessedTime) {\r
- this.lastAccessedTime = lastAccessedTime;\r
- }\r
- public long getThisAccessedTime() {\r
- return thisAccessedTime;\r
- }\r
- public void setThisAccessedTime(long thisAccessedTime) {\r
- this.thisAccessedTime = thisAccessedTime;\r
- }\r
-\r
-}\r
+
+/*
+ * 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.apache.catalina.ha.session;
+
+/**
+ * Title: Tomcat Session Replication for Tomcat 4.0 <BR>
+ * Description: A very simple straight forward implementation of
+ * session replication of servers in a cluster.<BR>
+ * This session replication is implemented "live". By live
+ * I mean, when a session attribute is added into a session on Node A
+ * a message is broadcasted to other messages and setAttribute is called on the replicated
+ * sessions.<BR>
+ * A full description of this implementation can be found under
+ * <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a><BR>
+ *
+ * Copyright: See apache license
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ * Description:<BR>
+ * The ReplicatedSession class is a simple extension of the StandardSession class
+ * It overrides a few methods (setAttribute, removeAttribute, expire, access) and has
+ * hooks into the InMemoryReplicationManager to broadcast and receive events from the cluster.<BR>
+ * This class inherits the readObjectData and writeObject data methods from the StandardSession
+ * and does not contain any serializable elements in addition to the inherited ones from the StandardSession
+ *
+ */
+import org.apache.catalina.Manager;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.Principal;
+
+public class ReplicatedSession extends org.apache.catalina.session.StandardSession
+implements org.apache.catalina.ha.ClusterSession{
+
+ private transient Manager mManager = null;
+ protected boolean isDirty = false;
+ private transient long lastAccessWasDistributed = System.currentTimeMillis();
+ private boolean isPrimarySession=true;
+
+
+ public ReplicatedSession(Manager manager) {
+ super(manager);
+ mManager = manager;
+ }
+
+
+ public boolean isDirty()
+ {
+ return isDirty;
+ }
+
+ public void setIsDirty(boolean dirty)
+ {
+ isDirty = dirty;
+ }
+
+
+ public void setLastAccessWasDistributed(long time) {
+ lastAccessWasDistributed = time;
+ }
+
+ public long getLastAccessWasDistributed() {
+ return lastAccessWasDistributed;
+ }
+
+
+ public void removeAttribute(String name) {
+ setIsDirty(true);
+ super.removeAttribute(name);
+ }
+
+ /**
+ * see parent description,
+ * plus we also notify other nodes in the cluster
+ */
+ public void removeAttribute(String name, boolean notify) {
+ setIsDirty(true);
+ super.removeAttribute(name,notify);
+ }
+
+
+ /**
+ * Sets an attribute and notifies the other nodes in the cluster
+ */
+ public void setAttribute(String name, Object value)
+ {
+ if ( value == null ) {
+ removeAttribute(name);
+ return;
+ }
+ if (!(value instanceof java.io.Serializable))
+ throw new java.lang.IllegalArgumentException("Value for attribute "+name+" is not serializable.");
+ setIsDirty(true);
+ super.setAttribute(name,value);
+ }
+
+ public void setMaxInactiveInterval(int interval) {
+ setIsDirty(true);
+ super.setMaxInactiveInterval(interval);
+ }
+
+
+ /**
+ * Sets the manager for this session
+ * @param mgr - the servers InMemoryReplicationManager
+ */
+ public void setManager(SimpleTcpReplicationManager mgr)
+ {
+ mManager = mgr;
+ super.setManager(mgr);
+ }
+
+
+ /**
+ * Set the authenticated Principal that is associated with this Session.
+ * This provides an <code>Authenticator</code> with a means to cache a
+ * previously authenticated Principal, and avoid potentially expensive
+ * <code>Realm.authenticate()</code> calls on every request.
+ *
+ * @param principal The new Principal, or <code>null</code> if none
+ */
+ public void setPrincipal(Principal principal) {
+ super.setPrincipal(principal);
+ setIsDirty(true);
+ }
+
+ public void expire() {
+ SimpleTcpReplicationManager mgr =(SimpleTcpReplicationManager)getManager();
+ mgr.sessionInvalidated(getIdInternal());
+ setIsDirty(true);
+ super.expire();
+ }
+
+ public void invalidate() {
+ SimpleTcpReplicationManager mgr =(SimpleTcpReplicationManager)getManager();
+ mgr.sessionInvalidated(getIdInternal());
+ setIsDirty(true);
+ super.invalidate();
+ }
+
+
+ /**
+ * Read a serialized version of the contents of this session object from
+ * the specified object input stream, without requiring that the
+ * StandardSession itself have been serialized.
+ *
+ * @param stream The object input stream to read from
+ *
+ * @exception ClassNotFoundException if an unknown class is specified
+ * @exception IOException if an input/output error occurs
+ */
+ public void readObjectData(ObjectInputStream stream)
+ throws ClassNotFoundException, IOException {
+
+ super.readObjectData(stream);
+
+ }
+
+
+ /**
+ * Write a serialized version of the contents of this session object to
+ * the specified object output stream, without requiring that the
+ * StandardSession itself have been serialized.
+ *
+ * @param stream The object output stream to write to
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ public void writeObjectData(ObjectOutputStream stream)
+ throws IOException {
+
+ super.writeObjectData(stream);
+
+ }
+
+ public void setId(String id, boolean tellNew) {
+
+ if ((this.id != null) && (manager != null))
+ manager.remove(this);
+
+ this.id = id;
+
+ if (manager != null)
+ manager.add(this);
+ if (tellNew) tellNew();
+ }
+
+
+
+
+
+
+
+
+ /**
+ * returns true if this session is the primary session, if that is the
+ * case, the manager can expire it upon timeout.
+ */
+ public boolean isPrimarySession() {
+ return isPrimarySession;
+ }
+
+ /**
+ * Sets whether this is the primary session or not.
+ * @param primarySession Flag value
+ */
+ public void setPrimarySession(boolean primarySession) {
+ this.isPrimarySession=primarySession;
+ }
+
+
+
+
+ /**
+ * Implements a log method to log through the manager
+ */
+ protected void log(String message) {
+
+ if ((mManager != null) && (mManager instanceof SimpleTcpReplicationManager)) {
+ ((SimpleTcpReplicationManager) mManager).log.debug("ReplicatedSession: " + message);
+ } else {
+ System.out.println("ReplicatedSession: " + message);
+ }
+
+ }
+
+ protected void log(String message, Throwable x) {
+
+ if ((mManager != null) && (mManager instanceof SimpleTcpReplicationManager)) {
+ ((SimpleTcpReplicationManager) mManager).log.error("ReplicatedSession: " + message,x);
+ } else {
+ System.out.println("ReplicatedSession: " + message);
+ x.printStackTrace();
+ }
+
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer("ReplicatedSession id=");
+ buf.append(getIdInternal()).append(" ref=").append(super.toString()).append("\n");
+ java.util.Enumeration e = getAttributeNames();
+ while ( e.hasMoreElements() ) {
+ String name = (String)e.nextElement();
+ Object value = getAttribute(name);
+ buf.append("\tname=").append(name).append("; value=").append(value).append("\n");
+ }
+ buf.append("\tLastAccess=").append(getLastAccessedTime()).append("\n");
+ return buf.toString();
+ }
+ public int getAccessCount() {
+ return accessCount.get();
+ }
+ public void setAccessCount(int accessCount) {
+ this.accessCount.set(accessCount);
+ }
+ public long getLastAccessedTime() {
+ return lastAccessedTime;
+ }
+ public void setLastAccessedTime(long lastAccessedTime) {
+ this.lastAccessedTime = lastAccessedTime;
+ }
+ public long getThisAccessedTime() {
+ return thisAccessedTime;
+ }
+ public void setThisAccessedTime(long thisAccessedTime) {
+ this.thisAccessedTime = thisAccessedTime;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.ha.session;\r
-\r
-\r
-import java.util.Arrays;\r
-import java.util.List;\r
-import org.apache.catalina.Realm;\r
-\r
-\r
-/**\r
- * Generic implementation of <strong>java.security.Principal</strong> that\r
- * is available for use by <code>Realm</code> implementations.\r
- * The GenericPrincipal does NOT implement serializable and I didn't want to change that implementation\r
- * hence I implemented this one instead.\r
- * @author Filip Hanik\r
- * @version $Revision: 303587 $ $Date: 2004-12-09 08:36:43 -0600 (Thu, 09 Dec 2004) $\r
- */\r
-import org.apache.catalina.realm.GenericPrincipal;\r
-import java.io.ObjectInput;\r
-import java.io.ObjectOutput;\r
-public class SerializablePrincipal implements java.io.Serializable {\r
-\r
-\r
- // ----------------------------------------------------------- Constructors\r
-\r
- public SerializablePrincipal()\r
- {\r
- super();\r
- }\r
- /**\r
- * Construct a new Principal, associated with the specified Realm, for the\r
- * specified username and password.\r
- *\r
- * @param realm The Realm that owns this Principal\r
- * @param name The username of the user represented by this Principal\r
- * @param password Credentials used to authenticate this user\r
- */\r
- public SerializablePrincipal(Realm realm, String name, String password) {\r
-\r
- this(realm, name, password, null);\r
-\r
- }\r
-\r
-\r
- /**\r
- * Construct a new Principal, associated with the specified Realm, for the\r
- * specified username and password, with the specified role names\r
- * (as Strings).\r
- *\r
- * @param realm The Realm that owns this principal\r
- * @param name The username of the user represented by this Principal\r
- * @param password Credentials used to authenticate this user\r
- * @param roles List of roles (must be Strings) possessed by this user\r
- */\r
- public SerializablePrincipal(Realm realm, String name, String password,\r
- List roles) {\r
-\r
- super();\r
- this.realm = realm;\r
- this.name = name;\r
- this.password = password;\r
- if (roles != null) {\r
- this.roles = new String[roles.size()];\r
- this.roles = (String[]) roles.toArray(this.roles);\r
- if (this.roles.length > 0)\r
- Arrays.sort(this.roles);\r
- }\r
-\r
- }\r
-\r
-\r
- // ------------------------------------------------------------- Properties\r
-\r
-\r
- /**\r
- * The username of the user represented by this Principal.\r
- */\r
- protected String name = null;\r
-\r
- public String getName() {\r
- return (this.name);\r
- }\r
-\r
-\r
- /**\r
- * The authentication credentials for the user represented by\r
- * this Principal.\r
- */\r
- protected String password = null;\r
-\r
- public String getPassword() {\r
- return (this.password);\r
- }\r
-\r
-\r
- /**\r
- * The Realm with which this Principal is associated.\r
- */\r
- protected transient Realm realm = null;\r
-\r
- public Realm getRealm() {\r
- return (this.realm);\r
- }\r
-\r
- public void setRealm(Realm realm) {\r
- this.realm = realm;\r
- }\r
-\r
-\r
-\r
-\r
- /**\r
- * The set of roles associated with this user.\r
- */\r
- protected String roles[] = new String[0];\r
-\r
- public String[] getRoles() {\r
- return (this.roles);\r
- }\r
-\r
-\r
- // --------------------------------------------------------- Public Methods\r
-\r
-\r
-\r
-\r
- /**\r
- * Return a String representation of this object, which exposes only\r
- * information that should be public.\r
- */\r
- public String toString() {\r
-\r
- StringBuffer sb = new StringBuffer("SerializablePrincipal[");\r
- sb.append(this.name);\r
- sb.append("]");\r
- return (sb.toString());\r
-\r
- }\r
-\r
- public static SerializablePrincipal createPrincipal(GenericPrincipal principal)\r
- {\r
- if ( principal==null) return null;\r
- return new SerializablePrincipal(principal.getRealm(),\r
- principal.getName(),\r
- principal.getPassword(),\r
- principal.getRoles()!=null?Arrays.asList(principal.getRoles()):null);\r
- }\r
-\r
- public GenericPrincipal getPrincipal( Realm realm )\r
- {\r
- return new GenericPrincipal(realm,name,password,getRoles()!=null?Arrays.asList(getRoles()):null);\r
- }\r
- \r
- public static GenericPrincipal readPrincipal(ObjectInput in, Realm realm) throws java.io.IOException{\r
- String name = in.readUTF();\r
- boolean hasPwd = in.readBoolean();\r
- String pwd = null;\r
- if ( hasPwd ) pwd = in.readUTF();\r
- int size = in.readInt();\r
- String[] roles = new String[size];\r
- for ( int i=0; i<size; i++ ) roles[i] = in.readUTF();\r
- return new GenericPrincipal(realm,name,pwd,Arrays.asList(roles));\r
- }\r
- \r
- public static void writePrincipal(GenericPrincipal p, ObjectOutput out) throws java.io.IOException {\r
- out.writeUTF(p.getName());\r
- out.writeBoolean(p.getPassword()!=null);\r
- if ( p.getPassword()!= null ) out.writeUTF(p.getPassword());\r
- String[] roles = p.getRoles();\r
- if ( roles == null ) roles = new String[0];\r
- out.writeInt(roles.length);\r
- for ( int i=0; i<roles.length; i++ ) out.writeUTF(roles[i]);\r
- }\r
-\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.catalina.Realm;
+
+
+/**
+ * Generic implementation of <strong>java.security.Principal</strong> that
+ * is available for use by <code>Realm</code> implementations.
+ * The GenericPrincipal does NOT implement serializable and I didn't want to change that implementation
+ * hence I implemented this one instead.
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+import org.apache.catalina.realm.GenericPrincipal;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+public class SerializablePrincipal implements java.io.Serializable {
+
+
+ // ----------------------------------------------------------- Constructors
+
+ public SerializablePrincipal()
+ {
+ super();
+ }
+ /**
+ * Construct a new Principal, associated with the specified Realm, for the
+ * specified username and password.
+ *
+ * @param realm The Realm that owns this Principal
+ * @param name The username of the user represented by this Principal
+ * @param password Credentials used to authenticate this user
+ */
+ public SerializablePrincipal(Realm realm, String name, String password) {
+
+ this(realm, name, password, null);
+
+ }
+
+
+ /**
+ * Construct a new Principal, associated with the specified Realm, for the
+ * specified username and password, with the specified role names
+ * (as Strings).
+ *
+ * @param realm The Realm that owns this principal
+ * @param name The username of the user represented by this Principal
+ * @param password Credentials used to authenticate this user
+ * @param roles List of roles (must be Strings) possessed by this user
+ */
+ public SerializablePrincipal(Realm realm, String name, String password,
+ List roles) {
+
+ super();
+ this.realm = realm;
+ this.name = name;
+ this.password = password;
+ if (roles != null) {
+ this.roles = new String[roles.size()];
+ this.roles = (String[]) roles.toArray(this.roles);
+ if (this.roles.length > 0)
+ Arrays.sort(this.roles);
+ }
+
+ }
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * The username of the user represented by this Principal.
+ */
+ protected String name = null;
+
+ public String getName() {
+ return (this.name);
+ }
+
+
+ /**
+ * The authentication credentials for the user represented by
+ * this Principal.
+ */
+ protected String password = null;
+
+ public String getPassword() {
+ return (this.password);
+ }
+
+
+ /**
+ * The Realm with which this Principal is associated.
+ */
+ protected transient Realm realm = null;
+
+ public Realm getRealm() {
+ return (this.realm);
+ }
+
+ public void setRealm(Realm realm) {
+ this.realm = realm;
+ }
+
+
+
+
+ /**
+ * The set of roles associated with this user.
+ */
+ protected String roles[] = new String[0];
+
+ public String[] getRoles() {
+ return (this.roles);
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+
+
+ /**
+ * Return a String representation of this object, which exposes only
+ * information that should be public.
+ */
+ public String toString() {
+
+ StringBuffer sb = new StringBuffer("SerializablePrincipal[");
+ sb.append(this.name);
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+ public static SerializablePrincipal createPrincipal(GenericPrincipal principal)
+ {
+ if ( principal==null) return null;
+ return new SerializablePrincipal(principal.getRealm(),
+ principal.getName(),
+ principal.getPassword(),
+ principal.getRoles()!=null?Arrays.asList(principal.getRoles()):null);
+ }
+
+ public GenericPrincipal getPrincipal( Realm realm )
+ {
+ return new GenericPrincipal(realm,name,password,getRoles()!=null?Arrays.asList(getRoles()):null);
+ }
+
+ public static GenericPrincipal readPrincipal(ObjectInput in, Realm realm) throws java.io.IOException{
+ String name = in.readUTF();
+ boolean hasPwd = in.readBoolean();
+ String pwd = null;
+ if ( hasPwd ) pwd = in.readUTF();
+ int size = in.readInt();
+ String[] roles = new String[size];
+ for ( int i=0; i<size; i++ ) roles[i] = in.readUTF();
+ return new GenericPrincipal(realm,name,pwd,Arrays.asList(roles));
+ }
+
+ public static void writePrincipal(GenericPrincipal p, ObjectOutput out) throws java.io.IOException {
+ out.writeUTF(p.getName());
+ out.writeBoolean(p.getPassword()!=null);
+ if ( p.getPassword()!= null ) out.writeUTF(p.getPassword());
+ String[] roles = p.getRoles();
+ if ( roles == null ) roles = new String[0];
+ out.writeInt(roles.length);
+ for ( int i=0; i<roles.length; i++ ) out.writeUTF(roles[i]);
+ }
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha.session;\r
-\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.ha.ClusterMessageBase;\r
-\r
-/**\r
- * Session id change cluster message\r
- * \r
- * @author Peter Rossbach\r
- * \r
- * @version $Revision: 326110 $ $Date: 2005-10-18 09:08:36 -0500 (Tue, 18 Oct 2005) $\r
- */\r
-public class SessionIDMessage extends ClusterMessageBase implements ClusterMessage {\r
-\r
- private int messageNumber;\r
-\r
- private String orignalSessionID;\r
-\r
- private String backupSessionID;\r
-\r
- private String host ;\r
- private String contextPath;\r
-\r
- public String getUniqueId() {\r
- StringBuffer result = new StringBuffer(getOrignalSessionID());\r
- result.append("#-#");\r
- result.append(getHost());\r
- result.append("#-#");\r
- result.append(getContextPath());\r
- result.append("#-#");\r
- result.append(getMessageNumber());\r
- result.append("#-#");\r
- result.append(System.currentTimeMillis());\r
- return result.toString();\r
- }\r
-\r
- /**\r
- * @return Returns the host.\r
- */\r
- public String getHost() {\r
- return host;\r
- }\r
-\r
- /**\r
- * @param host The host to set.\r
- */\r
- public void setHost(String host) {\r
- this.host = host;\r
- }\r
- \r
- /**\r
- * @return Returns the contextPath.\r
- */\r
- public String getContextPath() {\r
- return contextPath;\r
- }\r
- /**\r
- * @param contextPath The contextPath to set.\r
- */\r
- public void setContextPath(String contextPath) {\r
- this.contextPath = contextPath;\r
- }\r
- /**\r
- * @return Returns the messageNumber.\r
- */\r
- public int getMessageNumber() {\r
- return messageNumber;\r
- }\r
-\r
- /**\r
- * @param messageNumber\r
- * The messageNumber to set.\r
- */\r
- public void setMessageNumber(int messageNumber) {\r
- this.messageNumber = messageNumber;\r
- }\r
-\r
- \r
- /**\r
- * @return Returns the backupSessionID.\r
- */\r
- public String getBackupSessionID() {\r
- return backupSessionID;\r
- }\r
-\r
- /**\r
- * @param backupSessionID\r
- * The backupSessionID to set.\r
- */\r
- public void setBackupSessionID(String backupSessionID) {\r
- this.backupSessionID = backupSessionID;\r
- }\r
-\r
- /**\r
- * @return Returns the orignalSessionID.\r
- */\r
- public String getOrignalSessionID() {\r
- return orignalSessionID;\r
- }\r
-\r
- /**\r
- * @param orignalSessionID\r
- * The orignalSessionID to set.\r
- */\r
- public void setOrignalSessionID(String orignalSessionID) {\r
- this.orignalSessionID = orignalSessionID;\r
- }\r
-\r
- \r
-\r
-\r
-}\r
-\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.ha.ClusterMessageBase;
+
+/**
+ * Session id change cluster message
+ *
+ * @author Peter Rossbach
+ *
+ * @version $Revision$ $Date$
+ */
+public class SessionIDMessage extends ClusterMessageBase implements ClusterMessage {
+
+ private int messageNumber;
+
+ private String orignalSessionID;
+
+ private String backupSessionID;
+
+ private String host ;
+ private String contextPath;
+
+ public String getUniqueId() {
+ StringBuffer result = new StringBuffer(getOrignalSessionID());
+ result.append("#-#");
+ result.append(getHost());
+ result.append("#-#");
+ result.append(getContextPath());
+ result.append("#-#");
+ result.append(getMessageNumber());
+ result.append("#-#");
+ result.append(System.currentTimeMillis());
+ return result.toString();
+ }
+
+ /**
+ * @return Returns the host.
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * @param host The host to set.
+ */
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ /**
+ * @return Returns the contextPath.
+ */
+ public String getContextPath() {
+ return contextPath;
+ }
+ /**
+ * @param contextPath The contextPath to set.
+ */
+ public void setContextPath(String contextPath) {
+ this.contextPath = contextPath;
+ }
+ /**
+ * @return Returns the messageNumber.
+ */
+ public int getMessageNumber() {
+ return messageNumber;
+ }
+
+ /**
+ * @param messageNumber
+ * The messageNumber to set.
+ */
+ public void setMessageNumber(int messageNumber) {
+ this.messageNumber = messageNumber;
+ }
+
+
+ /**
+ * @return Returns the backupSessionID.
+ */
+ public String getBackupSessionID() {
+ return backupSessionID;
+ }
+
+ /**
+ * @param backupSessionID
+ * The backupSessionID to set.
+ */
+ public void setBackupSessionID(String backupSessionID) {
+ this.backupSessionID = backupSessionID;
+ }
+
+ /**
+ * @return Returns the orignalSessionID.
+ */
+ public String getOrignalSessionID() {
+ return orignalSessionID;
+ }
+
+ /**
+ * @param orignalSessionID
+ * The orignalSessionID to set.
+ */
+ public void setOrignalSessionID(String orignalSessionID) {
+ this.orignalSessionID = orignalSessionID;
+ }
+
+
+
+
+}
+
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha.session;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-\r
-/**\r
- *\r
- * <B>Class Description:</B><BR>\r
- * The SessionMessage class is a class that is used when a session has been\r
- * created, modified, expired in a Tomcat cluster node.<BR>\r
- *\r
- * The following events are currently available:\r
- * <ul>\r
- * <li><pre>public static final int EVT_SESSION_CREATED</pre><li>\r
- * <li><pre>public static final int EVT_SESSION_ACCESSED</pre><li>\r
- * <li><pre>public static final int EVT_ATTRIBUTE_ADDED</pre><li>\r
- * <li><pre>public static final int EVT_ATTRIBUTE_REMOVED</pre><li>\r
- * <li><pre>public static final int EVT_SESSION_EXPIRED_WONOTIFY</pre><li>\r
- * <li><pre>public static final int EVT_SESSION_EXPIRED_WNOTIFY</pre><li>\r
- * <li><pre>public static final int EVT_GET_ALL_SESSIONS</pre><li>\r
- * <li><pre>public static final int EVT_SET_USER_PRINCIPAL</pre><li>\r
- * <li><pre>public static final int EVT_SET_SESSION_NOTE</pre><li>\r
- * <li><pre>public static final int EVT_REMOVE_SESSION_NOTE</pre><li>\r
- * </ul>\r
- *\r
- */\r
-\r
-public interface SessionMessage extends ClusterMessage, java.io.Serializable\r
-{\r
-\r
- /**\r
- * Event type used when a session has been created on a node\r
- */\r
- public static final int EVT_SESSION_CREATED = 1;\r
- /**\r
- * Event type used when a session has expired\r
- */\r
- public static final int EVT_SESSION_EXPIRED = 2;\r
-\r
- /**\r
- * Event type used when a session has been accessed (ie, last access time\r
- * has been updated. This is used so that the replicated sessions will not expire\r
- * on the network\r
- */\r
- public static final int EVT_SESSION_ACCESSED = 3;\r
- /**\r
- * Event type used when a server comes online for the first time.\r
- * The first thing the newly started server wants to do is to grab the\r
- * all the sessions from one of the nodes and keep the same state in there\r
- */\r
- public static final int EVT_GET_ALL_SESSIONS = 4;\r
- /**\r
- * Event type used when an attribute has been added to a session,\r
- * the attribute will be sent to all the other nodes in the cluster\r
- */\r
- public static final int EVT_SESSION_DELTA = 13;\r
-\r
- /**\r
- * When a session state is transferred, this is the event.\r
- */\r
- public static final int EVT_ALL_SESSION_DATA = 12;\r
- \r
- /**\r
- * When a session state is complete transferred, this is the event.\r
- */\r
- public static final int EVT_ALL_SESSION_TRANSFERCOMPLETE = 14;\r
- \r
-\r
- \r
- public String getContextName();\r
- \r
- public String getEventTypeString();\r
- \r
- /**\r
- * returns the event type\r
- * @return one of the event types EVT_XXXX\r
- */\r
- public int getEventType(); \r
- /**\r
- * @return the serialized data for the session\r
- */\r
- public byte[] getSession();\r
- /**\r
- * @return the session ID for the session\r
- */\r
- public String getSessionID();\r
- \r
-\r
-\r
-}//SessionMessage\r
+/*
+ * 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.apache.catalina.ha.session;
+import org.apache.catalina.ha.ClusterMessage;
+
+/**
+ *
+ * <B>Class Description:</B><BR>
+ * The SessionMessage class is a class that is used when a session has been
+ * created, modified, expired in a Tomcat cluster node.<BR>
+ *
+ * The following events are currently available:
+ * <ul>
+ * <li><pre>public static final int EVT_SESSION_CREATED</pre><li>
+ * <li><pre>public static final int EVT_SESSION_ACCESSED</pre><li>
+ * <li><pre>public static final int EVT_ATTRIBUTE_ADDED</pre><li>
+ * <li><pre>public static final int EVT_ATTRIBUTE_REMOVED</pre><li>
+ * <li><pre>public static final int EVT_SESSION_EXPIRED_WONOTIFY</pre><li>
+ * <li><pre>public static final int EVT_SESSION_EXPIRED_WNOTIFY</pre><li>
+ * <li><pre>public static final int EVT_GET_ALL_SESSIONS</pre><li>
+ * <li><pre>public static final int EVT_SET_USER_PRINCIPAL</pre><li>
+ * <li><pre>public static final int EVT_SET_SESSION_NOTE</pre><li>
+ * <li><pre>public static final int EVT_REMOVE_SESSION_NOTE</pre><li>
+ * </ul>
+ *
+ */
+
+public interface SessionMessage extends ClusterMessage, java.io.Serializable
+{
+
+ /**
+ * Event type used when a session has been created on a node
+ */
+ public static final int EVT_SESSION_CREATED = 1;
+ /**
+ * Event type used when a session has expired
+ */
+ public static final int EVT_SESSION_EXPIRED = 2;
+
+ /**
+ * Event type used when a session has been accessed (ie, last access time
+ * has been updated. This is used so that the replicated sessions will not expire
+ * on the network
+ */
+ public static final int EVT_SESSION_ACCESSED = 3;
+ /**
+ * Event type used when a server comes online for the first time.
+ * The first thing the newly started server wants to do is to grab the
+ * all the sessions from one of the nodes and keep the same state in there
+ */
+ public static final int EVT_GET_ALL_SESSIONS = 4;
+ /**
+ * Event type used when an attribute has been added to a session,
+ * the attribute will be sent to all the other nodes in the cluster
+ */
+ public static final int EVT_SESSION_DELTA = 13;
+
+ /**
+ * When a session state is transferred, this is the event.
+ */
+ public static final int EVT_ALL_SESSION_DATA = 12;
+
+ /**
+ * When a session state is complete transferred, this is the event.
+ */
+ public static final int EVT_ALL_SESSION_TRANSFERCOMPLETE = 14;
+
+
+
+ public String getContextName();
+
+ public String getEventTypeString();
+
+ /**
+ * returns the event type
+ * @return one of the event types EVT_XXXX
+ */
+ public int getEventType();
+ /**
+ * @return the serialized data for the session
+ */
+ public byte[] getSession();
+ /**
+ * @return the session ID for the session
+ */
+ public String getSessionID();
+
+
+
+}//SessionMessage
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha.session;\r
-\r
-\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.ha.ClusterMessageBase;\r
-\r
-/**\r
- * Session cluster message\r
- * \r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * \r
- * @version $Revision: 326110 $ $Date: 2005-10-18 09:08:36 -0500 (Tue, 18 Oct 2005) $\r
- */\r
-public class SessionMessageImpl extends ClusterMessageBase implements SessionMessage, java.io.Serializable {\r
- \r
- public SessionMessageImpl() {\r
- }\r
- \r
- \r
- /*\r
-\r
- * Private serializable variables to keep the messages state\r
- */\r
- private int mEvtType = -1;\r
- private byte[] mSession;\r
- private String mSessionID;\r
-\r
- private String mContextName;\r
- private long serializationTimestamp;\r
- private boolean timestampSet = false ;\r
- private String uniqueId;\r
-\r
-\r
- private SessionMessageImpl( String contextName,\r
- int eventtype,\r
- byte[] session,\r
- String sessionID)\r
- {\r
- mEvtType = eventtype;\r
- mSession = session;\r
- mSessionID = sessionID;\r
- mContextName = contextName;\r
- uniqueId = sessionID;\r
- }\r
-\r
- /**\r
- * Creates a session message. Depending on what event type you want this\r
- * message to represent, you populate the different parameters in the constructor<BR>\r
- * The following rules apply dependent on what event type argument you use:<BR>\r
- * <B>EVT_SESSION_CREATED</B><BR>\r
- * The parameters: session, sessionID must be set.<BR>\r
- * <B>EVT_SESSION_EXPIRED</B><BR>\r
- * The parameters: sessionID must be set.<BR>\r
- * <B>EVT_SESSION_ACCESSED</B><BR>\r
- * The parameters: sessionID must be set.<BR>\r
- * <B>EVT_SESSION_EXPIRED_XXXX</B><BR>\r
- * The parameters: sessionID must be set.<BR>\r
- * <B>EVT_SESSION_DELTA</B><BR>\r
- * Send attribute delta (add,update,remove attribute or principal, ...).<BR>\r
- * <B>EVT_ALL_SESSION_DATA</B><BR>\r
- * Send complete serializes session list<BR>\r
- * <B>EVT_ALL_SESSION_TRANSFERCOMPLETE</B><BR>\r
- * send that all session state information are transfered\r
- * after GET_ALL_SESSION received from this sender.<BR>\r
- * @param contextName - the name of the context (application\r
- * @param eventtype - one of the 8 event type defined in this class\r
- * @param session - the serialized byte array of the session itself\r
- * @param sessionID - the id that identifies this session\r
- * @param uniqueID - the id that identifies this message\r
- */\r
- public SessionMessageImpl( String contextName,\r
- int eventtype,\r
- byte[] session,\r
- String sessionID,\r
- String uniqueID)\r
- {\r
- this(contextName,eventtype,session,sessionID);\r
- uniqueId = uniqueID;\r
- }\r
-\r
- /**\r
- * returns the event type\r
- * @return one of the event types EVT_XXXX\r
- */\r
- public int getEventType() { return mEvtType; }\r
-\r
- /**\r
- * @return the serialized data for the session\r
- */\r
- public byte[] getSession() { return mSession;}\r
-\r
- /**\r
- * @return the session ID for the session\r
- */\r
- public String getSessionID(){ return mSessionID; }\r
- \r
- /**\r
- * set message send time but only the first setting works (one shot)\r
- */\r
- public void setTimestamp(long time) {\r
- synchronized(this) {\r
- if(!timestampSet) {\r
- serializationTimestamp=time;\r
- timestampSet = true ;\r
- }\r
- }\r
- }\r
- \r
- public long getTimestamp() { return serializationTimestamp;}\r
- \r
- /**\r
- * clear text event type name (for logging purpose only) \r
- * @return the event type in a string representating, useful for debugging\r
- */\r
- public String getEventTypeString()\r
- {\r
- switch (mEvtType)\r
- {\r
- case EVT_SESSION_CREATED : return "SESSION-MODIFIED";\r
- case EVT_SESSION_EXPIRED : return "SESSION-EXPIRED";\r
- case EVT_SESSION_ACCESSED : return "SESSION-ACCESSED";\r
- case EVT_GET_ALL_SESSIONS : return "SESSION-GET-ALL";\r
- case EVT_SESSION_DELTA : return "SESSION-DELTA";\r
- case EVT_ALL_SESSION_DATA : return "ALL-SESSION-DATA";\r
- case EVT_ALL_SESSION_TRANSFERCOMPLETE : return "SESSION-STATE-TRANSFERED";\r
- default : return "UNKNOWN-EVENT-TYPE";\r
- }\r
- }\r
-\r
- public String getContextName() {\r
- return mContextName;\r
- }\r
- public String getUniqueId() {\r
- return uniqueId;\r
- }\r
- public void setUniqueId(String uniqueId) {\r
- this.uniqueId = uniqueId;\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.ha.ClusterMessageBase;
+
+/**
+ * Session cluster message
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ *
+ * @version $Revision$ $Date$
+ */
+public class SessionMessageImpl extends ClusterMessageBase implements SessionMessage, java.io.Serializable {
+
+ public SessionMessageImpl() {
+ }
+
+
+ /*
+
+ * Private serializable variables to keep the messages state
+ */
+ private int mEvtType = -1;
+ private byte[] mSession;
+ private String mSessionID;
+
+ private String mContextName;
+ private long serializationTimestamp;
+ private boolean timestampSet = false ;
+ private String uniqueId;
+
+
+ private SessionMessageImpl( String contextName,
+ int eventtype,
+ byte[] session,
+ String sessionID)
+ {
+ mEvtType = eventtype;
+ mSession = session;
+ mSessionID = sessionID;
+ mContextName = contextName;
+ uniqueId = sessionID;
+ }
+
+ /**
+ * Creates a session message. Depending on what event type you want this
+ * message to represent, you populate the different parameters in the constructor<BR>
+ * The following rules apply dependent on what event type argument you use:<BR>
+ * <B>EVT_SESSION_CREATED</B><BR>
+ * The parameters: session, sessionID must be set.<BR>
+ * <B>EVT_SESSION_EXPIRED</B><BR>
+ * The parameters: sessionID must be set.<BR>
+ * <B>EVT_SESSION_ACCESSED</B><BR>
+ * The parameters: sessionID must be set.<BR>
+ * <B>EVT_SESSION_EXPIRED_XXXX</B><BR>
+ * The parameters: sessionID must be set.<BR>
+ * <B>EVT_SESSION_DELTA</B><BR>
+ * Send attribute delta (add,update,remove attribute or principal, ...).<BR>
+ * <B>EVT_ALL_SESSION_DATA</B><BR>
+ * Send complete serializes session list<BR>
+ * <B>EVT_ALL_SESSION_TRANSFERCOMPLETE</B><BR>
+ * send that all session state information are transfered
+ * after GET_ALL_SESSION received from this sender.<BR>
+ * @param contextName - the name of the context (application
+ * @param eventtype - one of the 8 event type defined in this class
+ * @param session - the serialized byte array of the session itself
+ * @param sessionID - the id that identifies this session
+ * @param uniqueID - the id that identifies this message
+ */
+ public SessionMessageImpl( String contextName,
+ int eventtype,
+ byte[] session,
+ String sessionID,
+ String uniqueID)
+ {
+ this(contextName,eventtype,session,sessionID);
+ uniqueId = uniqueID;
+ }
+
+ /**
+ * returns the event type
+ * @return one of the event types EVT_XXXX
+ */
+ public int getEventType() { return mEvtType; }
+
+ /**
+ * @return the serialized data for the session
+ */
+ public byte[] getSession() { return mSession;}
+
+ /**
+ * @return the session ID for the session
+ */
+ public String getSessionID(){ return mSessionID; }
+
+ /**
+ * set message send time but only the first setting works (one shot)
+ */
+ public void setTimestamp(long time) {
+ synchronized(this) {
+ if(!timestampSet) {
+ serializationTimestamp=time;
+ timestampSet = true ;
+ }
+ }
+ }
+
+ public long getTimestamp() { return serializationTimestamp;}
+
+ /**
+ * clear text event type name (for logging purpose only)
+ * @return the event type in a string representating, useful for debugging
+ */
+ public String getEventTypeString()
+ {
+ switch (mEvtType)
+ {
+ case EVT_SESSION_CREATED : return "SESSION-MODIFIED";
+ case EVT_SESSION_EXPIRED : return "SESSION-EXPIRED";
+ case EVT_SESSION_ACCESSED : return "SESSION-ACCESSED";
+ case EVT_GET_ALL_SESSIONS : return "SESSION-GET-ALL";
+ case EVT_SESSION_DELTA : return "SESSION-DELTA";
+ case EVT_ALL_SESSION_DATA : return "ALL-SESSION-DATA";
+ case EVT_ALL_SESSION_TRANSFERCOMPLETE : return "SESSION-STATE-TRANSFERED";
+ default : return "UNKNOWN-EVENT-TYPE";
+ }
+ }
+
+ public String getContextName() {
+ return mContextName;
+ }
+ public String getUniqueId() {
+ return uniqueId;
+ }
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.ha.session;\r
-\r
-import java.io.IOException;\r
-\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.Session;\r
-import org.apache.catalina.ha.CatalinaCluster;\r
-import org.apache.catalina.ha.ClusterManager;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.realm.GenericPrincipal;\r
-import org.apache.catalina.session.StandardManager;\r
-import org.apache.catalina.tribes.io.ReplicationStream;\r
-import java.io.ByteArrayInputStream;\r
-import org.apache.catalina.Loader;\r
-\r
-/**\r
- * Title: Tomcat Session Replication for Tomcat 4.0 <BR>\r
- * Description: A very simple straight forward implementation of\r
- * session replication of servers in a cluster.<BR>\r
- * This session replication is implemented "live". By live\r
- * I mean, when a session attribute is added into a session on Node A\r
- * a message is broadcasted to other messages and setAttribute is called on the\r
- * replicated sessions.<BR>\r
- * A full description of this implementation can be found under\r
- * <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a><BR>\r
- *\r
- * Copyright: See apache license\r
- * Company: www.filip.net\r
- * @author <a href="mailto:mail@filip.net">Filip Hanik</a>\r
- * @author Bela Ban (modifications for synchronous replication)\r
- * @version 1.0 for TC 4.0\r
- * Description: The InMemoryReplicationManager is a session manager that replicated\r
- * session information in memory. \r
- * <BR><BR>\r
- * The InMemoryReplicationManager extends the StandardManager hence it allows for us\r
- * to inherit all the basic session management features like expiration, session listeners etc\r
- * <BR><BR>\r
- * To communicate with other nodes in the cluster, the InMemoryReplicationManager sends out 7 different type of multicast messages\r
- * all defined in the SessionMessage class.<BR>\r
- * When a session is replicated (not an attribute added/removed) the session is serialized into\r
- * a byte array using the StandardSession.readObjectData, StandardSession.writeObjectData methods.\r
- */\r
-public class SimpleTcpReplicationManager extends StandardManager implements ClusterManager\r
-{\r
- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( SimpleTcpReplicationManager.class );\r
-\r
- //the channel configuration\r
- protected String mChannelConfig = null;\r
-\r
- //the group name\r
- protected String mGroupName = "TomcatReplication";\r
-\r
- //somehow start() gets called more than once\r
- protected boolean mChannelStarted = false;\r
-\r
- //log to screen\r
- protected boolean mPrintToScreen = true;\r
-\r
- protected boolean defaultMode = false;\r
-\r
- protected boolean mManagerRunning = false;\r
-\r
- /** Use synchronous rather than asynchronous replication. Every session modification (creation, change, removal etc)\r
- * will be sent to all members. The call will then wait for max milliseconds, or forever (if timeout is 0) for\r
- * all responses.\r
- */\r
- protected boolean synchronousReplication=true;\r
-\r
- /** Set to true if we don't want the sessions to expire on shutdown */\r
- protected boolean mExpireSessionsOnShutdown = true;\r
-\r
- protected boolean useDirtyFlag = false;\r
-\r
- protected String name;\r
-\r
- protected boolean distributable = true;\r
-\r
- protected CatalinaCluster cluster;\r
-\r
- protected java.util.HashMap invalidatedSessions = new java.util.HashMap();\r
-\r
- /**\r
- * Flag to keep track if the state has been transferred or not\r
- * Assumes false.\r
- */\r
- protected boolean stateTransferred = false;\r
- private boolean notifyListenersOnReplication;\r
- private boolean sendClusterDomainOnly = true ;\r
-\r
- /**\r
- * Constructor, just calls super()\r
- *\r
- */\r
- public SimpleTcpReplicationManager()\r
- {\r
- super();\r
- }\r
-\r
- public boolean doDomainReplication() {\r
- return sendClusterDomainOnly;\r
- }\r
- \r
- /**\r
- * @param sendClusterDomainOnly The sendClusterDomainOnly to set.\r
- */\r
- public void setDomainReplication(boolean sendClusterDomainOnly) {\r
- this.sendClusterDomainOnly = sendClusterDomainOnly;\r
- }\r
- \r
- /**\r
- * @return Returns the defaultMode.\r
- */\r
- public boolean isDefaultMode() {\r
- return defaultMode;\r
- }\r
- /**\r
- * @param defaultMode The defaultMode to set.\r
- */\r
- public void setDefaultMode(boolean defaultMode) {\r
- this.defaultMode = defaultMode;\r
- }\r
- \r
- public boolean isManagerRunning()\r
- {\r
- return mManagerRunning;\r
- }\r
-\r
- public void setUseDirtyFlag(boolean usedirtyflag)\r
- {\r
- this.useDirtyFlag = usedirtyflag;\r
- }\r
-\r
- public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown)\r
- {\r
- mExpireSessionsOnShutdown = expireSessionsOnShutdown;\r
- }\r
-\r
- public void setCluster(CatalinaCluster cluster) {\r
- if(log.isDebugEnabled())\r
- log.debug("Cluster associated with SimpleTcpReplicationManager");\r
- this.cluster = cluster;\r
- }\r
-\r
- public boolean getExpireSessionsOnShutdown()\r
- {\r
- return mExpireSessionsOnShutdown;\r
- }\r
-\r
- public void setPrintToScreen(boolean printtoscreen)\r
- {\r
- if(log.isDebugEnabled())\r
- log.debug("Setting screen debug to:"+printtoscreen);\r
- mPrintToScreen = printtoscreen;\r
- }\r
-\r
- public void setSynchronousReplication(boolean flag)\r
- {\r
- synchronousReplication=flag;\r
- }\r
-\r
- /**\r
- * Override persistence since they don't go hand in hand with replication for now.\r
- */\r
- public void unload() throws IOException {\r
- if ( !getDistributable() ) {\r
- super.unload();\r
- }\r
- }\r
-\r
- /**\r
- * Creates a HTTP session.\r
- * Most of the code in here is copied from the StandardManager.\r
- * This is not pretty, yeah I know, but it was necessary since the\r
- * StandardManager had hard coded the session instantiation to the a\r
- * StandardSession, when we actually want to instantiate a ReplicatedSession<BR>\r
- * If the call comes from the Tomcat servlet engine, a SessionMessage goes out to the other\r
- * nodes in the cluster that this session has been created.\r
- * @param notify - if set to true the other nodes in the cluster will be notified.\r
- * This flag is needed so that we can create a session before we deserialize\r
- * a replicated one\r
- *\r
- * @see ReplicatedSession\r
- */\r
- protected Session createSession(String sessionId, boolean notify, boolean setId)\r
- {\r
-\r
- //inherited from the basic manager\r
- if ((getMaxActiveSessions() >= 0) &&\r
- (sessions.size() >= getMaxActiveSessions()))\r
- throw new IllegalStateException(sm.getString("standardManager.createSession.ise"));\r
-\r
-\r
- Session session = new ReplicatedSession(this);\r
-\r
- // Initialize the properties of the new session and return it\r
- session.setNew(true);\r
- session.setValid(true);\r
- session.setCreationTime(System.currentTimeMillis());\r
- session.setMaxInactiveInterval(this.maxInactiveInterval);\r
- if(sessionId == null)\r
- sessionId = generateSessionId();\r
- if ( setId ) session.setId(sessionId);\r
- if ( notify && (cluster!=null) ) {\r
- ((ReplicatedSession)session).setIsDirty(true);\r
- }\r
- return (session);\r
- }//createSession\r
-\r
- //=========================================================================\r
- // OVERRIDE THESE METHODS TO IMPLEMENT THE REPLICATION\r
- //=========================================================================\r
-\r
- /**\r
- * Construct and return a new session object, based on the default\r
- * settings specified by this Manager's properties. The session\r
- * id will be assigned by this method, and available via the getId()\r
- * method of the returned session. If a new session cannot be created\r
- * for any reason, return <code>null</code>.\r
- *\r
- * @exception IllegalStateException if a new session cannot be\r
- * instantiated for any reason\r
- */\r
- public Session createSession(String sessionId)\r
- {\r
- //create a session and notify the other nodes in the cluster\r
- Session session = createSession(sessionId,getDistributable(),true);\r
- add(session);\r
- return session;\r
- }\r
-\r
- public void sessionInvalidated(String sessionId) {\r
- synchronized ( invalidatedSessions ) {\r
- invalidatedSessions.put(sessionId, sessionId);\r
- }\r
- }\r
-\r
- public String[] getInvalidatedSessions() {\r
- synchronized ( invalidatedSessions ) {\r
- String[] result = new String[invalidatedSessions.size()];\r
- invalidatedSessions.values().toArray(result);\r
- return result;\r
- }\r
-\r
- }\r
-\r
- public ClusterMessage requestCompleted(String sessionId)\r
- {\r
- if ( !getDistributable() ) {\r
- log.warn("Received requestCompleted message, although this context["+\r
- getName()+"] is not distributable. Ignoring message");\r
- return null;\r
- }\r
- try\r
- {\r
- if ( invalidatedSessions.get(sessionId) != null ) {\r
- synchronized ( invalidatedSessions ) {\r
- invalidatedSessions.remove(sessionId);\r
- SessionMessage msg = new SessionMessageImpl(name,\r
- SessionMessage.EVT_SESSION_EXPIRED,\r
- null,\r
- sessionId,\r
- sessionId);\r
- return msg;\r
- }\r
- } else {\r
- ReplicatedSession session = (ReplicatedSession) findSession(\r
- sessionId);\r
- if (session != null) {\r
- //return immediately if the session is not dirty\r
- if (useDirtyFlag && (!session.isDirty())) {\r
- //but before we return doing nothing,\r
- //see if we should send\r
- //an updated last access message so that\r
- //sessions across cluster dont expire\r
- long interval = session.getMaxInactiveInterval();\r
- long lastaccdist = System.currentTimeMillis() -\r
- session.getLastAccessWasDistributed();\r
- if ( ((interval*1000) / lastaccdist)< 3 ) {\r
- SessionMessage accmsg = new SessionMessageImpl(name,\r
- SessionMessage.EVT_SESSION_ACCESSED,\r
- null,\r
- sessionId,\r
- sessionId);\r
- session.setLastAccessWasDistributed(System.currentTimeMillis());\r
- return accmsg;\r
- }\r
- return null;\r
- }\r
-\r
- session.setIsDirty(false);\r
- if (log.isDebugEnabled()) {\r
- try {\r
- log.debug("Sending session to cluster=" + session);\r
- }\r
- catch (Exception ignore) {}\r
- }\r
- SessionMessage msg = new SessionMessageImpl(name,\r
- SessionMessage.EVT_SESSION_CREATED,\r
- writeSession(session),\r
- session.getIdInternal(),\r
- session.getIdInternal());\r
- return msg;\r
- } //end if\r
- }//end if\r
- }\r
- catch (Exception x )\r
- {\r
- log.error("Unable to replicate session",x);\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * Serialize a session into a byte array<BR>\r
- * This method simple calls the writeObjectData method on the session\r
- * and returns the byte data from that call\r
- * @param session - the session to be serialized\r
- * @return a byte array containing the session data, null if the serialization failed\r
- */\r
- protected byte[] writeSession( Session session )\r
- {\r
- try\r
- {\r
- java.io.ByteArrayOutputStream session_data = new java.io.ByteArrayOutputStream();\r
- java.io.ObjectOutputStream session_out = new java.io.ObjectOutputStream(session_data);\r
- session_out.flush();\r
- boolean hasPrincipal = session.getPrincipal() != null;\r
- session_out.writeBoolean(hasPrincipal);\r
- if ( hasPrincipal )\r
- {\r
- session_out.writeObject(SerializablePrincipal.createPrincipal((GenericPrincipal)session.getPrincipal()));\r
- }//end if\r
- ((ReplicatedSession)session).writeObjectData(session_out);\r
- return session_data.toByteArray();\r
-\r
- }\r
- catch ( Exception x )\r
- {\r
- log.error("Failed to serialize the session!",x);\r
- }\r
- return null;\r
- }\r
- \r
- /**\r
- * Open Stream and use correct ClassLoader (Container) Switch\r
- * ThreadClassLoader\r
- * \r
- * @param data\r
- * @return The object input stream\r
- * @throws IOException\r
- */\r
- public ReplicationStream getReplicationStream(byte[] data) throws IOException {\r
- return getReplicationStream(data,0,data.length);\r
- }\r
- \r
- public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException {\r
- ByteArrayInputStream fis =null;\r
- ReplicationStream ois = null;\r
- Loader loader = null;\r
- ClassLoader classLoader = null;\r
- //fix to be able to run the DeltaManager\r
- //stand alone without a container.\r
- //use the Threads context class loader\r
- if (container != null)\r
- loader = container.getLoader();\r
- if (loader != null)\r
- classLoader = loader.getClassLoader();\r
- else\r
- classLoader = Thread.currentThread().getContextClassLoader();\r
- //end fix\r
- fis = new ByteArrayInputStream(data, offset, length);\r
- if ( classLoader == Thread.currentThread().getContextClassLoader() ) {\r
- ois = new ReplicationStream(fis, new ClassLoader[] {classLoader});\r
- } else {\r
- ois = new ReplicationStream(fis, new ClassLoader[] {classLoader,Thread.currentThread().getContextClassLoader()});\r
- }\r
- return ois;\r
- } \r
-\r
-\r
- \r
-\r
- /**\r
- * Reinstantiates a serialized session from the data passed in.\r
- * This will first call createSession() so that we get a fresh instance with all\r
- * the managers set and all the transient fields validated.\r
- * Then it calls Session.readObjectData(byte[]) to deserialize the object\r
- * @param data - a byte array containing session data\r
- * @return a valid Session object, null if an error occurs\r
- *\r
- */\r
- protected Session readSession( byte[] data, String sessionId )\r
- {\r
- try\r
- {\r
- ReplicationStream session_in = getReplicationStream(data);\r
-\r
- Session session = sessionId!=null?this.findSession(sessionId):null;\r
- boolean isNew = (session==null);\r
- //clear the old values from the existing session\r
- if ( session!=null ) {\r
- ReplicatedSession rs = (ReplicatedSession)session;\r
- rs.expire(false); //cleans up the previous values, since we are not doing removes\r
- session = null;\r
- }//end if\r
-\r
- if (session==null) {\r
- session = createSession(null,false, false);\r
- sessions.remove(session.getIdInternal());\r
- }\r
- \r
- \r
- boolean hasPrincipal = session_in.readBoolean();\r
- SerializablePrincipal p = null;\r
- if ( hasPrincipal )\r
- p = (SerializablePrincipal)session_in.readObject();\r
- ((ReplicatedSession)session).readObjectData(session_in);\r
- if ( hasPrincipal )\r
- session.setPrincipal(p.getPrincipal(getContainer().getRealm()));\r
- ((ReplicatedSession)session).setId(sessionId,isNew);\r
- ReplicatedSession rsession = (ReplicatedSession)session; \r
- rsession.setAccessCount(1);\r
- session.setManager(this);\r
- session.setValid(true);\r
- rsession.setLastAccessedTime(System.currentTimeMillis());\r
- rsession.setThisAccessedTime(System.currentTimeMillis());\r
- ((ReplicatedSession)session).setAccessCount(0);\r
- session.setNew(false);\r
- if(log.isTraceEnabled())\r
- log.trace("Session loaded id="+sessionId +\r
- " actualId="+session.getId()+ \r
- " exists="+this.sessions.containsKey(sessionId)+\r
- " valid="+rsession.isValid());\r
- return session;\r
-\r
- }\r
- catch ( Exception x )\r
- {\r
- log.error("Failed to deserialize the session!",x);\r
- }\r
- return null;\r
- }\r
-\r
- public String getName() {\r
- return this.name;\r
- }\r
- /**\r
- * Prepare for the beginning of active use of the public methods of this\r
- * component. This method should be called after <code>configure()</code>,\r
- * and before any of the public methods of the component are utilized.<BR>\r
- * Starts the cluster communication channel, this will connect with the other nodes\r
- * in the cluster, and request the current session state to be transferred to this node.\r
- * @exception IllegalStateException if this component has already been\r
- * started\r
- * @exception LifecycleException if this component detects a fatal error\r
- * that prevents this component from being used\r
- */\r
- public void start() throws LifecycleException {\r
- mManagerRunning = true;\r
- super.start();\r
- try {\r
- //the channel is already running\r
- if ( mChannelStarted ) return;\r
- if(log.isInfoEnabled())\r
- log.info("Starting clustering manager...:"+getName());\r
- if ( cluster == null ) {\r
- log.error("Starting... no cluster associated with this context:"+getName());\r
- return;\r
- }\r
- cluster.registerManager(this);\r
-\r
- if (cluster.getMembers().length > 0) {\r
- Member mbr = cluster.getMembers()[0];\r
- SessionMessage msg =\r
- new SessionMessageImpl(this.getName(),\r
- SessionMessage.EVT_GET_ALL_SESSIONS,\r
- null,\r
- "GET-ALL",\r
- "GET-ALL-"+this.getName());\r
- cluster.send(msg, mbr);\r
- if(log.isWarnEnabled())\r
- log.warn("Manager["+getName()+"], requesting session state from "+mbr+\r
- ". This operation will timeout if no session state has been received within "+\r
- "60 seconds");\r
- long reqStart = System.currentTimeMillis();\r
- long reqNow = 0;\r
- boolean isTimeout=false;\r
- do {\r
- try {\r
- Thread.sleep(100);\r
- }catch ( Exception sleep) {}\r
- reqNow = System.currentTimeMillis();\r
- isTimeout=((reqNow-reqStart)>(1000*60));\r
- } while ( (!isStateTransferred()) && (!isTimeout));\r
- if ( isTimeout || (!isStateTransferred()) ) {\r
- log.error("Manager["+getName()+"], No session state received, timing out.");\r
- }else {\r
- if(log.isInfoEnabled())\r
- log.info("Manager["+getName()+"], session state received in "+(reqNow-reqStart)+" ms.");\r
- }\r
- } else {\r
- if(log.isInfoEnabled())\r
- log.info("Manager["+getName()+"], skipping state transfer. No members active in cluster group.");\r
- }//end if\r
- mChannelStarted = true;\r
- } catch ( Exception x ) {\r
- log.error("Unable to start SimpleTcpReplicationManager",x);\r
- }\r
- }\r
-\r
- /**\r
- * Gracefully terminate the active use of the public methods of this\r
- * component. This method should be the last one called on a given\r
- * instance of this component.<BR>\r
- * This will disconnect the cluster communication channel and stop the listener thread.\r
- * @exception IllegalStateException if this component has not been started\r
- * @exception LifecycleException if this component detects a fatal error\r
- * that needs to be reported\r
- */\r
- public void stop() throws LifecycleException\r
- {\r
- mManagerRunning = false;\r
- mChannelStarted = false;\r
- super.stop();\r
- try\r
- {\r
- this.sessions.clear();\r
- cluster.removeManager(this);\r
- }\r
- catch ( Exception x )\r
- {\r
- log.error("Unable to stop SimpleTcpReplicationManager",x);\r
- }\r
- }\r
-\r
- public void setDistributable(boolean dist) {\r
- this.distributable = dist;\r
- }\r
-\r
- public boolean getDistributable() {\r
- return distributable;\r
- }\r
-\r
- /**\r
- * This method is called by the received thread when a SessionMessage has\r
- * been received from one of the other nodes in the cluster.\r
- * @param msg - the message received\r
- * @param sender - the sender of the message, this is used if we receive a\r
- * EVT_GET_ALL_SESSION message, so that we only reply to\r
- * the requesting node\r
- */\r
- protected void messageReceived( SessionMessage msg, Member sender ) {\r
- try {\r
- if(log.isInfoEnabled()) {\r
- log.debug("Received SessionMessage of type="+msg.getEventTypeString());\r
- log.debug("Received SessionMessage sender="+sender);\r
- }\r
- switch ( msg.getEventType() ) {\r
- case SessionMessage.EVT_GET_ALL_SESSIONS: {\r
- //get a list of all the session from this manager\r
- Object[] sessions = findSessions();\r
- java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();\r
- java.io.ObjectOutputStream oout = new java.io.ObjectOutputStream(bout);\r
- oout.writeInt(sessions.length);\r
- for (int i=0; i<sessions.length; i++){\r
- ReplicatedSession ses = (ReplicatedSession)sessions[i];\r
- oout.writeUTF(ses.getIdInternal());\r
- byte[] data = writeSession(ses);\r
- oout.writeObject(data);\r
- }//for\r
- //don't send a message if we don't have to\r
- oout.flush();\r
- oout.close();\r
- byte[] data = bout.toByteArray();\r
- SessionMessage newmsg = new SessionMessageImpl(name,\r
- SessionMessage.EVT_ALL_SESSION_DATA,\r
- data, "SESSION-STATE","SESSION-STATE-"+getName());\r
- cluster.send(newmsg, sender);\r
- break;\r
- }\r
- case SessionMessage.EVT_ALL_SESSION_DATA: {\r
- java.io.ByteArrayInputStream bin =\r
- new java.io.ByteArrayInputStream(msg.getSession());\r
- java.io.ObjectInputStream oin = new java.io.ObjectInputStream(bin);\r
- int size = oin.readInt();\r
- for ( int i=0; i<size; i++) {\r
- String id = oin.readUTF();\r
- byte[] data = (byte[])oin.readObject();\r
- Session session = readSession(data,id);\r
- }//for\r
- stateTransferred=true;\r
- break;\r
- }\r
- case SessionMessage.EVT_SESSION_CREATED: {\r
- Session session = this.readSession(msg.getSession(),msg.getSessionID());\r
- if ( log.isDebugEnabled() ) {\r
- log.debug("Received replicated session=" + session +\r
- " isValid=" + session.isValid());\r
- }\r
- break;\r
- }\r
- case SessionMessage.EVT_SESSION_EXPIRED: {\r
- Session session = findSession(msg.getSessionID());\r
- if ( session != null ) {\r
- session.expire();\r
- this.remove(session);\r
- }//end if\r
- break;\r
- }\r
- case SessionMessage.EVT_SESSION_ACCESSED :{\r
- Session session = findSession(msg.getSessionID());\r
- if ( session != null ) {\r
- session.access();\r
- session.endAccess();\r
- }\r
- break;\r
- }\r
- default: {\r
- //we didn't recognize the message type, do nothing\r
- break;\r
- }\r
- }//switch\r
- }\r
- catch ( Exception x )\r
- {\r
- log.error("Unable to receive message through TCP channel",x);\r
- }\r
- }\r
-\r
- public void messageDataReceived(ClusterMessage cmsg) {\r
- try {\r
- if ( cmsg instanceof SessionMessage ) {\r
- SessionMessage msg = (SessionMessage)cmsg;\r
- messageReceived(msg,\r
- msg.getAddress() != null ? (Member) msg.getAddress() : null);\r
- }\r
- } catch(Throwable ex){\r
- log.error("InMemoryReplicationManager.messageDataReceived()", ex);\r
- }//catch\r
- }\r
-\r
- public boolean isStateTransferred() {\r
- return stateTransferred;\r
- }\r
-\r
- public void setName(String name) {\r
- this.name = name;\r
- }\r
- public boolean isNotifyListenersOnReplication() {\r
- return notifyListenersOnReplication;\r
- }\r
- public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {\r
- this.notifyListenersOnReplication = notifyListenersOnReplication;\r
- }\r
-\r
-\r
- /* \r
- * @see org.apache.catalina.ha.ClusterManager#getCluster()\r
- */\r
- public CatalinaCluster getCluster() {\r
- return cluster;\r
- }\r
-\r
- public ClusterManager cloneFromTemplate() {\r
- throw new UnsupportedOperationException();\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.session;
+
+import java.io.IOException;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.catalina.ha.ClusterManager;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.tribes.io.ReplicationStream;
+import java.io.ByteArrayInputStream;
+import org.apache.catalina.Loader;
+
+/**
+ * Title: Tomcat Session Replication for Tomcat 4.0 <BR>
+ * Description: A very simple straight forward implementation of
+ * session replication of servers in a cluster.<BR>
+ * This session replication is implemented "live". By live
+ * I mean, when a session attribute is added into a session on Node A
+ * a message is broadcasted to other messages and setAttribute is called on the
+ * replicated sessions.<BR>
+ * A full description of this implementation can be found under
+ * <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a><BR>
+ *
+ * Copyright: See apache license
+ * Company: www.filip.net
+ * @author <a href="mailto:mail@filip.net">Filip Hanik</a>
+ * @author Bela Ban (modifications for synchronous replication)
+ * @version 1.0 for TC 4.0
+ * Description: The InMemoryReplicationManager is a session manager that replicated
+ * session information in memory.
+ * <BR><BR>
+ * The InMemoryReplicationManager extends the StandardManager hence it allows for us
+ * to inherit all the basic session management features like expiration, session listeners etc
+ * <BR><BR>
+ * To communicate with other nodes in the cluster, the InMemoryReplicationManager sends out 7 different type of multicast messages
+ * all defined in the SessionMessage class.<BR>
+ * When a session is replicated (not an attribute added/removed) the session is serialized into
+ * a byte array using the StandardSession.readObjectData, StandardSession.writeObjectData methods.
+ */
+public class SimpleTcpReplicationManager extends StandardManager implements ClusterManager
+{
+ public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( SimpleTcpReplicationManager.class );
+
+ //the channel configuration
+ protected String mChannelConfig = null;
+
+ //the group name
+ protected String mGroupName = "TomcatReplication";
+
+ //somehow start() gets called more than once
+ protected boolean mChannelStarted = false;
+
+ //log to screen
+ protected boolean mPrintToScreen = true;
+
+ protected boolean defaultMode = false;
+
+ protected boolean mManagerRunning = false;
+
+ /** Use synchronous rather than asynchronous replication. Every session modification (creation, change, removal etc)
+ * will be sent to all members. The call will then wait for max milliseconds, or forever (if timeout is 0) for
+ * all responses.
+ */
+ protected boolean synchronousReplication=true;
+
+ /** Set to true if we don't want the sessions to expire on shutdown */
+ protected boolean mExpireSessionsOnShutdown = true;
+
+ protected boolean useDirtyFlag = false;
+
+ protected String name;
+
+ protected boolean distributable = true;
+
+ protected CatalinaCluster cluster;
+
+ protected java.util.HashMap invalidatedSessions = new java.util.HashMap();
+
+ /**
+ * Flag to keep track if the state has been transferred or not
+ * Assumes false.
+ */
+ protected boolean stateTransferred = false;
+ private boolean notifyListenersOnReplication;
+ private boolean sendClusterDomainOnly = true ;
+
+ /**
+ * Constructor, just calls super()
+ *
+ */
+ public SimpleTcpReplicationManager()
+ {
+ super();
+ }
+
+ public boolean doDomainReplication() {
+ return sendClusterDomainOnly;
+ }
+
+ /**
+ * @param sendClusterDomainOnly The sendClusterDomainOnly to set.
+ */
+ public void setDomainReplication(boolean sendClusterDomainOnly) {
+ this.sendClusterDomainOnly = sendClusterDomainOnly;
+ }
+
+ /**
+ * @return Returns the defaultMode.
+ */
+ public boolean isDefaultMode() {
+ return defaultMode;
+ }
+ /**
+ * @param defaultMode The defaultMode to set.
+ */
+ public void setDefaultMode(boolean defaultMode) {
+ this.defaultMode = defaultMode;
+ }
+
+ public boolean isManagerRunning()
+ {
+ return mManagerRunning;
+ }
+
+ public void setUseDirtyFlag(boolean usedirtyflag)
+ {
+ this.useDirtyFlag = usedirtyflag;
+ }
+
+ public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown)
+ {
+ mExpireSessionsOnShutdown = expireSessionsOnShutdown;
+ }
+
+ public void setCluster(CatalinaCluster cluster) {
+ if(log.isDebugEnabled())
+ log.debug("Cluster associated with SimpleTcpReplicationManager");
+ this.cluster = cluster;
+ }
+
+ public boolean getExpireSessionsOnShutdown()
+ {
+ return mExpireSessionsOnShutdown;
+ }
+
+ public void setPrintToScreen(boolean printtoscreen)
+ {
+ if(log.isDebugEnabled())
+ log.debug("Setting screen debug to:"+printtoscreen);
+ mPrintToScreen = printtoscreen;
+ }
+
+ public void setSynchronousReplication(boolean flag)
+ {
+ synchronousReplication=flag;
+ }
+
+ /**
+ * Override persistence since they don't go hand in hand with replication for now.
+ */
+ public void unload() throws IOException {
+ if ( !getDistributable() ) {
+ super.unload();
+ }
+ }
+
+ /**
+ * Creates a HTTP session.
+ * Most of the code in here is copied from the StandardManager.
+ * This is not pretty, yeah I know, but it was necessary since the
+ * StandardManager had hard coded the session instantiation to the a
+ * StandardSession, when we actually want to instantiate a ReplicatedSession<BR>
+ * If the call comes from the Tomcat servlet engine, a SessionMessage goes out to the other
+ * nodes in the cluster that this session has been created.
+ * @param notify - if set to true the other nodes in the cluster will be notified.
+ * This flag is needed so that we can create a session before we deserialize
+ * a replicated one
+ *
+ * @see ReplicatedSession
+ */
+ protected Session createSession(String sessionId, boolean notify, boolean setId)
+ {
+
+ //inherited from the basic manager
+ if ((getMaxActiveSessions() >= 0) &&
+ (sessions.size() >= getMaxActiveSessions()))
+ throw new IllegalStateException(sm.getString("standardManager.createSession.ise"));
+
+
+ Session session = new ReplicatedSession(this);
+
+ // Initialize the properties of the new session and return it
+ session.setNew(true);
+ session.setValid(true);
+ session.setCreationTime(System.currentTimeMillis());
+ session.setMaxInactiveInterval(this.maxInactiveInterval);
+ if(sessionId == null)
+ sessionId = generateSessionId();
+ if ( setId ) session.setId(sessionId);
+ if ( notify && (cluster!=null) ) {
+ ((ReplicatedSession)session).setIsDirty(true);
+ }
+ return (session);
+ }//createSession
+
+ //=========================================================================
+ // OVERRIDE THESE METHODS TO IMPLEMENT THE REPLICATION
+ //=========================================================================
+
+ /**
+ * Construct and return a new session object, based on the default
+ * settings specified by this Manager's properties. The session
+ * id will be assigned by this method, and available via the getId()
+ * method of the returned session. If a new session cannot be created
+ * for any reason, return <code>null</code>.
+ *
+ * @exception IllegalStateException if a new session cannot be
+ * instantiated for any reason
+ */
+ public Session createSession(String sessionId)
+ {
+ //create a session and notify the other nodes in the cluster
+ Session session = createSession(sessionId,getDistributable(),true);
+ add(session);
+ return session;
+ }
+
+ public void sessionInvalidated(String sessionId) {
+ synchronized ( invalidatedSessions ) {
+ invalidatedSessions.put(sessionId, sessionId);
+ }
+ }
+
+ public String[] getInvalidatedSessions() {
+ synchronized ( invalidatedSessions ) {
+ String[] result = new String[invalidatedSessions.size()];
+ invalidatedSessions.values().toArray(result);
+ return result;
+ }
+
+ }
+
+ public ClusterMessage requestCompleted(String sessionId)
+ {
+ if ( !getDistributable() ) {
+ log.warn("Received requestCompleted message, although this context["+
+ getName()+"] is not distributable. Ignoring message");
+ return null;
+ }
+ try
+ {
+ if ( invalidatedSessions.get(sessionId) != null ) {
+ synchronized ( invalidatedSessions ) {
+ invalidatedSessions.remove(sessionId);
+ SessionMessage msg = new SessionMessageImpl(name,
+ SessionMessage.EVT_SESSION_EXPIRED,
+ null,
+ sessionId,
+ sessionId);
+ return msg;
+ }
+ } else {
+ ReplicatedSession session = (ReplicatedSession) findSession(
+ sessionId);
+ if (session != null) {
+ //return immediately if the session is not dirty
+ if (useDirtyFlag && (!session.isDirty())) {
+ //but before we return doing nothing,
+ //see if we should send
+ //an updated last access message so that
+ //sessions across cluster dont expire
+ long interval = session.getMaxInactiveInterval();
+ long lastaccdist = System.currentTimeMillis() -
+ session.getLastAccessWasDistributed();
+ if ( ((interval*1000) / lastaccdist)< 3 ) {
+ SessionMessage accmsg = new SessionMessageImpl(name,
+ SessionMessage.EVT_SESSION_ACCESSED,
+ null,
+ sessionId,
+ sessionId);
+ session.setLastAccessWasDistributed(System.currentTimeMillis());
+ return accmsg;
+ }
+ return null;
+ }
+
+ session.setIsDirty(false);
+ if (log.isDebugEnabled()) {
+ try {
+ log.debug("Sending session to cluster=" + session);
+ }
+ catch (Exception ignore) {}
+ }
+ SessionMessage msg = new SessionMessageImpl(name,
+ SessionMessage.EVT_SESSION_CREATED,
+ writeSession(session),
+ session.getIdInternal(),
+ session.getIdInternal());
+ return msg;
+ } //end if
+ }//end if
+ }
+ catch (Exception x )
+ {
+ log.error("Unable to replicate session",x);
+ }
+ return null;
+ }
+
+ /**
+ * Serialize a session into a byte array<BR>
+ * This method simple calls the writeObjectData method on the session
+ * and returns the byte data from that call
+ * @param session - the session to be serialized
+ * @return a byte array containing the session data, null if the serialization failed
+ */
+ protected byte[] writeSession( Session session )
+ {
+ try
+ {
+ java.io.ByteArrayOutputStream session_data = new java.io.ByteArrayOutputStream();
+ java.io.ObjectOutputStream session_out = new java.io.ObjectOutputStream(session_data);
+ session_out.flush();
+ boolean hasPrincipal = session.getPrincipal() != null;
+ session_out.writeBoolean(hasPrincipal);
+ if ( hasPrincipal )
+ {
+ session_out.writeObject(SerializablePrincipal.createPrincipal((GenericPrincipal)session.getPrincipal()));
+ }//end if
+ ((ReplicatedSession)session).writeObjectData(session_out);
+ return session_data.toByteArray();
+
+ }
+ catch ( Exception x )
+ {
+ log.error("Failed to serialize the session!",x);
+ }
+ return null;
+ }
+
+ /**
+ * Open Stream and use correct ClassLoader (Container) Switch
+ * ThreadClassLoader
+ *
+ * @param data
+ * @return The object input stream
+ * @throws IOException
+ */
+ public ReplicationStream getReplicationStream(byte[] data) throws IOException {
+ return getReplicationStream(data,0,data.length);
+ }
+
+ public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException {
+ ByteArrayInputStream fis =null;
+ ReplicationStream ois = null;
+ Loader loader = null;
+ ClassLoader classLoader = null;
+ //fix to be able to run the DeltaManager
+ //stand alone without a container.
+ //use the Threads context class loader
+ if (container != null)
+ loader = container.getLoader();
+ if (loader != null)
+ classLoader = loader.getClassLoader();
+ else
+ classLoader = Thread.currentThread().getContextClassLoader();
+ //end fix
+ fis = new ByteArrayInputStream(data, offset, length);
+ if ( classLoader == Thread.currentThread().getContextClassLoader() ) {
+ ois = new ReplicationStream(fis, new ClassLoader[] {classLoader});
+ } else {
+ ois = new ReplicationStream(fis, new ClassLoader[] {classLoader,Thread.currentThread().getContextClassLoader()});
+ }
+ return ois;
+ }
+
+
+
+
+ /**
+ * Reinstantiates a serialized session from the data passed in.
+ * This will first call createSession() so that we get a fresh instance with all
+ * the managers set and all the transient fields validated.
+ * Then it calls Session.readObjectData(byte[]) to deserialize the object
+ * @param data - a byte array containing session data
+ * @return a valid Session object, null if an error occurs
+ *
+ */
+ protected Session readSession( byte[] data, String sessionId )
+ {
+ try
+ {
+ ReplicationStream session_in = getReplicationStream(data);
+
+ Session session = sessionId!=null?this.findSession(sessionId):null;
+ boolean isNew = (session==null);
+ //clear the old values from the existing session
+ if ( session!=null ) {
+ ReplicatedSession rs = (ReplicatedSession)session;
+ rs.expire(false); //cleans up the previous values, since we are not doing removes
+ session = null;
+ }//end if
+
+ if (session==null) {
+ session = createSession(null,false, false);
+ sessions.remove(session.getIdInternal());
+ }
+
+
+ boolean hasPrincipal = session_in.readBoolean();
+ SerializablePrincipal p = null;
+ if ( hasPrincipal )
+ p = (SerializablePrincipal)session_in.readObject();
+ ((ReplicatedSession)session).readObjectData(session_in);
+ if ( hasPrincipal )
+ session.setPrincipal(p.getPrincipal(getContainer().getRealm()));
+ ((ReplicatedSession)session).setId(sessionId,isNew);
+ ReplicatedSession rsession = (ReplicatedSession)session;
+ rsession.setAccessCount(1);
+ session.setManager(this);
+ session.setValid(true);
+ rsession.setLastAccessedTime(System.currentTimeMillis());
+ rsession.setThisAccessedTime(System.currentTimeMillis());
+ ((ReplicatedSession)session).setAccessCount(0);
+ session.setNew(false);
+ if(log.isTraceEnabled())
+ log.trace("Session loaded id="+sessionId +
+ " actualId="+session.getId()+
+ " exists="+this.sessions.containsKey(sessionId)+
+ " valid="+rsession.isValid());
+ return session;
+
+ }
+ catch ( Exception x )
+ {
+ log.error("Failed to deserialize the session!",x);
+ }
+ return null;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized.<BR>
+ * Starts the cluster communication channel, this will connect with the other nodes
+ * in the cluster, and request the current session state to be transferred to this node.
+ * @exception IllegalStateException if this component has already been
+ * started
+ * @exception LifecycleException if this component detects a fatal error
+ * that prevents this component from being used
+ */
+ public void start() throws LifecycleException {
+ mManagerRunning = true;
+ super.start();
+ try {
+ //the channel is already running
+ if ( mChannelStarted ) return;
+ if(log.isInfoEnabled())
+ log.info("Starting clustering manager...:"+getName());
+ if ( cluster == null ) {
+ log.error("Starting... no cluster associated with this context:"+getName());
+ return;
+ }
+ cluster.registerManager(this);
+
+ if (cluster.getMembers().length > 0) {
+ Member mbr = cluster.getMembers()[0];
+ SessionMessage msg =
+ new SessionMessageImpl(this.getName(),
+ SessionMessage.EVT_GET_ALL_SESSIONS,
+ null,
+ "GET-ALL",
+ "GET-ALL-"+this.getName());
+ cluster.send(msg, mbr);
+ if(log.isWarnEnabled())
+ log.warn("Manager["+getName()+"], requesting session state from "+mbr+
+ ". This operation will timeout if no session state has been received within "+
+ "60 seconds");
+ long reqStart = System.currentTimeMillis();
+ long reqNow = 0;
+ boolean isTimeout=false;
+ do {
+ try {
+ Thread.sleep(100);
+ }catch ( Exception sleep) {}
+ reqNow = System.currentTimeMillis();
+ isTimeout=((reqNow-reqStart)>(1000*60));
+ } while ( (!isStateTransferred()) && (!isTimeout));
+ if ( isTimeout || (!isStateTransferred()) ) {
+ log.error("Manager["+getName()+"], No session state received, timing out.");
+ }else {
+ if(log.isInfoEnabled())
+ log.info("Manager["+getName()+"], session state received in "+(reqNow-reqStart)+" ms.");
+ }
+ } else {
+ if(log.isInfoEnabled())
+ log.info("Manager["+getName()+"], skipping state transfer. No members active in cluster group.");
+ }//end if
+ mChannelStarted = true;
+ } catch ( Exception x ) {
+ log.error("Unable to start SimpleTcpReplicationManager",x);
+ }
+ }
+
+ /**
+ * Gracefully terminate the active use of the public methods of this
+ * component. This method should be the last one called on a given
+ * instance of this component.<BR>
+ * This will disconnect the cluster communication channel and stop the listener thread.
+ * @exception IllegalStateException if this component has not been started
+ * @exception LifecycleException if this component detects a fatal error
+ * that needs to be reported
+ */
+ public void stop() throws LifecycleException
+ {
+ mManagerRunning = false;
+ mChannelStarted = false;
+ super.stop();
+ try
+ {
+ this.sessions.clear();
+ cluster.removeManager(this);
+ }
+ catch ( Exception x )
+ {
+ log.error("Unable to stop SimpleTcpReplicationManager",x);
+ }
+ }
+
+ public void setDistributable(boolean dist) {
+ this.distributable = dist;
+ }
+
+ public boolean getDistributable() {
+ return distributable;
+ }
+
+ /**
+ * This method is called by the received thread when a SessionMessage has
+ * been received from one of the other nodes in the cluster.
+ * @param msg - the message received
+ * @param sender - the sender of the message, this is used if we receive a
+ * EVT_GET_ALL_SESSION message, so that we only reply to
+ * the requesting node
+ */
+ protected void messageReceived( SessionMessage msg, Member sender ) {
+ try {
+ if(log.isInfoEnabled()) {
+ log.debug("Received SessionMessage of type="+msg.getEventTypeString());
+ log.debug("Received SessionMessage sender="+sender);
+ }
+ switch ( msg.getEventType() ) {
+ case SessionMessage.EVT_GET_ALL_SESSIONS: {
+ //get a list of all the session from this manager
+ Object[] sessions = findSessions();
+ java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
+ java.io.ObjectOutputStream oout = new java.io.ObjectOutputStream(bout);
+ oout.writeInt(sessions.length);
+ for (int i=0; i<sessions.length; i++){
+ ReplicatedSession ses = (ReplicatedSession)sessions[i];
+ oout.writeUTF(ses.getIdInternal());
+ byte[] data = writeSession(ses);
+ oout.writeObject(data);
+ }//for
+ //don't send a message if we don't have to
+ oout.flush();
+ oout.close();
+ byte[] data = bout.toByteArray();
+ SessionMessage newmsg = new SessionMessageImpl(name,
+ SessionMessage.EVT_ALL_SESSION_DATA,
+ data, "SESSION-STATE","SESSION-STATE-"+getName());
+ cluster.send(newmsg, sender);
+ break;
+ }
+ case SessionMessage.EVT_ALL_SESSION_DATA: {
+ java.io.ByteArrayInputStream bin =
+ new java.io.ByteArrayInputStream(msg.getSession());
+ java.io.ObjectInputStream oin = new java.io.ObjectInputStream(bin);
+ int size = oin.readInt();
+ for ( int i=0; i<size; i++) {
+ String id = oin.readUTF();
+ byte[] data = (byte[])oin.readObject();
+ Session session = readSession(data,id);
+ }//for
+ stateTransferred=true;
+ break;
+ }
+ case SessionMessage.EVT_SESSION_CREATED: {
+ Session session = this.readSession(msg.getSession(),msg.getSessionID());
+ if ( log.isDebugEnabled() ) {
+ log.debug("Received replicated session=" + session +
+ " isValid=" + session.isValid());
+ }
+ break;
+ }
+ case SessionMessage.EVT_SESSION_EXPIRED: {
+ Session session = findSession(msg.getSessionID());
+ if ( session != null ) {
+ session.expire();
+ this.remove(session);
+ }//end if
+ break;
+ }
+ case SessionMessage.EVT_SESSION_ACCESSED :{
+ Session session = findSession(msg.getSessionID());
+ if ( session != null ) {
+ session.access();
+ session.endAccess();
+ }
+ break;
+ }
+ default: {
+ //we didn't recognize the message type, do nothing
+ break;
+ }
+ }//switch
+ }
+ catch ( Exception x )
+ {
+ log.error("Unable to receive message through TCP channel",x);
+ }
+ }
+
+ public void messageDataReceived(ClusterMessage cmsg) {
+ try {
+ if ( cmsg instanceof SessionMessage ) {
+ SessionMessage msg = (SessionMessage)cmsg;
+ messageReceived(msg,
+ msg.getAddress() != null ? (Member) msg.getAddress() : null);
+ }
+ } catch(Throwable ex){
+ log.error("InMemoryReplicationManager.messageDataReceived()", ex);
+ }//catch
+ }
+
+ public boolean isStateTransferred() {
+ return stateTransferred;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ public boolean isNotifyListenersOnReplication() {
+ return notifyListenersOnReplication;
+ }
+ public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
+ this.notifyListenersOnReplication = notifyListenersOnReplication;
+ }
+
+
+ /*
+ * @see org.apache.catalina.ha.ClusterManager#getCluster()
+ */
+ public CatalinaCluster getCluster() {
+ return cluster;
+ }
+
+ public ClusterManager cloneFromTemplate() {
+ throw new UnsupportedOperationException();
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.ha.tcp;\r
-\r
-/**\r
- * Manifest constants for the <code>org.apache.catalina.ha.tcp</code>\r
- * package.\r
- *\r
- * @author Peter Rossbach\r
- * @version $Revision: 303753 $ $Date: 2005-03-14 15:24:30 -0600 (Mon, 14 Mar 2005) $\r
- */\r
-\r
-public class Constants {\r
-\r
- public static final String Package = "org.apache.catalina.ha.tcp";\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.tcp;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.ha.tcp</code>
+ * package.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class Constants {
+
+ public static final String Package = "org.apache.catalina.ha.tcp";
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.tcp;\r
-\r
-import java.io.IOException;\r
-import java.util.StringTokenizer;\r
-import java.util.regex.Pattern;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-import java.util.Iterator;\r
-import javax.servlet.ServletException;\r
-\r
-import org.apache.catalina.Manager;\r
-import org.apache.catalina.Session;\r
-import org.apache.catalina.Context;\r
-import org.apache.catalina.core.StandardContext;\r
-import org.apache.catalina.ha.CatalinaCluster;\r
-import org.apache.catalina.ha.ClusterManager;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.ha.ClusterSession;\r
-import org.apache.catalina.ha.ClusterValve;\r
-import org.apache.catalina.ha.session.DeltaManager;\r
-import org.apache.catalina.ha.session.DeltaSession;\r
-import org.apache.catalina.connector.Request;\r
-import org.apache.catalina.connector.Response;\r
-import org.apache.catalina.util.StringManager;\r
-import org.apache.catalina.valves.ValveBase;\r
-\r
-/**\r
- * <p>Implementation of a Valve that logs interesting contents from the\r
- * specified Request (before processing) and the corresponding Response\r
- * (after processing). It is especially useful in debugging problems\r
- * related to headers and cookies.</p>\r
- *\r
- * <p>This Valve may be attached to any Container, depending on the granularity\r
- * of the logging you wish to perform.</p>\r
- *\r
- * <p>primaryIndicator=true, then the request attribute <i>org.apache.catalina.ha.tcp.isPrimarySession.</i>\r
- * is set true, when request processing is at sessions primary node.\r
- * </p>\r
- *\r
- * @author Craig R. McClanahan\r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * @version $Revision: 375709 $ $Date: 2006-02-07 15:13:25 -0600 (Tue, 07 Feb 2006) $\r
- */\r
-\r
-public class ReplicationValve\r
- extends ValveBase implements ClusterValve {\r
- \r
- private static org.apache.juli.logging.Log log =\r
- org.apache.juli.logging.LogFactory.getLog( ReplicationValve.class );\r
-\r
- // ----------------------------------------------------- Instance Variables\r
-\r
- /**\r
- * The descriptive information related to this implementation.\r
- */\r
- private static final String info =\r
- "org.apache.catalina.ha.tcp.ReplicationValve/2.0";\r
-\r
-\r
- /**\r
- * The StringManager for this package.\r
- */\r
- protected static StringManager sm =\r
- StringManager.getManager(Constants.Package);\r
-\r
- private CatalinaCluster cluster = null ;\r
-\r
- /**\r
- * holds file endings to not call for like images and others\r
- */\r
- protected java.util.regex.Pattern[] reqFilters = new java.util.regex.Pattern[0];\r
- \r
- /**\r
- * Orginal filter \r
- */\r
- protected String filter ;\r
- \r
- /**\r
- * crossContext session container \r
- */\r
- protected ThreadLocal crossContextSessions = new ThreadLocal() ;\r
- \r
- /**\r
- * doProcessingStats (default = off)\r
- */\r
- protected boolean doProcessingStats = false;\r
- \r
- protected long totalRequestTime = 0;\r
- protected long totalSendTime = 0;\r
- protected long nrOfRequests = 0;\r
- protected long lastSendTime = 0;\r
- protected long nrOfFilterRequests = 0;\r
- protected long nrOfSendRequests = 0;\r
- protected long nrOfCrossContextSendRequests = 0;\r
- \r
- /**\r
- * must primary change indicator set \r
- */\r
- protected boolean primaryIndicator = false ;\r
- \r
- /**\r
- * Name of primary change indicator as request attribute\r
- */\r
- protected String primaryIndicatorName = "org.apache.catalina.ha.tcp.isPrimarySession";\r
- \r
- // ------------------------------------------------------------- Properties\r
-\r
- public ReplicationValve() {\r
- }\r
- \r
- /**\r
- * Return descriptive information about this Valve implementation.\r
- */\r
- public String getInfo() {\r
-\r
- return (info);\r
-\r
- }\r
- \r
- /**\r
- * @return Returns the cluster.\r
- */\r
- public CatalinaCluster getCluster() {\r
- return cluster;\r
- }\r
- \r
- /**\r
- * @param cluster The cluster to set.\r
- */\r
- public void setCluster(CatalinaCluster cluster) {\r
- this.cluster = cluster;\r
- }\r
- \r
- /**\r
- * @return Returns the filter\r
- */\r
- public String getFilter() {\r
- return filter ;\r
- }\r
-\r
- /**\r
- * compile filter string to regular expressions\r
- * @see Pattern#compile(java.lang.String)\r
- * @param filter\r
- * The filter to set.\r
- */\r
- public void setFilter(String filter) {\r
- if (log.isDebugEnabled())\r
- log.debug(sm.getString("ReplicationValve.filter.loading", filter));\r
- this.filter = filter;\r
- StringTokenizer t = new StringTokenizer(filter, ";");\r
- this.reqFilters = new Pattern[t.countTokens()];\r
- int i = 0;\r
- while (t.hasMoreTokens()) {\r
- String s = t.nextToken();\r
- if (log.isTraceEnabled())\r
- log.trace(sm.getString("ReplicationValve.filter.token", s));\r
- try {\r
- reqFilters[i++] = Pattern.compile(s);\r
- } catch (Exception x) {\r
- log.error(sm.getString("ReplicationValve.filter.token.failure",\r
- s), x);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * @return Returns the primaryIndicator.\r
- */\r
- public boolean isPrimaryIndicator() {\r
- return primaryIndicator;\r
- }\r
-\r
- /**\r
- * @param primaryIndicator The primaryIndicator to set.\r
- */\r
- public void setPrimaryIndicator(boolean primaryIndicator) {\r
- this.primaryIndicator = primaryIndicator;\r
- }\r
- \r
- /**\r
- * @return Returns the primaryIndicatorName.\r
- */\r
- public String getPrimaryIndicatorName() {\r
- return primaryIndicatorName;\r
- }\r
- \r
- /**\r
- * @param primaryIndicatorName The primaryIndicatorName to set.\r
- */\r
- public void setPrimaryIndicatorName(String primaryIndicatorName) {\r
- this.primaryIndicatorName = primaryIndicatorName;\r
- }\r
- \r
- /**\r
- * Calc processing stats\r
- */\r
- public boolean doStatistics() {\r
- return doProcessingStats;\r
- }\r
-\r
- /**\r
- * Set Calc processing stats\r
- * @see #resetStatistics()\r
- */\r
- public void setStatistics(boolean doProcessingStats) {\r
- this.doProcessingStats = doProcessingStats;\r
- }\r
-\r
- /**\r
- * @return Returns the lastSendTime.\r
- */\r
- public long getLastSendTime() {\r
- return lastSendTime;\r
- }\r
- \r
- /**\r
- * @return Returns the nrOfRequests.\r
- */\r
- public long getNrOfRequests() {\r
- return nrOfRequests;\r
- }\r
- \r
- /**\r
- * @return Returns the nrOfFilterRequests.\r
- */\r
- public long getNrOfFilterRequests() {\r
- return nrOfFilterRequests;\r
- }\r
-\r
- /**\r
- * @return Returns the nrOfCrossContextSendRequests.\r
- */\r
- public long getNrOfCrossContextSendRequests() {\r
- return nrOfCrossContextSendRequests;\r
- }\r
-\r
- /**\r
- * @return Returns the nrOfSendRequests.\r
- */\r
- public long getNrOfSendRequests() {\r
- return nrOfSendRequests;\r
- }\r
-\r
- /**\r
- * @return Returns the totalRequestTime.\r
- */\r
- public long getTotalRequestTime() {\r
- return totalRequestTime;\r
- }\r
- \r
- /**\r
- * @return Returns the totalSendTime.\r
- */\r
- public long getTotalSendTime() {\r
- return totalSendTime;\r
- }\r
-\r
- /**\r
- * @return Returns the reqFilters.\r
- */\r
- protected java.util.regex.Pattern[] getReqFilters() {\r
- return reqFilters;\r
- }\r
- \r
- /**\r
- * @param reqFilters The reqFilters to set.\r
- */\r
- protected void setReqFilters(java.util.regex.Pattern[] reqFilters) {\r
- this.reqFilters = reqFilters;\r
- }\r
- \r
- \r
- // --------------------------------------------------------- Public Methods\r
- \r
- /**\r
- * Register all cross context sessions inside endAccess.\r
- * Use a list with contains check, that the Portlet API can include a lot of fragments from same or\r
- * different applications with session changes.\r
- *\r
- * @param session cross context session\r
- */\r
- public void registerReplicationSession(DeltaSession session) {\r
- List sessions = (List)crossContextSessions.get();\r
- if(sessions != null) {\r
- if(!sessions.contains(session)) {\r
- if(log.isDebugEnabled())\r
- log.debug(sm.getString("ReplicationValve.crossContext.registerSession",\r
- session.getIdInternal(),\r
- session.getManager().getContainer().getName()));\r
- sessions.add(session);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Log the interesting request parameters, invoke the next Valve in the\r
- * sequence, and log the interesting response parameters.\r
- *\r
- * @param request The servlet request to be processed\r
- * @param response The servlet response to be created\r
- *\r
- * @exception IOException if an input/output error occurs\r
- * @exception ServletException if a servlet error occurs\r
- */\r
- public void invoke(Request request, Response response)\r
- throws IOException, ServletException\r
- {\r
- long totalstart = 0;\r
-\r
- //this happens before the request\r
- if(doStatistics()) {\r
- totalstart = System.currentTimeMillis();\r
- }\r
- if (primaryIndicator) {\r
- createPrimaryIndicator(request) ;\r
- }\r
- Context context = request.getContext();\r
- boolean isCrossContext = context != null\r
- && context instanceof StandardContext\r
- && ((StandardContext) context).getCrossContext();\r
- try {\r
- if(isCrossContext) {\r
- if(log.isDebugEnabled())\r
- log.debug(sm.getString("ReplicationValve.crossContext.add"));\r
- //FIXME add Pool of Arraylists\r
- crossContextSessions.set(new ArrayList());\r
- }\r
- getNext().invoke(request, response);\r
- Manager manager = request.getContext().getManager();\r
- if (manager != null && manager instanceof ClusterManager) {\r
- ClusterManager clusterManager = (ClusterManager) manager;\r
- CatalinaCluster containerCluster = (CatalinaCluster) getContainer().getCluster();\r
- if (containerCluster == null) {\r
- if (log.isWarnEnabled())\r
- log.warn(sm.getString("ReplicationValve.nocluster"));\r
- return;\r
- }\r
- // valve cluster can access manager - other cluster handle replication \r
- // at host level - hopefully!\r
- if(containerCluster.getManager(clusterManager.getName()) == null)\r
- return ;\r
- if(containerCluster.hasMembers()) {\r
- sendReplicationMessage(request, totalstart, isCrossContext, clusterManager, containerCluster);\r
- } else {\r
- resetReplicationRequest(request,isCrossContext);\r
- } \r
- }\r
- } finally {\r
- // Array must be remove: Current master request send endAccess at recycle. \r
- // Don't register this request session again!\r
- if(isCrossContext) {\r
- if(log.isDebugEnabled())\r
- log.debug(sm.getString("ReplicationValve.crossContext.remove"));\r
- // crossContextSessions.remove() only exist at Java 5\r
- // register ArrayList at a pool\r
- crossContextSessions.set(null);\r
- }\r
- }\r
- }\r
-\r
- \r
- /**\r
- * reset the active statitics \r
- */\r
- public void resetStatistics() {\r
- totalRequestTime = 0 ;\r
- totalSendTime = 0 ;\r
- lastSendTime = 0 ;\r
- nrOfFilterRequests = 0 ;\r
- nrOfRequests = 0 ;\r
- nrOfSendRequests = 0;\r
- nrOfCrossContextSendRequests = 0;\r
- }\r
- \r
- /**\r
- * Return a String rendering of this object.\r
- */\r
- public String toString() {\r
-\r
- StringBuffer sb = new StringBuffer("ReplicationValve[");\r
- if (container != null)\r
- sb.append(container.getName());\r
- sb.append("]");\r
- return (sb.toString());\r
-\r
- }\r
-\r
- // --------------------------------------------------------- Protected Methods\r
-\r
- /**\r
- * @param request\r
- * @param totalstart\r
- * @param isCrossContext\r
- * @param clusterManager\r
- * @param containerCluster\r
- */\r
- protected void sendReplicationMessage(Request request, long totalstart, boolean isCrossContext, ClusterManager clusterManager, CatalinaCluster containerCluster) {\r
- //this happens after the request\r
- long start = 0;\r
- if(doStatistics()) {\r
- start = System.currentTimeMillis();\r
- }\r
- try {\r
- // send invalid sessions\r
- // DeltaManager returns String[0]\r
- if (!(clusterManager instanceof DeltaManager))\r
- sendInvalidSessions(clusterManager, containerCluster);\r
- // send replication\r
- sendSessionReplicationMessage(request, clusterManager, containerCluster);\r
- if(isCrossContext)\r
- sendCrossContextSession(containerCluster);\r
- } catch (Exception x) {\r
- // FIXME we have a lot of sends, but the trouble with one node stops the correct replication to other nodes!\r
- log.error(sm.getString("ReplicationValve.send.failure"), x);\r
- } finally {\r
- // FIXME this stats update are not cheap!!\r
- if(doStatistics()) {\r
- updateStats(totalstart,start);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Send all changed cross context sessions to backups\r
- * @param containerCluster\r
- */\r
- protected void sendCrossContextSession(CatalinaCluster containerCluster) {\r
- Object sessions = crossContextSessions.get();\r
- if(sessions != null && sessions instanceof List\r
- && ((List)sessions).size() >0) {\r
- for(Iterator iter = ((List)sessions).iterator(); iter.hasNext() ;) { \r
- Session session = (Session)iter.next();\r
- if(log.isDebugEnabled())\r
- log.debug(sm.getString("ReplicationValve.crossContext.sendDelta", \r
- session.getManager().getContainer().getName() ));\r
- sendMessage(session,(ClusterManager)session.getManager(),containerCluster);\r
- if(doStatistics()) {\r
- nrOfCrossContextSendRequests++;\r
- }\r
- }\r
- }\r
- }\r
- \r
- /**\r
- * Fix memory leak for long sessions with many changes, when no backup member exists!\r
- * @param request current request after responce is generated\r
- * @param isCrossContext check crosscontext threadlocal\r
- */\r
- protected void resetReplicationRequest(Request request, boolean isCrossContext) {\r
- Session contextSession = request.getSessionInternal(false);\r
- if(contextSession != null & contextSession instanceof DeltaSession){\r
- resetDeltaRequest(contextSession);\r
- ((DeltaSession)contextSession).setPrimarySession(true);\r
- }\r
- if(isCrossContext) {\r
- Object sessions = crossContextSessions.get();\r
- if(sessions != null && sessions instanceof List\r
- && ((List)sessions).size() >0) {\r
- Iterator iter = ((List)sessions).iterator();\r
- for(; iter.hasNext() ;) { \r
- Session session = (Session)iter.next();\r
- resetDeltaRequest(session);\r
- if(session instanceof DeltaSession)\r
- ((DeltaSession)contextSession).setPrimarySession(true);\r
-\r
- }\r
- }\r
- } \r
- }\r
-\r
- /**\r
- * Reset DeltaRequest from session\r
- * @param session HttpSession from current request or cross context session\r
- */\r
- protected void resetDeltaRequest(Session session) {\r
- if(log.isDebugEnabled()) {\r
- log.debug(sm.getString("ReplicationValve.resetDeltaRequest" , \r
- session.getManager().getContainer().getName() ));\r
- }\r
- ((DeltaSession)session).resetDeltaRequest();\r
- }\r
-\r
- /**\r
- * Send Cluster Replication Request\r
- * @param request current request\r
- * @param manager session manager\r
- * @param cluster replication cluster\r
- */\r
- protected void sendSessionReplicationMessage(Request request,\r
- ClusterManager manager, CatalinaCluster cluster) {\r
- Session session = request.getSessionInternal(false);\r
- if (session != null) {\r
- String uri = request.getDecodedRequestURI();\r
- // request without session change\r
- if (!isRequestWithoutSessionChange(uri)) {\r
- if (log.isDebugEnabled())\r
- log.debug(sm.getString("ReplicationValve.invoke.uri", uri));\r
- sendMessage(session,manager,cluster);\r
- } else\r
- if(doStatistics())\r
- nrOfFilterRequests++;\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Send message delta message from request session \r
- * @param request current request\r
- * @param manager session manager\r
- * @param cluster replication cluster\r
- */\r
- protected void sendMessage(Session session,\r
- ClusterManager manager, CatalinaCluster cluster) {\r
- String id = session.getIdInternal();\r
- if (id != null) {\r
- send(manager, cluster, id);\r
- }\r
- }\r
-\r
- /**\r
- * send manager requestCompleted message to cluster\r
- * @param manager SessionManager\r
- * @param cluster replication cluster\r
- * @param sessionId sessionid from the manager\r
- * @see DeltaManager#requestCompleted(String)\r
- * @see SimpleTcpCluster#send(ClusterMessage)\r
- */\r
- protected void send(ClusterManager manager, CatalinaCluster cluster, String sessionId) {\r
- ClusterMessage msg = manager.requestCompleted(sessionId);\r
- if (msg != null) {\r
- if(manager.doDomainReplication()) {\r
- cluster.sendClusterDomain(msg);\r
- } else {\r
- cluster.send(msg);\r
- }\r
- if(doStatistics())\r
- nrOfSendRequests++;\r
- }\r
- }\r
- \r
- /**\r
- * check for session invalidations\r
- * @param manager\r
- * @param cluster\r
- */\r
- protected void sendInvalidSessions(ClusterManager manager, CatalinaCluster cluster) {\r
- String[] invalidIds=manager.getInvalidatedSessions();\r
- if ( invalidIds.length > 0 ) {\r
- for ( int i=0;i<invalidIds.length; i++ ) {\r
- try {\r
- send(manager,cluster,invalidIds[i]);\r
- } catch ( Exception x ) {\r
- log.error(sm.getString("ReplicationValve.send.invalid.failure",invalidIds[i]),x);\r
- }\r
- }\r
- }\r
- }\r
- \r
- /**\r
- * is request without possible session change\r
- * @param uri The request uri\r
- * @return True if no session change\r
- */\r
- protected boolean isRequestWithoutSessionChange(String uri) {\r
-\r
- boolean filterfound = false;\r
-\r
- for (int i = 0; (i < reqFilters.length) && (!filterfound); i++) {\r
- java.util.regex.Matcher matcher = reqFilters[i].matcher(uri);\r
- filterfound = matcher.matches();\r
- }\r
- return filterfound;\r
- }\r
-\r
- /**\r
- * protocol cluster replications stats\r
- * @param requestTime\r
- * @param clusterTime\r
- */\r
- protected void updateStats(long requestTime, long clusterTime) {\r
- synchronized(this) {\r
- lastSendTime=System.currentTimeMillis();\r
- totalSendTime+=lastSendTime - clusterTime;\r
- totalRequestTime+=lastSendTime - requestTime;\r
- nrOfRequests++;\r
- }\r
- if(log.isInfoEnabled()) {\r
- if ( (nrOfRequests % 100) == 0 ) {\r
- log.info(sm.getString("ReplicationValve.stats",\r
- new Object[]{\r
- new Long(totalRequestTime/nrOfRequests),\r
- new Long(totalSendTime/nrOfRequests),\r
- new Long(nrOfRequests),\r
- new Long(nrOfSendRequests),\r
- new Long(nrOfCrossContextSendRequests),\r
- new Long(nrOfFilterRequests),\r
- new Long(totalRequestTime),\r
- new Long(totalSendTime)}));\r
- }\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Mark Request that processed at primary node with attribute\r
- * primaryIndicatorName\r
- * \r
- * @param request\r
- * @throws IOException\r
- */\r
- protected void createPrimaryIndicator(Request request) throws IOException {\r
- String id = request.getRequestedSessionId();\r
- if ((id != null) && (id.length() > 0)) {\r
- Manager manager = request.getContext().getManager();\r
- Session session = manager.findSession(id);\r
- if (session instanceof ClusterSession) {\r
- ClusterSession cses = (ClusterSession) session;\r
- if (cses != null) {\r
- if (log.isDebugEnabled())\r
- log.debug(sm.getString(\r
- "ReplicationValve.session.indicator", request.getContext().getName(),id,\r
- primaryIndicatorName, cses.isPrimarySession()));\r
- request.setAttribute(primaryIndicatorName, cses.isPrimarySession()?Boolean.TRUE:Boolean.FALSE);\r
- }\r
- } else {\r
- if (log.isDebugEnabled()) {\r
- if (session != null) {\r
- log.debug(sm.getString(\r
- "ReplicationValve.session.found", request.getContext().getName(),id));\r
- } else {\r
- log.debug(sm.getString(\r
- "ReplicationValve.session.invalid", request.getContext().getName(),id));\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.tcp;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
+import org.apache.catalina.Context;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.catalina.ha.ClusterManager;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.ha.ClusterSession;
+import org.apache.catalina.ha.ClusterValve;
+import org.apache.catalina.ha.session.DeltaManager;
+import org.apache.catalina.ha.session.DeltaSession;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.valves.ValveBase;
+
+/**
+ * <p>Implementation of a Valve that logs interesting contents from the
+ * specified Request (before processing) and the corresponding Response
+ * (after processing). It is especially useful in debugging problems
+ * related to headers and cookies.</p>
+ *
+ * <p>This Valve may be attached to any Container, depending on the granularity
+ * of the logging you wish to perform.</p>
+ *
+ * <p>primaryIndicator=true, then the request attribute <i>org.apache.catalina.ha.tcp.isPrimarySession.</i>
+ * is set true, when request processing is at sessions primary node.
+ * </p>
+ *
+ * @author Craig R. McClanahan
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class ReplicationValve
+ extends ValveBase implements ClusterValve {
+
+ private static org.apache.juli.logging.Log log =
+ org.apache.juli.logging.LogFactory.getLog( ReplicationValve.class );
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * The descriptive information related to this implementation.
+ */
+ private static final String info =
+ "org.apache.catalina.ha.tcp.ReplicationValve/2.0";
+
+
+ /**
+ * The StringManager for this package.
+ */
+ protected static StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+ private CatalinaCluster cluster = null ;
+
+ /**
+ * holds file endings to not call for like images and others
+ */
+ protected java.util.regex.Pattern[] reqFilters = new java.util.regex.Pattern[0];
+
+ /**
+ * Orginal filter
+ */
+ protected String filter ;
+
+ /**
+ * crossContext session container
+ */
+ protected ThreadLocal crossContextSessions = new ThreadLocal() ;
+
+ /**
+ * doProcessingStats (default = off)
+ */
+ protected boolean doProcessingStats = false;
+
+ protected long totalRequestTime = 0;
+ protected long totalSendTime = 0;
+ protected long nrOfRequests = 0;
+ protected long lastSendTime = 0;
+ protected long nrOfFilterRequests = 0;
+ protected long nrOfSendRequests = 0;
+ protected long nrOfCrossContextSendRequests = 0;
+
+ /**
+ * must primary change indicator set
+ */
+ protected boolean primaryIndicator = false ;
+
+ /**
+ * Name of primary change indicator as request attribute
+ */
+ protected String primaryIndicatorName = "org.apache.catalina.ha.tcp.isPrimarySession";
+
+ // ------------------------------------------------------------- Properties
+
+ public ReplicationValve() {
+ }
+
+ /**
+ * Return descriptive information about this Valve implementation.
+ */
+ public String getInfo() {
+
+ return (info);
+
+ }
+
+ /**
+ * @return Returns the cluster.
+ */
+ public CatalinaCluster getCluster() {
+ return cluster;
+ }
+
+ /**
+ * @param cluster The cluster to set.
+ */
+ public void setCluster(CatalinaCluster cluster) {
+ this.cluster = cluster;
+ }
+
+ /**
+ * @return Returns the filter
+ */
+ public String getFilter() {
+ return filter ;
+ }
+
+ /**
+ * compile filter string to regular expressions
+ * @see Pattern#compile(java.lang.String)
+ * @param filter
+ * The filter to set.
+ */
+ public void setFilter(String filter) {
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("ReplicationValve.filter.loading", filter));
+ this.filter = filter;
+ StringTokenizer t = new StringTokenizer(filter, ";");
+ this.reqFilters = new Pattern[t.countTokens()];
+ int i = 0;
+ while (t.hasMoreTokens()) {
+ String s = t.nextToken();
+ if (log.isTraceEnabled())
+ log.trace(sm.getString("ReplicationValve.filter.token", s));
+ try {
+ reqFilters[i++] = Pattern.compile(s);
+ } catch (Exception x) {
+ log.error(sm.getString("ReplicationValve.filter.token.failure",
+ s), x);
+ }
+ }
+ }
+
+ /**
+ * @return Returns the primaryIndicator.
+ */
+ public boolean isPrimaryIndicator() {
+ return primaryIndicator;
+ }
+
+ /**
+ * @param primaryIndicator The primaryIndicator to set.
+ */
+ public void setPrimaryIndicator(boolean primaryIndicator) {
+ this.primaryIndicator = primaryIndicator;
+ }
+
+ /**
+ * @return Returns the primaryIndicatorName.
+ */
+ public String getPrimaryIndicatorName() {
+ return primaryIndicatorName;
+ }
+
+ /**
+ * @param primaryIndicatorName The primaryIndicatorName to set.
+ */
+ public void setPrimaryIndicatorName(String primaryIndicatorName) {
+ this.primaryIndicatorName = primaryIndicatorName;
+ }
+
+ /**
+ * Calc processing stats
+ */
+ public boolean doStatistics() {
+ return doProcessingStats;
+ }
+
+ /**
+ * Set Calc processing stats
+ * @see #resetStatistics()
+ */
+ public void setStatistics(boolean doProcessingStats) {
+ this.doProcessingStats = doProcessingStats;
+ }
+
+ /**
+ * @return Returns the lastSendTime.
+ */
+ public long getLastSendTime() {
+ return lastSendTime;
+ }
+
+ /**
+ * @return Returns the nrOfRequests.
+ */
+ public long getNrOfRequests() {
+ return nrOfRequests;
+ }
+
+ /**
+ * @return Returns the nrOfFilterRequests.
+ */
+ public long getNrOfFilterRequests() {
+ return nrOfFilterRequests;
+ }
+
+ /**
+ * @return Returns the nrOfCrossContextSendRequests.
+ */
+ public long getNrOfCrossContextSendRequests() {
+ return nrOfCrossContextSendRequests;
+ }
+
+ /**
+ * @return Returns the nrOfSendRequests.
+ */
+ public long getNrOfSendRequests() {
+ return nrOfSendRequests;
+ }
+
+ /**
+ * @return Returns the totalRequestTime.
+ */
+ public long getTotalRequestTime() {
+ return totalRequestTime;
+ }
+
+ /**
+ * @return Returns the totalSendTime.
+ */
+ public long getTotalSendTime() {
+ return totalSendTime;
+ }
+
+ /**
+ * @return Returns the reqFilters.
+ */
+ protected java.util.regex.Pattern[] getReqFilters() {
+ return reqFilters;
+ }
+
+ /**
+ * @param reqFilters The reqFilters to set.
+ */
+ protected void setReqFilters(java.util.regex.Pattern[] reqFilters) {
+ this.reqFilters = reqFilters;
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * Register all cross context sessions inside endAccess.
+ * Use a list with contains check, that the Portlet API can include a lot of fragments from same or
+ * different applications with session changes.
+ *
+ * @param session cross context session
+ */
+ public void registerReplicationSession(DeltaSession session) {
+ List sessions = (List)crossContextSessions.get();
+ if(sessions != null) {
+ if(!sessions.contains(session)) {
+ if(log.isDebugEnabled())
+ log.debug(sm.getString("ReplicationValve.crossContext.registerSession",
+ session.getIdInternal(),
+ session.getManager().getContainer().getName()));
+ sessions.add(session);
+ }
+ }
+ }
+
+ /**
+ * Log the interesting request parameters, invoke the next Valve in the
+ * sequence, and log the interesting response parameters.
+ *
+ * @param request The servlet request to be processed
+ * @param response The servlet response to be created
+ *
+ * @exception IOException if an input/output error occurs
+ * @exception ServletException if a servlet error occurs
+ */
+ public void invoke(Request request, Response response)
+ throws IOException, ServletException
+ {
+ long totalstart = 0;
+
+ //this happens before the request
+ if(doStatistics()) {
+ totalstart = System.currentTimeMillis();
+ }
+ if (primaryIndicator) {
+ createPrimaryIndicator(request) ;
+ }
+ Context context = request.getContext();
+ boolean isCrossContext = context != null
+ && context instanceof StandardContext
+ && ((StandardContext) context).getCrossContext();
+ try {
+ if(isCrossContext) {
+ if(log.isDebugEnabled())
+ log.debug(sm.getString("ReplicationValve.crossContext.add"));
+ //FIXME add Pool of Arraylists
+ crossContextSessions.set(new ArrayList());
+ }
+ getNext().invoke(request, response);
+ Manager manager = request.getContext().getManager();
+ if (manager != null && manager instanceof ClusterManager) {
+ ClusterManager clusterManager = (ClusterManager) manager;
+ CatalinaCluster containerCluster = (CatalinaCluster) getContainer().getCluster();
+ if (containerCluster == null) {
+ if (log.isWarnEnabled())
+ log.warn(sm.getString("ReplicationValve.nocluster"));
+ return;
+ }
+ // valve cluster can access manager - other cluster handle replication
+ // at host level - hopefully!
+ if(containerCluster.getManager(clusterManager.getName()) == null)
+ return ;
+ if(containerCluster.hasMembers()) {
+ sendReplicationMessage(request, totalstart, isCrossContext, clusterManager, containerCluster);
+ } else {
+ resetReplicationRequest(request,isCrossContext);
+ }
+ }
+ } finally {
+ // Array must be remove: Current master request send endAccess at recycle.
+ // Don't register this request session again!
+ if(isCrossContext) {
+ if(log.isDebugEnabled())
+ log.debug(sm.getString("ReplicationValve.crossContext.remove"));
+ // crossContextSessions.remove() only exist at Java 5
+ // register ArrayList at a pool
+ crossContextSessions.set(null);
+ }
+ }
+ }
+
+
+ /**
+ * reset the active statitics
+ */
+ public void resetStatistics() {
+ totalRequestTime = 0 ;
+ totalSendTime = 0 ;
+ lastSendTime = 0 ;
+ nrOfFilterRequests = 0 ;
+ nrOfRequests = 0 ;
+ nrOfSendRequests = 0;
+ nrOfCrossContextSendRequests = 0;
+ }
+
+ /**
+ * Return a String rendering of this object.
+ */
+ public String toString() {
+
+ StringBuffer sb = new StringBuffer("ReplicationValve[");
+ if (container != null)
+ sb.append(container.getName());
+ sb.append("]");
+ return (sb.toString());
+
+ }
+
+ // --------------------------------------------------------- Protected Methods
+
+ /**
+ * @param request
+ * @param totalstart
+ * @param isCrossContext
+ * @param clusterManager
+ * @param containerCluster
+ */
+ protected void sendReplicationMessage(Request request, long totalstart, boolean isCrossContext, ClusterManager clusterManager, CatalinaCluster containerCluster) {
+ //this happens after the request
+ long start = 0;
+ if(doStatistics()) {
+ start = System.currentTimeMillis();
+ }
+ try {
+ // send invalid sessions
+ // DeltaManager returns String[0]
+ if (!(clusterManager instanceof DeltaManager))
+ sendInvalidSessions(clusterManager, containerCluster);
+ // send replication
+ sendSessionReplicationMessage(request, clusterManager, containerCluster);
+ if(isCrossContext)
+ sendCrossContextSession(containerCluster);
+ } catch (Exception x) {
+ // FIXME we have a lot of sends, but the trouble with one node stops the correct replication to other nodes!
+ log.error(sm.getString("ReplicationValve.send.failure"), x);
+ } finally {
+ // FIXME this stats update are not cheap!!
+ if(doStatistics()) {
+ updateStats(totalstart,start);
+ }
+ }
+ }
+
+ /**
+ * Send all changed cross context sessions to backups
+ * @param containerCluster
+ */
+ protected void sendCrossContextSession(CatalinaCluster containerCluster) {
+ Object sessions = crossContextSessions.get();
+ if(sessions != null && sessions instanceof List
+ && ((List)sessions).size() >0) {
+ for(Iterator iter = ((List)sessions).iterator(); iter.hasNext() ;) {
+ Session session = (Session)iter.next();
+ if(log.isDebugEnabled())
+ log.debug(sm.getString("ReplicationValve.crossContext.sendDelta",
+ session.getManager().getContainer().getName() ));
+ sendMessage(session,(ClusterManager)session.getManager(),containerCluster);
+ if(doStatistics()) {
+ nrOfCrossContextSendRequests++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Fix memory leak for long sessions with many changes, when no backup member exists!
+ * @param request current request after responce is generated
+ * @param isCrossContext check crosscontext threadlocal
+ */
+ protected void resetReplicationRequest(Request request, boolean isCrossContext) {
+ Session contextSession = request.getSessionInternal(false);
+ if(contextSession != null & contextSession instanceof DeltaSession){
+ resetDeltaRequest(contextSession);
+ ((DeltaSession)contextSession).setPrimarySession(true);
+ }
+ if(isCrossContext) {
+ Object sessions = crossContextSessions.get();
+ if(sessions != null && sessions instanceof List
+ && ((List)sessions).size() >0) {
+ Iterator iter = ((List)sessions).iterator();
+ for(; iter.hasNext() ;) {
+ Session session = (Session)iter.next();
+ resetDeltaRequest(session);
+ if(session instanceof DeltaSession)
+ ((DeltaSession)contextSession).setPrimarySession(true);
+
+ }
+ }
+ }
+ }
+
+ /**
+ * Reset DeltaRequest from session
+ * @param session HttpSession from current request or cross context session
+ */
+ protected void resetDeltaRequest(Session session) {
+ if(log.isDebugEnabled()) {
+ log.debug(sm.getString("ReplicationValve.resetDeltaRequest" ,
+ session.getManager().getContainer().getName() ));
+ }
+ ((DeltaSession)session).resetDeltaRequest();
+ }
+
+ /**
+ * Send Cluster Replication Request
+ * @param request current request
+ * @param manager session manager
+ * @param cluster replication cluster
+ */
+ protected void sendSessionReplicationMessage(Request request,
+ ClusterManager manager, CatalinaCluster cluster) {
+ Session session = request.getSessionInternal(false);
+ if (session != null) {
+ String uri = request.getDecodedRequestURI();
+ // request without session change
+ if (!isRequestWithoutSessionChange(uri)) {
+ if (log.isDebugEnabled())
+ log.debug(sm.getString("ReplicationValve.invoke.uri", uri));
+ sendMessage(session,manager,cluster);
+ } else
+ if(doStatistics())
+ nrOfFilterRequests++;
+ }
+
+ }
+
+ /**
+ * Send message delta message from request session
+ * @param request current request
+ * @param manager session manager
+ * @param cluster replication cluster
+ */
+ protected void sendMessage(Session session,
+ ClusterManager manager, CatalinaCluster cluster) {
+ String id = session.getIdInternal();
+ if (id != null) {
+ send(manager, cluster, id);
+ }
+ }
+
+ /**
+ * send manager requestCompleted message to cluster
+ * @param manager SessionManager
+ * @param cluster replication cluster
+ * @param sessionId sessionid from the manager
+ * @see DeltaManager#requestCompleted(String)
+ * @see SimpleTcpCluster#send(ClusterMessage)
+ */
+ protected void send(ClusterManager manager, CatalinaCluster cluster, String sessionId) {
+ ClusterMessage msg = manager.requestCompleted(sessionId);
+ if (msg != null) {
+ if(manager.doDomainReplication()) {
+ cluster.sendClusterDomain(msg);
+ } else {
+ cluster.send(msg);
+ }
+ if(doStatistics())
+ nrOfSendRequests++;
+ }
+ }
+
+ /**
+ * check for session invalidations
+ * @param manager
+ * @param cluster
+ */
+ protected void sendInvalidSessions(ClusterManager manager, CatalinaCluster cluster) {
+ String[] invalidIds=manager.getInvalidatedSessions();
+ if ( invalidIds.length > 0 ) {
+ for ( int i=0;i<invalidIds.length; i++ ) {
+ try {
+ send(manager,cluster,invalidIds[i]);
+ } catch ( Exception x ) {
+ log.error(sm.getString("ReplicationValve.send.invalid.failure",invalidIds[i]),x);
+ }
+ }
+ }
+ }
+
+ /**
+ * is request without possible session change
+ * @param uri The request uri
+ * @return True if no session change
+ */
+ protected boolean isRequestWithoutSessionChange(String uri) {
+
+ boolean filterfound = false;
+
+ for (int i = 0; (i < reqFilters.length) && (!filterfound); i++) {
+ java.util.regex.Matcher matcher = reqFilters[i].matcher(uri);
+ filterfound = matcher.matches();
+ }
+ return filterfound;
+ }
+
+ /**
+ * protocol cluster replications stats
+ * @param requestTime
+ * @param clusterTime
+ */
+ protected void updateStats(long requestTime, long clusterTime) {
+ synchronized(this) {
+ lastSendTime=System.currentTimeMillis();
+ totalSendTime+=lastSendTime - clusterTime;
+ totalRequestTime+=lastSendTime - requestTime;
+ nrOfRequests++;
+ }
+ if(log.isInfoEnabled()) {
+ if ( (nrOfRequests % 100) == 0 ) {
+ log.info(sm.getString("ReplicationValve.stats",
+ new Object[]{
+ new Long(totalRequestTime/nrOfRequests),
+ new Long(totalSendTime/nrOfRequests),
+ new Long(nrOfRequests),
+ new Long(nrOfSendRequests),
+ new Long(nrOfCrossContextSendRequests),
+ new Long(nrOfFilterRequests),
+ new Long(totalRequestTime),
+ new Long(totalSendTime)}));
+ }
+ }
+ }
+
+
+ /**
+ * Mark Request that processed at primary node with attribute
+ * primaryIndicatorName
+ *
+ * @param request
+ * @throws IOException
+ */
+ protected void createPrimaryIndicator(Request request) throws IOException {
+ String id = request.getRequestedSessionId();
+ if ((id != null) && (id.length() > 0)) {
+ Manager manager = request.getContext().getManager();
+ Session session = manager.findSession(id);
+ if (session instanceof ClusterSession) {
+ ClusterSession cses = (ClusterSession) session;
+ if (cses != null) {
+ if (log.isDebugEnabled())
+ log.debug(sm.getString(
+ "ReplicationValve.session.indicator", request.getContext().getName(),id,
+ primaryIndicatorName, cses.isPrimarySession()));
+ request.setAttribute(primaryIndicatorName, cses.isPrimarySession()?Boolean.TRUE:Boolean.FALSE);
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ if (session != null) {
+ log.debug(sm.getString(
+ "ReplicationValve.session.found", request.getContext().getName(),id));
+ } else {
+ log.debug(sm.getString(
+ "ReplicationValve.session.invalid", request.getContext().getName(),id));
+ }
+ }
+ }
+ }
+ }
+
+}
/**
* @author Peter Rossbach
- * @version $Revision: 303987 $ $Date: 2005-07-08 15:50:30 -0500 (Fri, 08 Jul 2005) $
+ * @version $Revision$ $Date$
*/
public class SendMessageData {
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.tcp;\r
-\r
-import java.beans.PropertyChangeSupport;\r
-import java.io.IOException;\r
-import java.io.Serializable;\r
-import java.net.URL;\r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.apache.catalina.Container;\r
-import org.apache.catalina.Context;\r
-import org.apache.catalina.Engine;\r
-import org.apache.catalina.Host;\r
-import org.apache.catalina.Lifecycle;\r
-import org.apache.catalina.LifecycleEvent;\r
-import org.apache.catalina.LifecycleException;\r
-import org.apache.catalina.LifecycleListener;\r
-import org.apache.catalina.Manager;\r
-import org.apache.catalina.Valve;\r
-import org.apache.catalina.ha.CatalinaCluster;\r
-import org.apache.catalina.ha.ClusterListener;\r
-import org.apache.catalina.ha.ClusterManager;\r
-import org.apache.catalina.ha.ClusterMessage;\r
-import org.apache.catalina.ha.ClusterValve;\r
-import org.apache.catalina.ha.session.DeltaManager;\r
-import org.apache.catalina.ha.util.IDynamicProperty;\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelListener;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.MembershipListener;\r
-import org.apache.catalina.tribes.group.GroupChannel;\r
-import org.apache.catalina.util.LifecycleSupport;\r
-import org.apache.catalina.util.StringManager;\r
-import org.apache.juli.logging.Log;\r
-import org.apache.juli.logging.LogFactory;\r
-import org.apache.tomcat.util.IntrospectionUtils;\r
-import org.apache.catalina.ha.session.ClusterSessionListener;\r
-import org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor;\r
-import org.apache.catalina.tribes.group.interceptors.TcpFailureDetector;\r
-import org.apache.catalina.ha.session.JvmRouteBinderValve;\r
-import org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener;\r
-\r
-/**\r
- * A <b>Cluster </b> implementation using simple multicast. Responsible for\r
- * setting up a cluster and provides callers with a valid multicast\r
- * receiver/sender.\r
- * \r
- * FIXME remove install/remove/start/stop context dummys\r
- * FIXME wrote testcases \r
- * \r
- * @author Filip Hanik\r
- * @author Remy Maucherat\r
- * @author Peter Rossbach\r
- * @version $Revision: 379550 $, $Date: 2006-02-21 12:06:35 -0600 (Tue, 21 Feb 2006) $\r
- */\r
-public class SimpleTcpCluster \r
- implements CatalinaCluster, Lifecycle, LifecycleListener, IDynamicProperty,\r
- MembershipListener, ChannelListener{\r
-\r
- public static Log log = LogFactory.getLog(SimpleTcpCluster.class);\r
-\r
- // ----------------------------------------------------- Instance Variables\r
-\r
- /**\r
- * Descriptive information about this component implementation.\r
- */\r
- protected static final String info = "SimpleTcpCluster/2.2";\r
-\r
- public static final String BEFORE_MEMBERREGISTER_EVENT = "before_member_register";\r
-\r
- public static final String AFTER_MEMBERREGISTER_EVENT = "after_member_register";\r
-\r
- public static final String BEFORE_MANAGERREGISTER_EVENT = "before_manager_register";\r
-\r
- public static final String AFTER_MANAGERREGISTER_EVENT = "after_manager_register";\r
-\r
- public static final String BEFORE_MANAGERUNREGISTER_EVENT = "before_manager_unregister";\r
-\r
- public static final String AFTER_MANAGERUNREGISTER_EVENT = "after_manager_unregister";\r
-\r
- public static final String BEFORE_MEMBERUNREGISTER_EVENT = "before_member_unregister";\r
-\r
- public static final String AFTER_MEMBERUNREGISTER_EVENT = "after_member_unregister";\r
-\r
- public static final String SEND_MESSAGE_FAILURE_EVENT = "send_message_failure";\r
-\r
- public static final String RECEIVE_MESSAGE_FAILURE_EVENT = "receive_message_failure";\r
- \r
- /**\r
- * Group channel.\r
- */\r
- protected Channel channel = new GroupChannel();\r
-\r
-\r
- /**\r
- * Name for logging purpose\r
- */\r
- protected String clusterImpName = "SimpleTcpCluster";\r
-\r
- /**\r
- * The string manager for this package.\r
- */\r
- protected StringManager sm = StringManager.getManager(Constants.Package);\r
-\r
- /**\r
- * The cluster name to join\r
- */\r
- protected String clusterName ;\r
-\r
- /**\r
- * The Container associated with this Cluster.\r
- */\r
- protected Container container = null;\r
-\r
- /**\r
- * The lifecycle event support for this component.\r
- */\r
- protected LifecycleSupport lifecycle = new LifecycleSupport(this);\r
-\r
- /**\r
- * Has this component been started?\r
- */\r
- protected boolean started = false;\r
-\r
- /**\r
- * The property change support for this component.\r
- */\r
- protected PropertyChangeSupport support = new PropertyChangeSupport(this);\r
-\r
- /**\r
- * The context name <->manager association for distributed contexts.\r
- */\r
- protected Map managers = new HashMap();\r
-\r
- protected ClusterManager managerTemplate = new DeltaManager();\r
-\r
- private List valves = new ArrayList();\r
-\r
- private org.apache.catalina.ha.ClusterDeployer clusterDeployer;\r
-\r
- /**\r
- * Listeners of messages\r
- */\r
- protected List clusterListeners = new ArrayList();\r
-\r
- /**\r
- * Comment for <code>notifyLifecycleListenerOnFailure</code>\r
- */\r
- private boolean notifyLifecycleListenerOnFailure = false;\r
-\r
- /**\r
- * dynamic sender <code>properties</code>\r
- */\r
- private Map properties = new HashMap();\r
- \r
- private int channelSendOptions = Channel.SEND_OPTIONS_ASYNCHRONOUS;\r
-\r
- // ------------------------------------------------------------- Properties\r
-\r
- public SimpleTcpCluster() {\r
- }\r
-\r
- /**\r
- * Return descriptive information about this Cluster implementation and the\r
- * corresponding version number, in the format\r
- * <code><description>/<version></code>.\r
- */\r
- public String getInfo() {\r
- return (info);\r
- }\r
-\r
- /**\r
- * Set the name of the cluster to join, if no cluster with this name is\r
- * present create one.\r
- * \r
- * @param clusterName\r
- * The clustername to join\r
- */\r
- public void setClusterName(String clusterName) {\r
- this.clusterName = clusterName;\r
- }\r
-\r
- /**\r
- * Return the name of the cluster that this Server is currently configured\r
- * to operate within.\r
- * \r
- * @return The name of the cluster associated with this server\r
- */\r
- public String getClusterName() {\r
- if(clusterName == null && container != null)\r
- return container.getName() ;\r
- return clusterName;\r
- }\r
-\r
- /**\r
- * Set the Container associated with our Cluster\r
- * \r
- * @param container\r
- * The Container to use\r
- */\r
- public void setContainer(Container container) {\r
- Container oldContainer = this.container;\r
- this.container = container;\r
- support.firePropertyChange("container", oldContainer, this.container);\r
- }\r
-\r
- /**\r
- * Get the Container associated with our Cluster\r
- * \r
- * @return The Container associated with our Cluster\r
- */\r
- public Container getContainer() {\r
- return (this.container);\r
- }\r
-\r
- /**\r
- * @return Returns the notifyLifecycleListenerOnFailure.\r
- */\r
- public boolean isNotifyLifecycleListenerOnFailure() {\r
- return notifyLifecycleListenerOnFailure;\r
- }\r
-\r
- /**\r
- * @param notifyListenerOnFailure\r
- * The notifyLifecycleListenerOnFailure to set.\r
- */\r
- public void setNotifyLifecycleListenerOnFailure(\r
- boolean notifyListenerOnFailure) {\r
- boolean oldNotifyListenerOnFailure = this.notifyLifecycleListenerOnFailure;\r
- this.notifyLifecycleListenerOnFailure = notifyListenerOnFailure;\r
- support.firePropertyChange("notifyLifecycleListenerOnFailure",\r
- oldNotifyListenerOnFailure,\r
- this.notifyLifecycleListenerOnFailure);\r
- }\r
-\r
- /**\r
- * @deprecated use getManagerTemplate().getClass().getName() instead.\r
- * @return String\r
- */\r
- public String getManagerClassName() {\r
- return managerTemplate.getClass().getName();\r
- }\r
-\r
- /**\r
- * @deprecated use nested <Manager> element inside the cluster config instead.\r
- * @param managerClassName String\r
- */\r
- public void setManagerClassName(String managerClassName) {\r
- log.warn("setManagerClassName is deprecated, use nested <Manager> element inside the <Cluster> element instead, this request will be ignored.");\r
- }\r
-\r
- /**\r
- * Add cluster valve \r
- * Cluster Valves are only add to container when cluster is started!\r
- * @param valve The new cluster Valve.\r
- */\r
- public void addValve(Valve valve) {\r
- if (valve instanceof ClusterValve && (!valves.contains(valve)))\r
- valves.add(valve);\r
- }\r
-\r
- /**\r
- * get all cluster valves\r
- * @return current cluster valves\r
- */\r
- public Valve[] getValves() {\r
- return (Valve[]) valves.toArray(new Valve[valves.size()]);\r
- }\r
-\r
- /**\r
- * Get the cluster listeners associated with this cluster. If this Array has\r
- * no listeners registered, a zero-length array is returned.\r
- */\r
- public ClusterListener[] findClusterListeners() {\r
- if (clusterListeners.size() > 0) {\r
- ClusterListener[] listener = new ClusterListener[clusterListeners.size()];\r
- clusterListeners.toArray(listener);\r
- return listener;\r
- } else\r
- return new ClusterListener[0];\r
-\r
- }\r
-\r
- /**\r
- * add cluster message listener and register cluster to this listener\r
- * \r
- * @see org.apache.catalina.ha.CatalinaCluster#addClusterListener(org.apache.catalina.ha.MessageListener)\r
- */\r
- public void addClusterListener(ClusterListener listener) {\r
- if (listener != null && !clusterListeners.contains(listener)) {\r
- clusterListeners.add(listener);\r
- listener.setCluster(this);\r
- }\r
- }\r
-\r
- /**\r
- * remove message listener and deregister Cluster from listener\r
- * \r
- * @see org.apache.catalina.ha.CatalinaCluster#removeClusterListener(org.apache.catalina.ha.MessageListener)\r
- */\r
- public void removeClusterListener(ClusterListener listener) {\r
- if (listener != null) {\r
- clusterListeners.remove(listener);\r
- listener.setCluster(null);\r
- }\r
- }\r
-\r
- /**\r
- * get current Deployer\r
- */\r
- public org.apache.catalina.ha.ClusterDeployer getClusterDeployer() {\r
- return clusterDeployer;\r
- }\r
-\r
- /**\r
- * set a new Deployer, must be set before cluster started!\r
- */\r
- public void setClusterDeployer(\r
- org.apache.catalina.ha.ClusterDeployer clusterDeployer) {\r
- this.clusterDeployer = clusterDeployer;\r
- }\r
-\r
- public void setChannel(Channel channel) {\r
- this.channel = channel;\r
- }\r
-\r
- public void setManagerTemplate(ClusterManager managerTemplate) {\r
- this.managerTemplate = managerTemplate;\r
- }\r
-\r
- public void setChannelSendOptions(int channelSendOptions) {\r
- this.channelSendOptions = channelSendOptions;\r
- }\r
-\r
- /**\r
- * has members\r
- */\r
- protected boolean hasMembers = false;\r
- public boolean hasMembers() {\r
- return hasMembers;\r
- }\r
- \r
- /**\r
- * Get all current cluster members\r
- * @return all members or empty array \r
- */\r
- public Member[] getMembers() {\r
- return channel.getMembers();\r
- }\r
-\r
- /**\r
- * Return the member that represents this node.\r
- * \r
- * @return Member\r
- */\r
- public Member getLocalMember() {\r
- return channel.getLocalMember(true);\r
- }\r
-\r
- // ------------------------------------------------------------- dynamic\r
- // manager property handling\r
-\r
- /**\r
- * JMX hack to direct use at jconsole\r
- * \r
- * @param name\r
- * @param value\r
- */\r
- public void setProperty(String name, String value) {\r
- setProperty(name, (Object) value);\r
- }\r
-\r
- /**\r
- * set config attributes with reflect and propagate to all managers\r
- * \r
- * @param name\r
- * @param value\r
- */\r
- public void setProperty(String name, Object value) {\r
- if (log.isTraceEnabled())\r
- log.trace(sm.getString("SimpleTcpCluster.setProperty", name, value,properties.get(name)));\r
- properties.put(name, value);\r
- //using a dynamic way of setting properties is nice, but a security risk\r
- //if exposed through JMX. This way you can sit and try to guess property names,\r
- //we will only allow explicit property names\r
- log.warn("Dynamic setProperty("+name+",value) has been disabled, please use explicit properties for the element you are trying to identify");\r
- if(started) {\r
- // FIXME Hmm, is that correct when some DeltaManagers are direct configured inside Context?\r
- // Why we not support it for other elements, like sender, receiver or membership?\r
- // Must we restart element after change?\r
-// if (name.startsWith("manager")) {\r
-// String key = name.substring("manager".length() + 1);\r
-// String pvalue = value.toString();\r
-// for (Iterator iter = managers.values().iterator(); iter.hasNext();) {\r
-// Manager manager = (Manager) iter.next();\r
-// if(manager instanceof DeltaManager && ((ClusterManager) manager).isDefaultMode()) {\r
-// IntrospectionUtils.setProperty(manager, key, pvalue );\r
-// }\r
-// }\r
-// } \r
- }\r
- }\r
-\r
- /**\r
- * get current config\r
- * \r
- * @param key\r
- * @return The property\r
- */\r
- public Object getProperty(String key) {\r
- if (log.isTraceEnabled())\r
- log.trace(sm.getString("SimpleTcpCluster.getProperty", key));\r
- return properties.get(key);\r
- }\r
-\r
- /**\r
- * Get all properties keys\r
- * \r
- * @return An iterator over the property names.\r
- */\r
- public Iterator getPropertyNames() {\r
- return properties.keySet().iterator();\r
- }\r
-\r
- /**\r
- * remove a configured property.\r
- * \r
- * @param key\r
- */\r
- public void removeProperty(String key) {\r
- properties.remove(key);\r
- }\r
-\r
- /**\r
- * transfer properties from cluster configuration to subelement bean.\r
- * @param prefix\r
- * @param bean\r
- */\r
- protected void transferProperty(String prefix, Object bean) {\r
- if (prefix != null) {\r
- for (Iterator iter = getPropertyNames(); iter.hasNext();) {\r
- String pkey = (String) iter.next();\r
- if (pkey.startsWith(prefix)) {\r
- String key = pkey.substring(prefix.length() + 1);\r
- Object value = getProperty(pkey);\r
- IntrospectionUtils.setProperty(bean, key, value.toString());\r
- }\r
- }\r
- }\r
- }\r
-\r
- // --------------------------------------------------------- Public Methods\r
-\r
- /**\r
- * @return Returns the managers.\r
- */\r
- public Map getManagers() {\r
- return managers;\r
- }\r
-\r
- public Channel getChannel() {\r
- return channel;\r
- }\r
-\r
- public ClusterManager getManagerTemplate() {\r
- return managerTemplate;\r
- }\r
-\r
- public int getChannelSendOptions() {\r
- return channelSendOptions;\r
- }\r
-\r
- /**\r
- * Create new Manager without add to cluster (comes with start the manager)\r
- * \r
- * @param name\r
- * Context Name of this manager\r
- * @see org.apache.catalina.Cluster#createManager(java.lang.String)\r
- * @see #addManager(String, Manager)\r
- * @see DeltaManager#start()\r
- */\r
- public synchronized Manager createManager(String name) {\r
- if (log.isDebugEnabled()) log.debug("Creating ClusterManager for context " + name + " using class " + getManagerClassName());\r
- Manager manager = null;\r
- try {\r
- manager = managerTemplate.cloneFromTemplate();\r
- ((ClusterManager)manager).setName(name);\r
- } catch (Exception x) {\r
- log.error("Unable to clone cluster manager, defaulting to org.apache.catalina.ha.session.DeltaManager", x);\r
- manager = new org.apache.catalina.ha.session.DeltaManager();\r
- } finally {\r
- if ( manager != null && (manager instanceof ClusterManager)) ((ClusterManager)manager).setCluster(this);\r
- }\r
- return manager;\r
- }\r
- \r
- public void registerManager(Manager manager) {\r
- \r
- if (! (manager instanceof ClusterManager)) {\r
- log.warn("Manager [ " + manager + "] does not implement ClusterManager, addition to cluster has been aborted.");\r
- return;\r
- }\r
- ClusterManager cmanager = (ClusterManager) manager ;\r
- cmanager.setDistributable(true);\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(BEFORE_MANAGERREGISTER_EVENT, manager);\r
- String clusterName = getManagerName(cmanager.getName(), manager);\r
- cmanager.setName(clusterName);\r
- cmanager.setCluster(this);\r
- cmanager.setDefaultMode(false);\r
- \r
- managers.put(clusterName, manager);\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(AFTER_MANAGERREGISTER_EVENT, manager); \r
- }\r
-\r
- /**\r
- * remove an application form cluster replication bus\r
- * \r
- * @see org.apache.catalina.ha.CatalinaCluster#removeManager(java.lang.String,Manager)\r
- */\r
- public void removeManager(Manager manager) {\r
- if (manager != null && manager instanceof ClusterManager ) {\r
- ClusterManager cmgr = (ClusterManager) manager;\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(BEFORE_MANAGERUNREGISTER_EVENT,manager);\r
- managers.remove(getManagerName(cmgr.getName(),manager));\r
- cmgr.setCluster(null);\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(AFTER_MANAGERUNREGISTER_EVENT, manager);\r
- }\r
- }\r
-\r
- /**\r
- * @param name\r
- * @param manager\r
- * @return\r
- */\r
- public String getManagerName(String name, Manager manager) {\r
- String clusterName = name ;\r
- if ( clusterName == null ) clusterName = manager.getContainer().getName();\r
- if(getContainer() instanceof Engine) {\r
- Container context = manager.getContainer() ;\r
- if(context != null && context instanceof Context) {\r
- Container host = ((Context)context).getParent();\r
- if(host != null && host instanceof Host && clusterName!=null && !(clusterName.indexOf("#")>=0))\r
- clusterName = host.getName() +"#" + clusterName ;\r
- }\r
- }\r
- return clusterName;\r
- }\r
-\r
- /*\r
- * Get Manager\r
- * \r
- * @see org.apache.catalina.ha.CatalinaCluster#getManager(java.lang.String)\r
- */\r
- public Manager getManager(String name) {\r
- return (Manager) managers.get(name);\r
- }\r
- \r
- // ------------------------------------------------------ Lifecycle Methods\r
-\r
- /**\r
- * Execute a periodic task, such as reloading, etc. This method will be\r
- * invoked inside the classloading context of this container. Unexpected\r
- * throwables will be caught and logged.\r
- * @see org.apache.catalina.ha.deploy.FarmWarDeployer#backgroundProcess()\r
- * @see ReplicationTransmitter#backgroundProcess()\r
- */\r
- public void backgroundProcess() {\r
- if (clusterDeployer != null) clusterDeployer.backgroundProcess();\r
- //send a heartbeat through the channel\r
- if ( channel !=null ) channel.heartbeat();\r
- }\r
-\r
- /**\r
- * Add a lifecycle event listener to this component.\r
- * \r
- * @param listener\r
- * The listener to add\r
- */\r
- public void addLifecycleListener(LifecycleListener listener) {\r
- lifecycle.addLifecycleListener(listener);\r
- }\r
-\r
- /**\r
- * Get the lifecycle listeners associated with this lifecycle. If this\r
- * Lifecycle has no listeners registered, a zero-length array is returned.\r
- */\r
- public LifecycleListener[] findLifecycleListeners() {\r
-\r
- return lifecycle.findLifecycleListeners();\r
-\r
- }\r
-\r
- /**\r
- * Remove a lifecycle event listener from this component.\r
- * \r
- * @param listener\r
- * The listener to remove\r
- */\r
- public void removeLifecycleListener(LifecycleListener listener) {\r
- lifecycle.removeLifecycleListener(listener);\r
- }\r
-\r
- /**\r
- * Use as base to handle start/stop/periodic Events from host. Currently\r
- * only log the messages as trace level.\r
- * \r
- * @see org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)\r
- */\r
- public void lifecycleEvent(LifecycleEvent lifecycleEvent) {\r
- if (log.isTraceEnabled())\r
- log.trace(sm.getString("SimpleTcpCluster.event.log", lifecycleEvent.getType(), lifecycleEvent.getData()));\r
- }\r
-\r
- // ------------------------------------------------------ public\r
-\r
- /**\r
- * Prepare for the beginning of active use of the public methods of this\r
- * component. This method should be called after <code>configure()</code>,\r
- * and before any of the public methods of the component are utilized. <BR>\r
- * Starts the cluster communication channel, this will connect with the\r
- * other nodes in the cluster, and request the current session state to be\r
- * transferred to this node.\r
- * \r
- * @exception IllegalStateException\r
- * if this component has already been started\r
- * @exception LifecycleException\r
- * if this component detects a fatal error that prevents this\r
- * component from being used\r
- */\r
- public void start() throws LifecycleException {\r
- if (started)\r
- throw new LifecycleException(sm.getString("cluster.alreadyStarted"));\r
- if (log.isInfoEnabled()) log.info("Cluster is about to start");\r
-\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, this);\r
- try {\r
- checkDefaults();\r
- registerClusterValve();\r
- channel.addMembershipListener(this);\r
- channel.addChannelListener(this);\r
- channel.start(channel.DEFAULT);\r
- if (clusterDeployer != null) clusterDeployer.start();\r
- this.started = true;\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(AFTER_START_EVENT, this);\r
- } catch (Exception x) {\r
- log.error("Unable to start cluster.", x);\r
- throw new LifecycleException(x);\r
- }\r
- }\r
-\r
- protected void checkDefaults() {\r
- if ( clusterListeners.size() == 0 ) {\r
- addClusterListener(new JvmRouteSessionIDBinderListener()); \r
- addClusterListener(new ClusterSessionListener());\r
- }\r
- if ( valves.size() == 0 ) {\r
- addValve(new JvmRouteBinderValve());\r
- addValve(new ReplicationValve());\r
- }\r
- if ( clusterDeployer != null ) clusterDeployer.setCluster(this);\r
- if ( channel == null ) channel = new GroupChannel();\r
- if ( channel instanceof GroupChannel && !((GroupChannel)channel).getInterceptors().hasNext()) {\r
- channel.addInterceptor(new MessageDispatch15Interceptor());\r
- channel.addInterceptor(new TcpFailureDetector());\r
- }\r
- }\r
-\r
- /**\r
- * register all cluster valve to host or engine\r
- * @throws Exception\r
- * @throws ClassNotFoundException\r
- */\r
- protected void registerClusterValve() throws Exception {\r
- if(container != null ) {\r
- for (Iterator iter = valves.iterator(); iter.hasNext();) {\r
- ClusterValve valve = (ClusterValve) iter.next();\r
- if (log.isDebugEnabled())\r
- log.debug("Invoking addValve on " + getContainer()\r
- + " with class=" + valve.getClass().getName());\r
- if (valve != null) {\r
- IntrospectionUtils.callMethodN(getContainer(), "addValve",\r
- new Object[] { valve },\r
- new Class[] { org.apache.catalina.Valve.class });\r
-\r
- }\r
- valve.setCluster(this);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * unregister all cluster valve to host or engine\r
- * @throws Exception\r
- * @throws ClassNotFoundException\r
- */\r
- protected void unregisterClusterValve() throws Exception {\r
- for (Iterator iter = valves.iterator(); iter.hasNext();) {\r
- ClusterValve valve = (ClusterValve) iter.next();\r
- if (log.isDebugEnabled())\r
- log.debug("Invoking removeValve on " + getContainer()\r
- + " with class=" + valve.getClass().getName());\r
- if (valve != null) {\r
- IntrospectionUtils.callMethodN(getContainer(), "removeValve",\r
- new Object[] { valve }, new Class[] { org.apache.catalina.Valve.class });\r
- }\r
- valve.setCluster(this);\r
- }\r
- }\r
-\r
- /**\r
- * Gracefully terminate the active cluster component.<br/>\r
- * This will disconnect the cluster communication channel, stop the\r
- * listener and deregister the valves from host or engine.<br/><br/>\r
- * <b>Note:</b><br/>The sub elements receiver, sender, membership,\r
- * listener or valves are not removed. You can easily start the cluster again.\r
- * \r
- * @exception IllegalStateException\r
- * if this component has not been started\r
- * @exception LifecycleException\r
- * if this component detects a fatal error that needs to be\r
- * reported\r
- */\r
- public void stop() throws LifecycleException {\r
-\r
- if (!started)\r
- throw new IllegalStateException(sm.getString("cluster.notStarted"));\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, this);\r
-\r
- if (clusterDeployer != null) clusterDeployer.stop();\r
- this.managers.clear();\r
- try {\r
- if ( clusterDeployer != null ) clusterDeployer.setCluster(null);\r
- channel.stop(Channel.DEFAULT);\r
- channel.removeChannelListener(this);\r
- channel.removeMembershipListener(this);\r
- this.unregisterClusterValve();\r
- } catch (Exception x) {\r
- log.error("Unable to stop cluster valve.", x);\r
- }\r
- started = false;\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, this);\r
- }\r
-\r
- \r
-\r
-\r
- /**\r
- * send message to all cluster members\r
- * @param msg message to transfer\r
- * \r
- * @see org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage)\r
- */\r
- public void send(ClusterMessage msg) {\r
- send(msg, null);\r
- }\r
-\r
- /**\r
- * send message to all cluster members same cluster domain\r
- * \r
- * @see org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage)\r
- */\r
- public void sendClusterDomain(ClusterMessage msg) {\r
- send(msg,null);\r
- } \r
-\r
- \r
- /**\r
- * send a cluster message to one member\r
- * \r
- * @param msg message to transfer\r
- * @param dest Receiver member\r
- * @see org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage,\r
- * org.apache.catalina.ha.Member)\r
- */\r
- public void send(ClusterMessage msg, Member dest) {\r
- try {\r
- msg.setAddress(getLocalMember());\r
- if (dest != null) {\r
- if (!getLocalMember().equals(dest)) {\r
- channel.send(new Member[] {dest}, msg,channelSendOptions);\r
- } else\r
- log.error("Unable to send message to local member " + msg);\r
- } else {\r
- channel.send(channel.getMembers(),msg,channelSendOptions);\r
- }\r
- } catch (Exception x) {\r
- log.error("Unable to send message through cluster sender.", x);\r
- }\r
- }\r
-\r
- /**\r
- * New cluster member is registered\r
- * \r
- * @see org.apache.catalina.ha.MembershipListener#memberAdded(org.apache.catalina.ha.Member)\r
- */\r
- public void memberAdded(Member member) {\r
- try {\r
- hasMembers = channel.hasMembers();\r
- if (log.isInfoEnabled()) log.info("Replication member added:" + member);\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(BEFORE_MEMBERREGISTER_EVENT, member);\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(AFTER_MEMBERREGISTER_EVENT, member);\r
- } catch (Exception x) {\r
- log.error("Unable to connect to replication system.", x);\r
- }\r
-\r
- }\r
-\r
- /**\r
- * Cluster member is gone\r
- * \r
- * @see org.apache.catalina.ha.MembershipListener#memberDisappeared(org.apache.catalina.ha.Member)\r
- */\r
- public void memberDisappeared(Member member) {\r
- try {\r
- hasMembers = channel.hasMembers(); \r
- if (log.isInfoEnabled()) log.info("Received member disappeared:" + member);\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(BEFORE_MEMBERUNREGISTER_EVENT, member);\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(AFTER_MEMBERUNREGISTER_EVENT, member);\r
- } catch (Exception x) {\r
- log.error("Unable remove cluster node from replication system.", x);\r
- }\r
- }\r
-\r
- // --------------------------------------------------------- receiver\r
- // messages\r
-\r
- /**\r
- * notify all listeners from receiving a new message is not ClusterMessage\r
- * emitt Failure Event to LifecylceListener\r
- * \r
- * @param message\r
- * receveived Message\r
- */\r
- public boolean accept(Serializable msg, Member sender) {\r
- return (msg instanceof ClusterMessage);\r
- }\r
- \r
- \r
- public void messageReceived(Serializable message, Member sender) {\r
- ClusterMessage fwd = (ClusterMessage)message;\r
- fwd.setAddress(sender);\r
- messageReceived(fwd);\r
- }\r
-\r
- public void messageReceived(ClusterMessage message) {\r
-\r
- long start = 0;\r
- if (log.isDebugEnabled() && message != null)\r
- log.debug("Assuming clocks are synched: Replication for "\r
- + message.getUniqueId() + " took="\r
- + (System.currentTimeMillis() - (message).getTimestamp())\r
- + " ms.");\r
-\r
- //invoke all the listeners\r
- boolean accepted = false;\r
- if (message != null) {\r
- for (Iterator iter = clusterListeners.iterator(); iter.hasNext();) {\r
- ClusterListener listener = (ClusterListener) iter.next();\r
- if (listener.accept(message)) {\r
- accepted = true;\r
- listener.messageReceived(message);\r
- }\r
- }\r
- }\r
- if (!accepted && log.isDebugEnabled()) {\r
- if (notifyLifecycleListenerOnFailure) {\r
- Member dest = message.getAddress();\r
- // Notify our interested LifecycleListeners\r
- lifecycle.fireLifecycleEvent(RECEIVE_MESSAGE_FAILURE_EVENT,\r
- new SendMessageData(message, dest, null));\r
- }\r
- log.debug("Message " + message.toString() + " from type "\r
- + message.getClass().getName()\r
- + " transfered but no listener registered");\r
- }\r
- return;\r
- }\r
-\r
- // --------------------------------------------------------- Logger\r
-\r
- public Log getLogger() {\r
- return log;\r
- }\r
-\r
-\r
-\r
- \r
- // ------------------------------------------------------------- deprecated\r
-\r
- /**\r
- * \r
- * @see org.apache.catalina.Cluster#setProtocol(java.lang.String)\r
- */\r
- public void setProtocol(String protocol) {\r
- }\r
-\r
- /**\r
- * @see org.apache.catalina.Cluster#getProtocol()\r
- */\r
- public String getProtocol() {\r
- return null;\r
- }\r
-\r
- /**\r
- * @see org.apache.catalina.Cluster#startContext(java.lang.String)\r
- */\r
- public void startContext(String contextPath) throws IOException {\r
- \r
- }\r
-\r
- /**\r
- * @see org.apache.catalina.Cluster#installContext(java.lang.String, java.net.URL)\r
- */\r
- public void installContext(String contextPath, URL war) {\r
- \r
- }\r
-\r
- /**\r
- * @see org.apache.catalina.Cluster#stop(java.lang.String)\r
- */\r
- public void stop(String contextPath) throws IOException {\r
- \r
- }\r
-}\r
+/*
+ * 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.apache.catalina.ha.tcp;
+
+import java.beans.PropertyChangeSupport;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+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.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Valve;
+import org.apache.catalina.ha.CatalinaCluster;
+import org.apache.catalina.ha.ClusterListener;
+import org.apache.catalina.ha.ClusterManager;
+import org.apache.catalina.ha.ClusterMessage;
+import org.apache.catalina.ha.ClusterValve;
+import org.apache.catalina.ha.session.DeltaManager;
+import org.apache.catalina.ha.util.IDynamicProperty;
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelListener;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipListener;
+import org.apache.catalina.tribes.group.GroupChannel;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.catalina.ha.session.ClusterSessionListener;
+import org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor;
+import org.apache.catalina.tribes.group.interceptors.TcpFailureDetector;
+import org.apache.catalina.ha.session.JvmRouteBinderValve;
+import org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener;
+
+/**
+ * A <b>Cluster </b> implementation using simple multicast. Responsible for
+ * setting up a cluster and provides callers with a valid multicast
+ * receiver/sender.
+ *
+ * FIXME remove install/remove/start/stop context dummys
+ * FIXME wrote testcases
+ *
+ * @author Filip Hanik
+ * @author Remy Maucherat
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public class SimpleTcpCluster
+ implements CatalinaCluster, Lifecycle, LifecycleListener, IDynamicProperty,
+ MembershipListener, ChannelListener{
+
+ public static Log log = LogFactory.getLog(SimpleTcpCluster.class);
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * Descriptive information about this component implementation.
+ */
+ protected static final String info = "SimpleTcpCluster/2.2";
+
+ public static final String BEFORE_MEMBERREGISTER_EVENT = "before_member_register";
+
+ public static final String AFTER_MEMBERREGISTER_EVENT = "after_member_register";
+
+ public static final String BEFORE_MANAGERREGISTER_EVENT = "before_manager_register";
+
+ public static final String AFTER_MANAGERREGISTER_EVENT = "after_manager_register";
+
+ public static final String BEFORE_MANAGERUNREGISTER_EVENT = "before_manager_unregister";
+
+ public static final String AFTER_MANAGERUNREGISTER_EVENT = "after_manager_unregister";
+
+ public static final String BEFORE_MEMBERUNREGISTER_EVENT = "before_member_unregister";
+
+ public static final String AFTER_MEMBERUNREGISTER_EVENT = "after_member_unregister";
+
+ public static final String SEND_MESSAGE_FAILURE_EVENT = "send_message_failure";
+
+ public static final String RECEIVE_MESSAGE_FAILURE_EVENT = "receive_message_failure";
+
+ /**
+ * Group channel.
+ */
+ protected Channel channel = new GroupChannel();
+
+
+ /**
+ * Name for logging purpose
+ */
+ protected String clusterImpName = "SimpleTcpCluster";
+
+ /**
+ * The string manager for this package.
+ */
+ protected StringManager sm = StringManager.getManager(Constants.Package);
+
+ /**
+ * The cluster name to join
+ */
+ protected String clusterName ;
+
+ /**
+ * The Container associated with this Cluster.
+ */
+ protected Container container = null;
+
+ /**
+ * The lifecycle event support for this component.
+ */
+ protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /**
+ * Has this component been started?
+ */
+ protected boolean started = false;
+
+ /**
+ * The property change support for this component.
+ */
+ protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+ /**
+ * The context name <->manager association for distributed contexts.
+ */
+ protected Map managers = new HashMap();
+
+ protected ClusterManager managerTemplate = new DeltaManager();
+
+ private List valves = new ArrayList();
+
+ private org.apache.catalina.ha.ClusterDeployer clusterDeployer;
+
+ /**
+ * Listeners of messages
+ */
+ protected List clusterListeners = new ArrayList();
+
+ /**
+ * Comment for <code>notifyLifecycleListenerOnFailure</code>
+ */
+ private boolean notifyLifecycleListenerOnFailure = false;
+
+ /**
+ * dynamic sender <code>properties</code>
+ */
+ private Map properties = new HashMap();
+
+ private int channelSendOptions = Channel.SEND_OPTIONS_ASYNCHRONOUS;
+
+ // ------------------------------------------------------------- Properties
+
+ public SimpleTcpCluster() {
+ }
+
+ /**
+ * Return descriptive information about this Cluster implementation and the
+ * corresponding version number, in the format
+ * <code><description>/<version></code>.
+ */
+ public String getInfo() {
+ return (info);
+ }
+
+ /**
+ * Set the name of the cluster to join, if no cluster with this name is
+ * present create one.
+ *
+ * @param clusterName
+ * The clustername to join
+ */
+ public void setClusterName(String clusterName) {
+ this.clusterName = clusterName;
+ }
+
+ /**
+ * Return the name of the cluster that this Server is currently configured
+ * to operate within.
+ *
+ * @return The name of the cluster associated with this server
+ */
+ public String getClusterName() {
+ if(clusterName == null && container != null)
+ return container.getName() ;
+ return clusterName;
+ }
+
+ /**
+ * Set the Container associated with our Cluster
+ *
+ * @param container
+ * The Container to use
+ */
+ public void setContainer(Container container) {
+ Container oldContainer = this.container;
+ this.container = container;
+ support.firePropertyChange("container", oldContainer, this.container);
+ }
+
+ /**
+ * Get the Container associated with our Cluster
+ *
+ * @return The Container associated with our Cluster
+ */
+ public Container getContainer() {
+ return (this.container);
+ }
+
+ /**
+ * @return Returns the notifyLifecycleListenerOnFailure.
+ */
+ public boolean isNotifyLifecycleListenerOnFailure() {
+ return notifyLifecycleListenerOnFailure;
+ }
+
+ /**
+ * @param notifyListenerOnFailure
+ * The notifyLifecycleListenerOnFailure to set.
+ */
+ public void setNotifyLifecycleListenerOnFailure(
+ boolean notifyListenerOnFailure) {
+ boolean oldNotifyListenerOnFailure = this.notifyLifecycleListenerOnFailure;
+ this.notifyLifecycleListenerOnFailure = notifyListenerOnFailure;
+ support.firePropertyChange("notifyLifecycleListenerOnFailure",
+ oldNotifyListenerOnFailure,
+ this.notifyLifecycleListenerOnFailure);
+ }
+
+ /**
+ * @deprecated use getManagerTemplate().getClass().getName() instead.
+ * @return String
+ */
+ public String getManagerClassName() {
+ return managerTemplate.getClass().getName();
+ }
+
+ /**
+ * @deprecated use nested <Manager> element inside the cluster config instead.
+ * @param managerClassName String
+ */
+ public void setManagerClassName(String managerClassName) {
+ log.warn("setManagerClassName is deprecated, use nested <Manager> element inside the <Cluster> element instead, this request will be ignored.");
+ }
+
+ /**
+ * Add cluster valve
+ * Cluster Valves are only add to container when cluster is started!
+ * @param valve The new cluster Valve.
+ */
+ public void addValve(Valve valve) {
+ if (valve instanceof ClusterValve && (!valves.contains(valve)))
+ valves.add(valve);
+ }
+
+ /**
+ * get all cluster valves
+ * @return current cluster valves
+ */
+ public Valve[] getValves() {
+ return (Valve[]) valves.toArray(new Valve[valves.size()]);
+ }
+
+ /**
+ * Get the cluster listeners associated with this cluster. If this Array has
+ * no listeners registered, a zero-length array is returned.
+ */
+ public ClusterListener[] findClusterListeners() {
+ if (clusterListeners.size() > 0) {
+ ClusterListener[] listener = new ClusterListener[clusterListeners.size()];
+ clusterListeners.toArray(listener);
+ return listener;
+ } else
+ return new ClusterListener[0];
+
+ }
+
+ /**
+ * add cluster message listener and register cluster to this listener
+ *
+ * @see org.apache.catalina.ha.CatalinaCluster#addClusterListener(org.apache.catalina.ha.MessageListener)
+ */
+ public void addClusterListener(ClusterListener listener) {
+ if (listener != null && !clusterListeners.contains(listener)) {
+ clusterListeners.add(listener);
+ listener.setCluster(this);
+ }
+ }
+
+ /**
+ * remove message listener and deregister Cluster from listener
+ *
+ * @see org.apache.catalina.ha.CatalinaCluster#removeClusterListener(org.apache.catalina.ha.MessageListener)
+ */
+ public void removeClusterListener(ClusterListener listener) {
+ if (listener != null) {
+ clusterListeners.remove(listener);
+ listener.setCluster(null);
+ }
+ }
+
+ /**
+ * get current Deployer
+ */
+ public org.apache.catalina.ha.ClusterDeployer getClusterDeployer() {
+ return clusterDeployer;
+ }
+
+ /**
+ * set a new Deployer, must be set before cluster started!
+ */
+ public void setClusterDeployer(
+ org.apache.catalina.ha.ClusterDeployer clusterDeployer) {
+ this.clusterDeployer = clusterDeployer;
+ }
+
+ public void setChannel(Channel channel) {
+ this.channel = channel;
+ }
+
+ public void setManagerTemplate(ClusterManager managerTemplate) {
+ this.managerTemplate = managerTemplate;
+ }
+
+ public void setChannelSendOptions(int channelSendOptions) {
+ this.channelSendOptions = channelSendOptions;
+ }
+
+ /**
+ * has members
+ */
+ protected boolean hasMembers = false;
+ public boolean hasMembers() {
+ return hasMembers;
+ }
+
+ /**
+ * Get all current cluster members
+ * @return all members or empty array
+ */
+ public Member[] getMembers() {
+ return channel.getMembers();
+ }
+
+ /**
+ * Return the member that represents this node.
+ *
+ * @return Member
+ */
+ public Member getLocalMember() {
+ return channel.getLocalMember(true);
+ }
+
+ // ------------------------------------------------------------- dynamic
+ // manager property handling
+
+ /**
+ * JMX hack to direct use at jconsole
+ *
+ * @param name
+ * @param value
+ */
+ public void setProperty(String name, String value) {
+ setProperty(name, (Object) value);
+ }
+
+ /**
+ * set config attributes with reflect and propagate to all managers
+ *
+ * @param name
+ * @param value
+ */
+ public void setProperty(String name, Object value) {
+ if (log.isTraceEnabled())
+ log.trace(sm.getString("SimpleTcpCluster.setProperty", name, value,properties.get(name)));
+ properties.put(name, value);
+ //using a dynamic way of setting properties is nice, but a security risk
+ //if exposed through JMX. This way you can sit and try to guess property names,
+ //we will only allow explicit property names
+ log.warn("Dynamic setProperty("+name+",value) has been disabled, please use explicit properties for the element you are trying to identify");
+ if(started) {
+ // FIXME Hmm, is that correct when some DeltaManagers are direct configured inside Context?
+ // Why we not support it for other elements, like sender, receiver or membership?
+ // Must we restart element after change?
+// if (name.startsWith("manager")) {
+// String key = name.substring("manager".length() + 1);
+// String pvalue = value.toString();
+// for (Iterator iter = managers.values().iterator(); iter.hasNext();) {
+// Manager manager = (Manager) iter.next();
+// if(manager instanceof DeltaManager && ((ClusterManager) manager).isDefaultMode()) {
+// IntrospectionUtils.setProperty(manager, key, pvalue );
+// }
+// }
+// }
+ }
+ }
+
+ /**
+ * get current config
+ *
+ * @param key
+ * @return The property
+ */
+ public Object getProperty(String key) {
+ if (log.isTraceEnabled())
+ log.trace(sm.getString("SimpleTcpCluster.getProperty", key));
+ return properties.get(key);
+ }
+
+ /**
+ * Get all properties keys
+ *
+ * @return An iterator over the property names.
+ */
+ public Iterator getPropertyNames() {
+ return properties.keySet().iterator();
+ }
+
+ /**
+ * remove a configured property.
+ *
+ * @param key
+ */
+ public void removeProperty(String key) {
+ properties.remove(key);
+ }
+
+ /**
+ * transfer properties from cluster configuration to subelement bean.
+ * @param prefix
+ * @param bean
+ */
+ protected void transferProperty(String prefix, Object bean) {
+ if (prefix != null) {
+ for (Iterator iter = getPropertyNames(); iter.hasNext();) {
+ String pkey = (String) iter.next();
+ if (pkey.startsWith(prefix)) {
+ String key = pkey.substring(prefix.length() + 1);
+ Object value = getProperty(pkey);
+ IntrospectionUtils.setProperty(bean, key, value.toString());
+ }
+ }
+ }
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * @return Returns the managers.
+ */
+ public Map getManagers() {
+ return managers;
+ }
+
+ public Channel getChannel() {
+ return channel;
+ }
+
+ public ClusterManager getManagerTemplate() {
+ return managerTemplate;
+ }
+
+ public int getChannelSendOptions() {
+ return channelSendOptions;
+ }
+
+ /**
+ * Create new Manager without add to cluster (comes with start the manager)
+ *
+ * @param name
+ * Context Name of this manager
+ * @see org.apache.catalina.Cluster#createManager(java.lang.String)
+ * @see #addManager(String, Manager)
+ * @see DeltaManager#start()
+ */
+ public synchronized Manager createManager(String name) {
+ if (log.isDebugEnabled()) log.debug("Creating ClusterManager for context " + name + " using class " + getManagerClassName());
+ Manager manager = null;
+ try {
+ manager = managerTemplate.cloneFromTemplate();
+ ((ClusterManager)manager).setName(name);
+ } catch (Exception x) {
+ log.error("Unable to clone cluster manager, defaulting to org.apache.catalina.ha.session.DeltaManager", x);
+ manager = new org.apache.catalina.ha.session.DeltaManager();
+ } finally {
+ if ( manager != null && (manager instanceof ClusterManager)) ((ClusterManager)manager).setCluster(this);
+ }
+ return manager;
+ }
+
+ public void registerManager(Manager manager) {
+
+ if (! (manager instanceof ClusterManager)) {
+ log.warn("Manager [ " + manager + "] does not implement ClusterManager, addition to cluster has been aborted.");
+ return;
+ }
+ ClusterManager cmanager = (ClusterManager) manager ;
+ cmanager.setDistributable(true);
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_MANAGERREGISTER_EVENT, manager);
+ String clusterName = getManagerName(cmanager.getName(), manager);
+ cmanager.setName(clusterName);
+ cmanager.setCluster(this);
+ cmanager.setDefaultMode(false);
+
+ managers.put(clusterName, manager);
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_MANAGERREGISTER_EVENT, manager);
+ }
+
+ /**
+ * remove an application form cluster replication bus
+ *
+ * @see org.apache.catalina.ha.CatalinaCluster#removeManager(java.lang.String,Manager)
+ */
+ public void removeManager(Manager manager) {
+ if (manager != null && manager instanceof ClusterManager ) {
+ ClusterManager cmgr = (ClusterManager) manager;
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_MANAGERUNREGISTER_EVENT,manager);
+ managers.remove(getManagerName(cmgr.getName(),manager));
+ cmgr.setCluster(null);
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_MANAGERUNREGISTER_EVENT, manager);
+ }
+ }
+
+ /**
+ * @param name
+ * @param manager
+ * @return
+ */
+ public String getManagerName(String name, Manager manager) {
+ String clusterName = name ;
+ if ( clusterName == null ) clusterName = manager.getContainer().getName();
+ if(getContainer() instanceof Engine) {
+ Container context = manager.getContainer() ;
+ if(context != null && context instanceof Context) {
+ Container host = ((Context)context).getParent();
+ if(host != null && host instanceof Host && clusterName!=null && !(clusterName.indexOf("#")>=0))
+ clusterName = host.getName() +"#" + clusterName ;
+ }
+ }
+ return clusterName;
+ }
+
+ /*
+ * Get Manager
+ *
+ * @see org.apache.catalina.ha.CatalinaCluster#getManager(java.lang.String)
+ */
+ public Manager getManager(String name) {
+ return (Manager) managers.get(name);
+ }
+
+ // ------------------------------------------------------ Lifecycle Methods
+
+ /**
+ * Execute a periodic task, such as reloading, etc. This method will be
+ * invoked inside the classloading context of this container. Unexpected
+ * throwables will be caught and logged.
+ * @see org.apache.catalina.ha.deploy.FarmWarDeployer#backgroundProcess()
+ * @see ReplicationTransmitter#backgroundProcess()
+ */
+ public void backgroundProcess() {
+ if (clusterDeployer != null) clusterDeployer.backgroundProcess();
+ //send a heartbeat through the channel
+ if ( channel !=null ) channel.heartbeat();
+ }
+
+ /**
+ * Add a lifecycle event listener to this component.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public void addLifecycleListener(LifecycleListener listener) {
+ lifecycle.addLifecycleListener(listener);
+ }
+
+ /**
+ * Get the lifecycle listeners associated with this lifecycle. If this
+ * Lifecycle has no listeners registered, a zero-length array is returned.
+ */
+ public LifecycleListener[] findLifecycleListeners() {
+
+ return lifecycle.findLifecycleListeners();
+
+ }
+
+ /**
+ * Remove a lifecycle event listener from this component.
+ *
+ * @param listener
+ * The listener to remove
+ */
+ public void removeLifecycleListener(LifecycleListener listener) {
+ lifecycle.removeLifecycleListener(listener);
+ }
+
+ /**
+ * Use as base to handle start/stop/periodic Events from host. Currently
+ * only log the messages as trace level.
+ *
+ * @see org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)
+ */
+ public void lifecycleEvent(LifecycleEvent lifecycleEvent) {
+ if (log.isTraceEnabled())
+ log.trace(sm.getString("SimpleTcpCluster.event.log", lifecycleEvent.getType(), lifecycleEvent.getData()));
+ }
+
+ // ------------------------------------------------------ public
+
+ /**
+ * Prepare for the beginning of active use of the public methods of this
+ * component. This method should be called after <code>configure()</code>,
+ * and before any of the public methods of the component are utilized. <BR>
+ * Starts the cluster communication channel, this will connect with the
+ * other nodes in the cluster, and request the current session state to be
+ * transferred to this node.
+ *
+ * @exception IllegalStateException
+ * if this component has already been started
+ * @exception LifecycleException
+ * if this component detects a fatal error that prevents this
+ * component from being used
+ */
+ public void start() throws LifecycleException {
+ if (started)
+ throw new LifecycleException(sm.getString("cluster.alreadyStarted"));
+ if (log.isInfoEnabled()) log.info("Cluster is about to start");
+
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, this);
+ try {
+ checkDefaults();
+ registerClusterValve();
+ channel.addMembershipListener(this);
+ channel.addChannelListener(this);
+ channel.start(channel.DEFAULT);
+ if (clusterDeployer != null) clusterDeployer.start();
+ this.started = true;
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_START_EVENT, this);
+ } catch (Exception x) {
+ log.error("Unable to start cluster.", x);
+ throw new LifecycleException(x);
+ }
+ }
+
+ protected void checkDefaults() {
+ if ( clusterListeners.size() == 0 ) {
+ addClusterListener(new JvmRouteSessionIDBinderListener());
+ addClusterListener(new ClusterSessionListener());
+ }
+ if ( valves.size() == 0 ) {
+ addValve(new JvmRouteBinderValve());
+ addValve(new ReplicationValve());
+ }
+ if ( clusterDeployer != null ) clusterDeployer.setCluster(this);
+ if ( channel == null ) channel = new GroupChannel();
+ if ( channel instanceof GroupChannel && !((GroupChannel)channel).getInterceptors().hasNext()) {
+ channel.addInterceptor(new MessageDispatch15Interceptor());
+ channel.addInterceptor(new TcpFailureDetector());
+ }
+ }
+
+ /**
+ * register all cluster valve to host or engine
+ * @throws Exception
+ * @throws ClassNotFoundException
+ */
+ protected void registerClusterValve() throws Exception {
+ if(container != null ) {
+ for (Iterator iter = valves.iterator(); iter.hasNext();) {
+ ClusterValve valve = (ClusterValve) iter.next();
+ if (log.isDebugEnabled())
+ log.debug("Invoking addValve on " + getContainer()
+ + " with class=" + valve.getClass().getName());
+ if (valve != null) {
+ IntrospectionUtils.callMethodN(getContainer(), "addValve",
+ new Object[] { valve },
+ new Class[] { org.apache.catalina.Valve.class });
+
+ }
+ valve.setCluster(this);
+ }
+ }
+ }
+
+ /**
+ * unregister all cluster valve to host or engine
+ * @throws Exception
+ * @throws ClassNotFoundException
+ */
+ protected void unregisterClusterValve() throws Exception {
+ for (Iterator iter = valves.iterator(); iter.hasNext();) {
+ ClusterValve valve = (ClusterValve) iter.next();
+ if (log.isDebugEnabled())
+ log.debug("Invoking removeValve on " + getContainer()
+ + " with class=" + valve.getClass().getName());
+ if (valve != null) {
+ IntrospectionUtils.callMethodN(getContainer(), "removeValve",
+ new Object[] { valve }, new Class[] { org.apache.catalina.Valve.class });
+ }
+ valve.setCluster(this);
+ }
+ }
+
+ /**
+ * Gracefully terminate the active cluster component.<br/>
+ * This will disconnect the cluster communication channel, stop the
+ * listener and deregister the valves from host or engine.<br/><br/>
+ * <b>Note:</b><br/>The sub elements receiver, sender, membership,
+ * listener or valves are not removed. You can easily start the cluster again.
+ *
+ * @exception IllegalStateException
+ * if this component has not been started
+ * @exception LifecycleException
+ * if this component detects a fatal error that needs to be
+ * reported
+ */
+ public void stop() throws LifecycleException {
+
+ if (!started)
+ throw new IllegalStateException(sm.getString("cluster.notStarted"));
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+
+ if (clusterDeployer != null) clusterDeployer.stop();
+ this.managers.clear();
+ try {
+ if ( clusterDeployer != null ) clusterDeployer.setCluster(null);
+ channel.stop(Channel.DEFAULT);
+ channel.removeChannelListener(this);
+ channel.removeMembershipListener(this);
+ this.unregisterClusterValve();
+ } catch (Exception x) {
+ log.error("Unable to stop cluster valve.", x);
+ }
+ started = false;
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+ }
+
+
+
+
+ /**
+ * send message to all cluster members
+ * @param msg message to transfer
+ *
+ * @see org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage)
+ */
+ public void send(ClusterMessage msg) {
+ send(msg, null);
+ }
+
+ /**
+ * send message to all cluster members same cluster domain
+ *
+ * @see org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage)
+ */
+ public void sendClusterDomain(ClusterMessage msg) {
+ send(msg,null);
+ }
+
+
+ /**
+ * send a cluster message to one member
+ *
+ * @param msg message to transfer
+ * @param dest Receiver member
+ * @see org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage,
+ * org.apache.catalina.ha.Member)
+ */
+ public void send(ClusterMessage msg, Member dest) {
+ try {
+ msg.setAddress(getLocalMember());
+ if (dest != null) {
+ if (!getLocalMember().equals(dest)) {
+ channel.send(new Member[] {dest}, msg,channelSendOptions);
+ } else
+ log.error("Unable to send message to local member " + msg);
+ } else {
+ channel.send(channel.getMembers(),msg,channelSendOptions);
+ }
+ } catch (Exception x) {
+ log.error("Unable to send message through cluster sender.", x);
+ }
+ }
+
+ /**
+ * New cluster member is registered
+ *
+ * @see org.apache.catalina.ha.MembershipListener#memberAdded(org.apache.catalina.ha.Member)
+ */
+ public void memberAdded(Member member) {
+ try {
+ hasMembers = channel.hasMembers();
+ if (log.isInfoEnabled()) log.info("Replication member added:" + member);
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_MEMBERREGISTER_EVENT, member);
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_MEMBERREGISTER_EVENT, member);
+ } catch (Exception x) {
+ log.error("Unable to connect to replication system.", x);
+ }
+
+ }
+
+ /**
+ * Cluster member is gone
+ *
+ * @see org.apache.catalina.ha.MembershipListener#memberDisappeared(org.apache.catalina.ha.Member)
+ */
+ public void memberDisappeared(Member member) {
+ try {
+ hasMembers = channel.hasMembers();
+ if (log.isInfoEnabled()) log.info("Received member disappeared:" + member);
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(BEFORE_MEMBERUNREGISTER_EVENT, member);
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(AFTER_MEMBERUNREGISTER_EVENT, member);
+ } catch (Exception x) {
+ log.error("Unable remove cluster node from replication system.", x);
+ }
+ }
+
+ // --------------------------------------------------------- receiver
+ // messages
+
+ /**
+ * notify all listeners from receiving a new message is not ClusterMessage
+ * emitt Failure Event to LifecylceListener
+ *
+ * @param message
+ * receveived Message
+ */
+ public boolean accept(Serializable msg, Member sender) {
+ return (msg instanceof ClusterMessage);
+ }
+
+
+ public void messageReceived(Serializable message, Member sender) {
+ ClusterMessage fwd = (ClusterMessage)message;
+ fwd.setAddress(sender);
+ messageReceived(fwd);
+ }
+
+ public void messageReceived(ClusterMessage message) {
+
+ long start = 0;
+ if (log.isDebugEnabled() && message != null)
+ log.debug("Assuming clocks are synched: Replication for "
+ + message.getUniqueId() + " took="
+ + (System.currentTimeMillis() - (message).getTimestamp())
+ + " ms.");
+
+ //invoke all the listeners
+ boolean accepted = false;
+ if (message != null) {
+ for (Iterator iter = clusterListeners.iterator(); iter.hasNext();) {
+ ClusterListener listener = (ClusterListener) iter.next();
+ if (listener.accept(message)) {
+ accepted = true;
+ listener.messageReceived(message);
+ }
+ }
+ }
+ if (!accepted && log.isDebugEnabled()) {
+ if (notifyLifecycleListenerOnFailure) {
+ Member dest = message.getAddress();
+ // Notify our interested LifecycleListeners
+ lifecycle.fireLifecycleEvent(RECEIVE_MESSAGE_FAILURE_EVENT,
+ new SendMessageData(message, dest, null));
+ }
+ log.debug("Message " + message.toString() + " from type "
+ + message.getClass().getName()
+ + " transfered but no listener registered");
+ }
+ return;
+ }
+
+ // --------------------------------------------------------- Logger
+
+ public Log getLogger() {
+ return log;
+ }
+
+
+
+
+ // ------------------------------------------------------------- deprecated
+
+ /**
+ *
+ * @see org.apache.catalina.Cluster#setProtocol(java.lang.String)
+ */
+ public void setProtocol(String protocol) {
+ }
+
+ /**
+ * @see org.apache.catalina.Cluster#getProtocol()
+ */
+ public String getProtocol() {
+ return null;
+ }
+
+ /**
+ * @see org.apache.catalina.Cluster#startContext(java.lang.String)
+ */
+ public void startContext(String contextPath) throws IOException {
+
+ }
+
+ /**
+ * @see org.apache.catalina.Cluster#installContext(java.lang.String, java.net.URL)
+ */
+ public void installContext(String contextPath, URL war) {
+
+ }
+
+ /**
+ * @see org.apache.catalina.Cluster#stop(java.lang.String)
+ */
+ public void stop(String contextPath) throws IOException {
+
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.ha.util;\r
-\r
-import java.util.Iterator;\r
-\r
-/**\r
- * @author Peter Rossbach\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-\r
-public interface IDynamicProperty {\r
-\r
- /**\r
- * set config attributes with reflect\r
- * \r
- * @param name\r
- * @param value\r
- */\r
- public void setProperty(String name, Object value) ;\r
-\r
- /**\r
- * get current config\r
- * \r
- * @param key\r
- * @return The property\r
- */\r
- public Object getProperty(String key) ;\r
- /**\r
- * Get all properties keys\r
- * \r
- * @return An iterator over the property names\r
- */\r
- public Iterator getPropertyNames() ;\r
-\r
- /**\r
- * remove a configured property.\r
- * \r
- * @param key\r
- */\r
- public void removeProperty(String key) ;\r
-\r
-}\r
+/*
+ * 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.apache.catalina.ha.util;
+
+import java.util.Iterator;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+
+public interface IDynamicProperty {
+
+ /**
+ * set config attributes with reflect
+ *
+ * @param name
+ * @param value
+ */
+ public void setProperty(String name, Object value) ;
+
+ /**
+ * get current config
+ *
+ * @param key
+ * @return The property
+ */
+ public Object getProperty(String key) ;
+ /**
+ * Get all properties keys
+ *
+ * @return An iterator over the property names
+ */
+ public Iterator getPropertyNames() ;
+
+ /**
+ * remove a configured property.
+ *
+ * @param key
+ */
+ public void removeProperty(String key) ;
+
+}
* the classloader associated with the context.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface Reloader {
* Resource entry.
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ResourceEntry {
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 303071 $ $Date: 2004-08-05 12:54:43 +0200 (jeu., 05 août 2004) $
+ * @version $Revision$ $Date$
*/
public class StandardClassLoader
* MBean interface for StandardClassLoader, to allow JMX remote management.
*
* @author Remy Maucherat
- * @version $Revision: 302740 $ $Date: 2004-03-02 13:32:19 +0100 (mar., 02 mars 2004) $
+ * @version $Revision$ $Date$
*/
public interface StandardClassLoaderMBean {
}
*
* @author Remy Maucherat
* @author Craig R. McClanahan
- * @version $Revision$ $Date: 2006-05-19 19:52:46 -0700 (Fri, 19 May 2006) $
+ * @version $Revision$ $Date$
*/
public class WebappClassLoader
extends URLClassLoader
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 303352 $ $Date: 2004-10-05 19:12:52 +0200 (mar., 05 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class WebappLoader
* @author Bip Thelin
* @author Malcolm Edgar
* @author Glenn L. Nielsen
-* @version $Revision: 326772 $, $Date: 2005-10-20 03:37:02 +0200 (jeu., 20 oct. 2005) $
+* @version $Revision$, $Date$
* @see ManagerServlet
*/
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 393613 $ $Date: 2006-04-12 23:08:01 +0200 (mer., 12 avr. 2006) $
+ * @version $Revision$ $Date$
*/
public class ManagerServlet
* This servlet will display a complete status of the HTTP/1.1 connector.
*
* @author Remy Maucherat
- * @version $Revision: 303870 $ $Date: 2005-04-19 00:50:24 +0200 (mar., 19 avr. 2005) $
+ * @version $Revision$ $Date$
*/
public class StatusManagerServlet
* use XSLT, that is unnecessarily complex.
*
* @author Peter Lin
- * @version $Revision: 303967 $ $Date: 2005-06-29 19:31:56 +0200 (mer., 29 juin 2005) $
+ * @version $Revision$ $Date$
*/
public class StatusTransformer {
* @author Malcolm Edgar
* @author Glenn L. Nielsen
* @author Peter Rossbach
-* @version $Revision: 384293 $, $Date: 2006-03-08 19:09:36 +0100 (mer., 08 mars 2006) $
+* @version $Revision$, $Date$
* @see ManagerServlet
*/
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 447499 $ $Date: 2006-09-18 20:47:54 +0200 (lun., 18 sept. 2006) $
+ * @version $Revision$ $Date$
*/
public class HostManagerServlet
* qualified class name of the managed object as its value.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ClassNameMBean extends BaseModelMBean {
* <code>org.apache.coyote.tomcat5.CoyoteConnector</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 303320 $ $Date: 2004-10-04 11:25:55 +0200 (lun., 04 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class ConnectorMBean extends ClassNameMBean {
* <code>org.apache.catalina.deploy.ContextEnvironment</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextEnvironmentMBean extends BaseModelMBean {
* <code>org.apache.catalina.deploy.ContextResourceLink</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextResourceLinkMBean extends BaseModelMBean {
* <code>org.apache.catalina.deploy.ContextResource</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 303032 $ $Date: 2004-07-26 18:04:02 +0200 (lun., 26 juil. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextResourceMBean extends BaseModelMBean {
* <code>org.apache.catalina.core.StandardDefaultContext</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public class DefaultContextMBean extends BaseModelMBean {
* management.
*
* @author Craig R. McClanahan
- * @version $Revision: 302983 $ $Date: 2004-06-26 01:56:25 +0200 (sam., 26 juin 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* <code>org.apache.catalina.Group</code> component.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302833 $ $Date: 2004-04-15 03:44:09 +0200 (jeu., 15 avr. 2004) $
+ * @version $Revision$ $Date$
*/
public class GroupMBean extends BaseModelMBean {
* <code>org.apache.catalina.core.StandardServer</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 304032 $ $Date: 2005-07-27 17:11:55 +0200 (mer., 27 juil. 2005) $
+ * @version $Revision$ $Date$
*/
public class MBeanFactory extends BaseModelMBean {
*
* @author Craig R. McClanahan
* @author Amy Roh
- * @version $Revision: 303558 $ $Date: 2004-12-01 12:17:34 +0100 (mer., 01 déc. 2004) $
+ * @version $Revision$ $Date$
*/
public class MBeanUtils {
* <code>org.apache.catalina.users.MemoryUserDatabase</code> component.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302833 $ $Date: 2004-04-15 03:44:09 +0200 (jeu., 15 avr. 2004) $
+ * @version $Revision$ $Date$
*/
public class MemoryUserDatabaseMBean extends BaseModelMBean {
* <code>org.apache.catalina.deploy.NamingResources</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public class NamingResourcesMBean extends BaseModelMBean {
* <code>org.apache.catalina.Role</code> component.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class RoleMBean extends BaseModelMBean {
*
* @author Craig R. McClanahan
* @author Amy Roh
- * @version $Revision: 303365 $ $Date: 2004-10-06 18:10:57 +0200 (mer., 06 oct. 2004) $
+ * @version $Revision$ $Date$
*/
public class ServerLifecycleListener
* <code>org.apache.catalina.core.StandardContext</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public class StandardContextMBean extends BaseModelMBean {
* <code>org.apache.catalina.core.StandardEngine</code> component.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class StandardEngineMBean extends BaseModelMBean {
* <code>org.apache.catalina.core.StandardHost</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class StandardHostMBean extends BaseModelMBean {
* <code>org.apache.catalina.core.StandardServer</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class StandardServerMBean extends BaseModelMBean {
* <code>org.apache.catalina.core.StandardService</code> component.</p>
*
* @author Amy Roh
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class StandardServiceMBean extends BaseModelMBean {
* <code>org.apache.catalina.User</code> component.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302833 $ $Date: 2004-04-15 03:44:09 +0200 (jeu., 15 avr. 2004) $
+ * @version $Revision$ $Date$
*/
public class UserMBean extends BaseModelMBean {
*
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class Constants {
* @author Craig R. McClanahan
* @author Carson McDonald
* @author Ignacio Ortega
-* @version $Revision: 394121 $
+* @version $Revision$
*/
public class DataSourceRealm
* is available for use by <code>Realm</code> implementations.
*
* @author Craig R. McClanahan
- * @version $Revision: 303827 $ $Date: 2005-04-01 13:36:52 +0200 (ven., 01 avr. 2005) $
+ * @version $Revision$ $Date$
*/
public class GenericPrincipal implements Principal {
*
* @author Craig R. McClanahan
* @author Andrew R. Jaquith
- * @version $Revision: 303719 $ $Date: 2005-02-19 00:43:20 +0100 (sam., 19 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public class JAASCallbackHandler implements CallbackHandler {
* the functionality required of a <code>Realm</code> implementation.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303719 $ $Date: 2005-02-19 00:43:20 +0100 (sam., 19 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule, Realm {
*
* @author Craig R. McClanahan
* @author Yoav Shapira
- * @version $Revision: 303827 $ $Date: 2005-04-01 13:36:52 +0200 (ven., 01 avr. 2005) $
+ * @version $Revision$ $Date$
*/
public class JAASRealm
* @author Craig R. McClanahan
* @author Carson McDonald
* @author Ignacio Ortega
-* @version $Revision: 373023 $ $Date: 2006-01-28 00:17:43 +0100 (sam., 28 janv. 2006) $
+* @version $Revision$ $Date$
*/
public class JDBCRealm
*
* @author John Holman
* @author Craig R. McClanahan
- * @version $Revision: 373029 $ $Date: 2006-01-28 00:27:16 +0100 (sam., 28 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class JNDIRealm extends RealmBase {
-# $Id: LocalStrings.properties 303678 2005-02-03 15:14:34Z remm $
+# $Id$
# language
-# $Id: LocalStrings_es.properties 302991 2004-07-03 04:16:41Z billbarker $
+# $Id$
# language es
-# $Id: LocalStrings_fr.properties 302991 2004-07-03 04:16:41Z billbarker $
+# $Id$
# language
-# $Id: LocalStrings_ja.properties 302991 2004-07-03 04:16:41Z billbarker $
+# $Id$
# language ja
* synchronization is performed around accesses to the principals collection.
*
* @author Craig R. McClanahan
- * @version $Revision: 373023 $ $Date: 2006-01-28 00:17:43 +0100 (sam., 28 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class MemoryRealm extends RealmBase {
* XML file processed by <code>MemoryRealm</code>.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302984 $ $Date: 2004-06-26 19:41:33 +0200 (sam., 26 juin 2004) $
+ * @version $Revision$ $Date$
*/
public class MemoryRuleSet extends RuleSetBase {
* location) are identical to those currently supported by Tomcat 3.X.
*
* @author Craig R. McClanahan
- * @version $Revision: 388949 $ $Date: 2006-03-26 21:55:03 +0200 (dim., 26 mars 2006) $
+ * @version $Revision$ $Date$
*/
public abstract class RealmBase
* of <code>UserDatabase</code> that we should consult.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 373023 $ $Date: 2006-01-28 00:17:43 +0100 (sam., 28 janv. 2006) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
*
* @author Glenn L. Nielsen
* @author Jean-Francois Arcand
- * @version $Revision: 304042 $ $Date: 2005-08-04 08:07:46 +0200 (jeu., 04 août 2005) $
+ * @version $Revision$ $Date$
*/
public final class SecurityClassLoad {
*
* @author Martin T Dengler [root@martindengler.com]
* @author Amy Roh
- * @version $Revision: 421476 $, $Date: 2006-07-12 22:08:28 -0400 (Wed, 12 Jul 2006) $
+ * @version $Revision$, $Date$
* @since Tomcat 4.0
*
*/
* <p>
* </p>
*
- * @version $Revision: 421476 $, $Date: 2006-07-12 22:08:28 -0400 (Wed, 12 Jul 2006) $
+ * @version $Revision$, $Date$
* @since Tomcat 4.0
*
*/
* and <code>setResponse</code> methods, respectively.
* </p>
*
- * @version $Revision: 421476 $, $Date: 2006-07-12 22:08:28 -0400 (Wed, 12 Jul 2006) $
+ * @version $Revision$, $Date$
*/
protected class CGIRunner {
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 332127 $ $Date: 2005-11-09 20:50:47 +0100 (mer., 09 nov. 2005) $
+ * @version $Revision$ $Date$
*/
public class DefaultServlet
* to the servlet, because a new servlet mapping will have been created.
*
* @author Craig R. McClanahan
- * @version $Revision: 303211 $ $Date: 2004-09-08 12:38:14 +0200 (mer., 08 sept. 2004) $
+ * @version $Revision$ $Date$
*/
class InvokerHttpRequest extends HttpServletRequestWrapper {
* in the web application deployment descriptor.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class InvokerServlet
* are handled by the DefaultServlet.
*
* @author Remy Maucherat
- * @version $Revision: 303599 $ $Date: 2004-12-20 19:54:14 +0100 (lun., 20 déc. 2004) $
+ * @version $Revision$ $Date$
*/
public class WebdavServlet
* saved are still subject to being expired based on inactivity.
*
* @author Craig R. McClanahan
- * @version $Revision: 303826 $ $Date: 2005-03-31 12:31:54 +0200 (jeu., 31 mars 2005) $
+ * @version $Revision$ $Date$
*/
public final class FileStore
* saved are still subject to being expired based on inactivity.
*
* @author Bip Thelin
- * @version $Revision: 303826 $, $Date: 2005-03-31 12:31:54 +0200 (jeu., 31 mars 2005) $
+ * @version $Revision$, $Date$
*/
public class JDBCStore
* be subclassed to create more sophisticated Manager implementations.
*
* @author Craig R. McClanahan
- * @version $Revision: 388112 $ $Date: 2006-03-23 10:00:30 +0100 (jeu., 23 mars 2006) $
+ * @version $Revision$ $Date$
*/
public abstract class ManagerBase implements Manager, MBeanRegistration {
* <li>Limit the number of active sessions kept in memory by
* swapping less active sessions out to disk.</li>
*
- * @version $Revision: 302726 $
+ * @version $Revision$
* @author Kief Morris (kief@kief.com)
*/
*
* @author Craig R. McClanahan
* @author Jean-Francois Arcand
- * @version $Revision: 303826 $ $Date: 2005-03-31 12:31:54 +0200 (jeu., 31 mars 2005) $
+ * @version $Revision$ $Date$
*/
public abstract class PersistentManagerBase
*
* @author Craig R. McClanahan
* @author Jean-Francois Arcand
- * @version $Revision: 387590 $ $Date: 2006-03-21 18:55:07 +0100 (mar., 21 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class StandardManager
* @author Craig R. McClanahan
* @author Sean Legassick
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
- * @version $Revision: 384817 $ $Date: 2006-03-10 16:27:43 +0100 (ven., 10 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class StandardSession
* Facade for the StandardSession object.
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class StandardSessionFacade
* support most of the functionality required by a Store.
*
* @author Bip Thelin
- * @version $Revision: 303826 $, $Date: 2005-03-31 12:31:54 +0200 (jeu., 31 mars 2005) $
+ * @version $Revision$, $Date$
*/
public abstract class StoreBase
* <code>SsiInclude</code>
*
* @author Bip Thelin
- * @version $Revision: 304045 $, $Date: 2005-08-04 14:18:30 +0200 (jeu., 04 août 2005) $
+ * @version $Revision$, $Date$
* @see ServletOutputStream and ByteArrayOutputStream
*/
public class ByteArrayServletOutputStream extends ServletOutputStream {
/**
* Represents a parsed expression.
*
- * @version $Revision: 303166 $
+ * @version $Revision$
* @author Paul Speed
*/
public class ExpressionParseTree {
* patterned similar to the StreamTokenizer in the JDK but customized for SSI
* conditional expression parsing.
*
- * @version $Revision: 304061 $
+ * @version $Revision$
* @author Paul Speed
*/
public class ExpressionTokenizer {
*
* @author Bip Thelin
* @author David Becker
- * @version $Revision: 304032 $, $Date: 2005-07-27 17:11:55 +0200 (mer., 27 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class ResponseIncludeWrapper extends HttpServletResponseWrapper {
/**
* @author Bip Thelin
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public interface SSICommand {
/**
/**
* SSI command that handles all conditional directives.
*
- * @version $Revision: 303882 $
+ * @version $Revision$
* @author Paul Speed
* @author David Becker
*/
* information necessary to process the nested conditional commands ( if, elif,
* else, endif ).
*
- * @version $Revision: 303166 $
+ * @version $Revision$
* @author Dan Sandberg
* @author Paul Speed
*/
* @author Paul Speed
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public final class SSIConfig implements SSICommand {
/**
* @author Paul Speed
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSIEcho implements SSICommand {
protected final static String DEFAULT_ENCODING = "entity";
* @author Paul Speed
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSIExec implements SSICommand {
protected SSIInclude ssiInclude = new SSIInclude();
* servlet )
*
* @author Dan Sandberg
- * @version $Revision: 303166 $, $Date: 2004-09-01 20:33:33 +0200 (mer., 01 sept. 2004) $
+ * @version $Revision$, $Date$
*/
public interface SSIExternalResolver {
/**
* from within web.xml.
*
* @author David Becker
- * @version $Revision: 303920 $, $Date: 2005-05-05 22:52:37 +0200 (jeu., 05 mai 2005) $
+ * @version $Revision$, $Date$
* @see org.apache.catalina.ssi.SSIServlet
*/
public class SSIFilter implements Filter {
* @author Paul Speed
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 304061 $, $Date: 2005-08-17 23:21:37 +0200 (mer., 17 août 2005) $
+ * @version $Revision$, $Date$
*/
public final class SSIFlastmod implements SSICommand {
/**
* @author Paul Speed
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public final class SSIFsize implements SSICommand {
protected final static int ONE_KILOBYTE = 1024;
* @author Paul Speed
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public final class SSIInclude implements SSICommand {
/**
* @author Paul Speed
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSIMediator {
protected final static String DEFAULT_CONFIG_ERR_MSG = "[an error occurred while processing this directive]";
*
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSIPrintenv implements SSICommand {
/**
*
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSIProcessor {
/** The start pattern */
* @author Amy Roh
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSIServlet extends HttpServlet {
/** Debug level for this servlet. */
*
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 349308 $, $Date: 2005-11-27 21:52:55 +0100 (dim., 27 nov. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSIServletExternalResolver implements SSIExternalResolver {
protected final String VARIABLE_NAMES[] = {"AUTH_TYPE", "CONTENT_LENGTH",
* @author Paul Speed
* @author Dan Sandberg
* @author David Becker
- * @version $Revision: 303882 $, $Date: 2005-04-23 12:22:37 +0200 (sam., 23 avr. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSISet implements SSICommand {
/**
*
* @author Paul Speed
* @author Dan Sandberg
- * @version $Revision: 303166 $, $Date: 2004-09-01 20:33:33 +0200 (mer., 01 sept. 2004) $
+ * @version $Revision$, $Date$
*/
public class SSIStopProcessingException extends Exception {
}
\ No newline at end of file
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 304108 $ $Date: 2005-09-29 07:55:15 +0200 (jeu., 29 sept. 2005) $
+ * @version $Revision$ $Date$
*/
public final class Bootstrap {
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 380229 $ $Date: 2006-02-23 22:28:29 +0100 (jeu., 23 févr. 2006) $
+ * @version $Revision$ $Date$
*/
public class Catalina extends Embedded {
* Utility class to read the bootstrap Catalina configuration.
*
* @author Remy Maucherat
- * @version $Revision: 304109 $ $Date: 2005-09-29 16:36:25 +0200 (jeu., 29 sept. 2005) $
+ * @version $Revision$ $Date$
*/
public class CatalinaProperties {
* </ul>
*
* @author Craig R. McClanahan
- * @version $Revision: 303210 $ $Date: 2004-09-08 01:13:01 +0200 (mer., 08 sept. 2004) $
+ * @version $Revision$ $Date$
*/
public final class ClassLoaderFactory {
*
* @author Filip Hanik
* @author Peter Rossbach
- * @version $Revision: 379550 $ $Date: 2006-02-21 12:06:35 -0600 (Tue, 21 Feb 2006) $
+ * @version $Revision$ $Date$
*/
public static class DefaultClusterRuleSet extends RuleSetBase {
-/* $Id: ConnectorCreateRule.java 303287 2004-09-29 09:55:39Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
*
* @author Craig R. McClanahan
* @author Jean-Francois Arcand
- * @version $Revision: 303179 $ $Date: 2004-09-02 12:28:00 +0200 (jeu., 02 sept. 2004) $
+ * @version $Revision$ $Date$
*/
public final class Constants {
*
* @author Craig R. McClanahan
* @author Jean-Francois Arcand
- * @version $Revision: 379614 $ $Date: 2006-02-22 00:02:57 +0100 (mer., 22 févr. 2006) $
+ * @version $Revision$ $Date$
*/
public class ContextConfig
* DefaultContext, be sure to specify a prefix that ends with "/Default".</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 345090 $ $Date: 2005-11-16 19:51:09 +0100 (mer., 16 nov. 2005) $
+ * @version $Revision$ $Date$
*/
public class ContextRuleSet extends RuleSetBase {
* <code>Container</code>).</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302984 $ $Date: 2004-06-26 19:41:33 +0200 (sam., 26 juin 2004) $
+ * @version $Revision$ $Date$
*/
public class CopyParentClassLoaderRule extends Rule {
* of how Tomcat is set up and launched as an Embedded application.
*
* @author Craig R. McClanahan
- * @version $Revision: 304107 $ $Date: 2005-09-29 07:52:48 +0200 (jeu., 29 sept. 2005) $
+ * @version $Revision$ $Date$
*/
public class Embedded extends StandardService implements Lifecycle {
* of that Engine, and the associated defined contexts.
*
* @author Craig R. McClanahan
- * @version $Revision: 349922 $ $Date: 2005-11-30 12:10:55 +0100 (mer., 30 nov. 2005) $
+ * @version $Revision$ $Date$
*/
public class EngineConfig
* <code>ContextRuleSet</code>, respectively.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303970 $ $Date: 2005-06-30 15:08:14 +0200 (jeu., 30 juin 2005) $
+ * @version $Revision$ $Date$
*/
public class EngineRuleSet extends RuleSetBase {
* @author Craig R. McClanahan
* @author Remy Maucherat
* @author Glenn L. Nielsen
- * @version $Revision: 303769 $
+ * @version $Revision$
*/
public class ExpandWar {
* to our constructor to be "home" directories for those users.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class HomesUserDatabase
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 386336 $ $Date: 2006-03-16 15:13:00 +0100 (jeu., 16 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class HostConfig
implements LifecycleListener {
* be added via instances of <code>ContextRuleSet</code>.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302984 $ $Date: 2004-06-26 19:41:33 +0200 (sam., 26 juin 2004) $
+ * @version $Revision$ $Date$
*/
public class HostRuleSet extends RuleSetBase {
*
* @author Craig R. McClanahan
* @author Remy Maucherat
- * @version $Revision: 303177 $ $Date: 2004-09-02 12:05:00 +0200 (jeu., 02 sept. 2004) $
+ * @version $Revision$ $Date$
*/
public class NamingRuleSet extends RuleSetBase {
* that processes the <code>/etc/passwd</code> file on a Unix system.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class PasswdUserDatabase
-/* $Id: SetNextNamingRule.java 303133 2004-08-29 16:46:15Z yoavs $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* descriptor resource.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302984 $ $Date: 2004-06-26 19:41:33 +0200 (sam., 26 juin 2004) $
+ * @version $Revision$ $Date$
*/
public class TldRuleSet extends RuleSetBase {
* </ul>
*
* @author Craig R. McClanahan
- * @version $Revision: 302983 $ $Date: 2004-06-26 01:56:25 +0200 (sam., 26 juin 2004) $
+ * @version $Revision$ $Date$
*/
public final class Tool {
* xxxxx is the username of the owning user for that web application
*
* @author Craig R. McClanahan
- * @version $Revision: 302976 $ $Date: 2004-06-23 15:51:38 +0200 (mer., 23 juin 2004) $
+ * @version $Revision$ $Date$
*/
public final class UserConfig
* current server platform.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public interface UserDatabase {
* classes (<code>/WEB-INF/classes</code> and <code>/WEB-INF/lib</code>).</p>
*
* @author Fabien Carrion
- * @version $Revision: 304108 $ $Date: 2006-03-01 10:39:15 -0700 (Wed, 03 Mar 2006) $
+ * @version $Revision$ $Date$
*/
public class WebAnnotationSet {
* deployment descriptor (<code>/WEB-INF/web.xml</code>) resource.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 304108 $ $Date: 2005-09-29 07:55:15 +0200 (jeu., 29 sept. 2005) $
+ * @version $Revision$ $Date$
*/
public class WebRuleSet extends RuleSetBase {
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-import java.io.Serializable;\r
-import java.io.Externalizable;\r
-import java.io.ObjectInput;\r
-import java.io.IOException;\r
-import java.io.ObjectOutput;\r
-\r
-/**\r
- * A byte message is not serialized and deserialized by the channel\r
- * instead it is sent as a byte array<br>\r
- * By default Tribes uses java serialization when it receives an object\r
- * to be sent over the wire. Java serialization is not the most\r
- * efficient of serializing data, and Tribes might not even\r
- * have access to the correct class loaders to deserialize the object properly.\r
- * <br>\r
- * The ByteMessage class is a class where the channel when it receives it will\r
- * not attempt to perform serialization, instead it will simply stream the <code>getMessage()</code>\r
- * bytes.<br>\r
- * If you are using multiple applications on top of Tribes you should add some sort of header\r
- * so that you can decide with the <code>ChannelListener.accept()</code> whether this message was intended\r
- * for you.\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-\r
-public class ByteMessage implements Serializable, Externalizable {\r
- /**\r
- * Storage for the message to be sent\r
- */\r
- private byte[] message;\r
-\r
-\r
- /**\r
- * Creates an empty byte message\r
- * Constructor also for deserialization\r
- */\r
- public ByteMessage() {\r
- }\r
-\r
- /**\r
- * Creates a byte message wit h\r
- * @param data byte[] - the message contents\r
- */\r
- public ByteMessage(byte[] data) {\r
- message = data;\r
- }\r
-\r
- /**\r
- * Returns the message contents of this byte message\r
- * @return byte[] - message contents, can be null\r
- */\r
- public byte[] getMessage() {\r
- return message;\r
- }\r
-\r
- /**\r
- * Sets the message contents of this byte message\r
- * @param message byte[]\r
- */\r
- public void setMessage(byte[] message) {\r
- this.message = message;\r
- }\r
-\r
- /**\r
- * @see java.io.Externalizable#readExternal\r
- * @param in ObjectInput\r
- * @throws IOException\r
- */\r
- public void readExternal(ObjectInput in ) throws IOException {\r
- int length = in.readInt();\r
- message = new byte[length];\r
- in.read(message,0,length);\r
- }\r
-\r
- /**\r
- * @see java.io.Externalizable#writeExternal\r
- * @param out ObjectOutput\r
- * @throws IOException\r
- */\r
- public void writeExternal(ObjectOutput out) throws IOException {\r
- out.writeInt(message!=null?message.length:0);\r
- if ( message!=null ) out.write(message,0,message.length);\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+import java.io.Serializable;
+import java.io.Externalizable;
+import java.io.ObjectInput;
+import java.io.IOException;
+import java.io.ObjectOutput;
+
+/**
+ * A byte message is not serialized and deserialized by the channel
+ * instead it is sent as a byte array<br>
+ * By default Tribes uses java serialization when it receives an object
+ * to be sent over the wire. Java serialization is not the most
+ * efficient of serializing data, and Tribes might not even
+ * have access to the correct class loaders to deserialize the object properly.
+ * <br>
+ * The ByteMessage class is a class where the channel when it receives it will
+ * not attempt to perform serialization, instead it will simply stream the <code>getMessage()</code>
+ * bytes.<br>
+ * If you are using multiple applications on top of Tribes you should add some sort of header
+ * so that you can decide with the <code>ChannelListener.accept()</code> whether this message was intended
+ * for you.
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+public class ByteMessage implements Serializable, Externalizable {
+ /**
+ * Storage for the message to be sent
+ */
+ private byte[] message;
+
+
+ /**
+ * Creates an empty byte message
+ * Constructor also for deserialization
+ */
+ public ByteMessage() {
+ }
+
+ /**
+ * Creates a byte message wit h
+ * @param data byte[] - the message contents
+ */
+ public ByteMessage(byte[] data) {
+ message = data;
+ }
+
+ /**
+ * Returns the message contents of this byte message
+ * @return byte[] - message contents, can be null
+ */
+ public byte[] getMessage() {
+ return message;
+ }
+
+ /**
+ * Sets the message contents of this byte message
+ * @param message byte[]
+ */
+ public void setMessage(byte[] message) {
+ this.message = message;
+ }
+
+ /**
+ * @see java.io.Externalizable#readExternal
+ * @param in ObjectInput
+ * @throws IOException
+ */
+ public void readExternal(ObjectInput in ) throws IOException {
+ int length = in.readInt();
+ message = new byte[length];
+ in.read(message,0,length);
+ }
+
+ /**
+ * @see java.io.Externalizable#writeExternal
+ * @param out ObjectOutput
+ * @throws IOException
+ */
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeInt(message!=null?message.length:0);
+ if ( message!=null ) out.write(message,0,message.length);
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-import java.io.Serializable;\r
-\r
-/**\r
- * Channel interface<br>\r
- * A channel is a representation of a group of nodes all participating in some sort of\r
- * communication with each other.<br>\r
- * The channel is the main API class for Tribes, this is essentially the only class\r
- * that an application needs to be aware of. Through the channel the application can:<br>\r
- * 1. send messages<br>\r
- * 2. receive message (by registering a <code>ChannelListener</code><br>\r
- * 3. get all members of the group <code>getMembers()</code><br>\r
- * 4. receive notifications of members added and members disappeared by\r
- * registerering a <code>MembershipListener</code><br>\r
- * <br>\r
- * The channel has 5 major components:<br>\r
- * 1. Data receiver, with a built in thread pool to receive messages from other peers<br>\r
- * 2. Data sender, an implementation for sending data using NIO or java.io<br>\r
- * 3. Membership listener,listens for membership broadcasts<br>\r
- * 4. Membership broadcaster, broadcasts membership pings.<br>\r
- * 5. Channel interceptors, the ability to manipulate messages as they are sent or arrive<br><br>\r
- * The channel layout is:\r
- * <pre><code>\r
- * ChannelListener_1..ChannelListener_N MembershipListener_1..MembershipListener_N [Application Layer]\r
- * \ \ / /\r
- * \ \ / /\r
- * \ \ / /\r
- * \ \ / /\r
- * \ \ / /\r
- * \ \ / /\r
- * ---------------------------------------\r
- * |\r
- * |\r
- * Channel\r
- * |\r
- * ChannelInterceptor_1\r
- * | [Channel stack]\r
- * ChannelInterceptor_N\r
- * |\r
- * Coordinator (implements MessageListener,MembershipListener,ChannelInterceptor)\r
- * --------------------\r
- * / | \ \r
- * / | \\r
- * / | \\r
- * / | \\r
- * / | \\r
- * MembershipService ChannelSender ChannelReceiver [IO layer]\r
- * </code></pre>\r
- * \r
- * For example usage @see org.apache.catalina.tribes.group.GroupChannel\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-public interface Channel {\r
- \r
- /**\r
- * Start and stop sequences can be controlled by these constants\r
- * This allows you to start separate components of the channel <br>\r
- * DEFAULT - starts or stops all components in the channel\r
- * @see #start(int)\r
- * @see #stop(int)\r
- */\r
- public static final int DEFAULT = 15;\r
-\r
- /**\r
- * Start and stop sequences can be controlled by these constants\r
- * This allows you to start separate components of the channel <br>\r
- * SND_RX_SEQ - starts or stops the data receiver. Start means opening a server socket\r
- * in case of a TCP implementation\r
- * @see #start(int)\r
- * @see #stop(int)\r
- */\r
- public static final int SND_RX_SEQ = 1;\r
-\r
- /**\r
- * Start and stop sequences can be controlled by these constants\r
- * This allows you to start separate components of the channel <br>\r
- * SND_TX_SEQ - starts or stops the data sender. This should not open any sockets,\r
- * as sockets are opened on demand when a message is being sent\r
- * @see #start(int)\r
- * @see #stop(int)\r
- */\r
- public static final int SND_TX_SEQ = 2;\r
-\r
- /**\r
- * Start and stop sequences can be controlled by these constants\r
- * This allows you to start separate components of the channel <br>\r
- * MBR_RX_SEQ - starts or stops the membership listener. In a multicast implementation\r
- * this will open a datagram socket and join a group and listen for membership messages\r
- * members joining\r
- * @see #start(int)\r
- * @see #stop(int)\r
- */\r
- public static final int MBR_RX_SEQ = 4;\r
-\r
- /**\r
- * Start and stop sequences can be controlled by these constants\r
- * This allows you to start separate components of the channel <br>\r
- * MBR_TX_SEQ - starts or stops the membership broadcaster. In a multicast implementation\r
- * this will open a datagram socket and join a group and broadcast the local member information\r
- * @see #start(int)\r
- * @see #stop(int)\r
- */\r
- public static final int MBR_TX_SEQ = 8;\r
- \r
- /**\r
- * Send options, when a message is sent, it can have an option flag\r
- * to trigger certain behavior. Most flags are used to trigger channel interceptors\r
- * as the message passes through the channel stack. <br>\r
- * However, there are five default flags that every channel implementation must implement<br>\r
- * SEND_OPTIONS_BYTE_MESSAGE - The message is a pure byte message and no marshalling or unmarshalling will\r
- * be performed.<br>\r
- * \r
- * @see #send(Member[], Serializable , int)\r
- * @see #send(Member[], Serializable, int, ErrorHandler)\r
- */\r
- public static final int SEND_OPTIONS_BYTE_MESSAGE = 0x0001;\r
-\r
- /**\r
- * Send options, when a message is sent, it can have an option flag\r
- * to trigger certain behavior. Most flags are used to trigger channel interceptors\r
- * as the message passes through the channel stack. <br>\r
- * However, there are five default flags that every channel implementation must implement<br>\r
- * SEND_OPTIONS_USE_ACK - Message is sent and an ACK is received when the message has been received by the recipient<br>\r
- * If no ack is received, the message is not considered successful<br>\r
- * @see #send(Member[], Serializable , int)\r
- * @see #send(Member[], Serializable, int, ErrorHandler)\r
- */\r
- public static final int SEND_OPTIONS_USE_ACK = 0x0002;\r
-\r
- /**\r
- * Send options, when a message is sent, it can have an option flag\r
- * to trigger certain behavior. Most flags are used to trigger channel interceptors\r
- * as the message passes through the channel stack. <br>\r
- * However, there are five default flags that every channel implementation must implement<br>\r
- * SEND_OPTIONS_SYNCHRONIZED_ACK - Message is sent and an ACK is received when the message has been received and \r
- * processed by the recipient<br>\r
- * If no ack is received, the message is not considered successful<br>\r
- * @see #send(Member[], Serializable , int)\r
- * @see #send(Member[], Serializable, int, ErrorHandler)\r
- */\r
- public static final int SEND_OPTIONS_SYNCHRONIZED_ACK = 0x0004;\r
- \r
- /**\r
- * Send options, when a message is sent, it can have an option flag\r
- * to trigger certain behavior. Most flags are used to trigger channel interceptors\r
- * as the message passes through the channel stack. <br>\r
- * However, there are five default flags that every channel implementation must implement<br>\r
- * SEND_OPTIONS_ASYNCHRONOUS - Message is sent and an ACK is received when the message has been received and \r
- * processed by the recipient<br>\r
- * If no ack is received, the message is not considered successful<br>\r
- * @see #send(Member[], Serializable , int)\r
- * @see #send(Member[], Serializable, int, ErrorHandler)\r
- */\r
- public static final int SEND_OPTIONS_ASYNCHRONOUS = 0x0008;\r
- \r
- /**\r
- * Send options, when a message is sent, it can have an option flag\r
- * to trigger certain behavior. Most flags are used to trigger channel interceptors\r
- * as the message passes through the channel stack. <br>\r
- * However, there are five default flags that every channel implementation must implement<br>\r
- * SEND_OPTIONS_SECURE - Message is sent over an encrypted channel<br>\r
- * @see #send(Member[], Serializable , int)\r
- * @see #send(Member[], Serializable, int, ErrorHandler)\r
- */\r
- public static final int SEND_OPTIONS_SECURE = 0x0010;\r
- \r
-\r
- /**\r
- * Send options, when a message is sent, it can have an option flag\r
- * to trigger certain behavior. Most flags are used to trigger channel interceptors\r
- * as the message passes through the channel stack. <br>\r
- * However, there are five default flags that every channel implementation must implement<br>\r
- * SEND_OPTIONS_DEFAULT - the default sending options, just a helper variable. <br>\r
- * The default is <code>int SEND_OPTIONS_DEFAULT = SEND_OPTIONS_USE_ACK;</code><br>\r
- * @see #SEND_OPTIONS_USE_ACK\r
- * @see #send(Member[], Serializable , int)\r
- * @see #send(Member[], Serializable, int, ErrorHandler)\r
- */\r
- public static final int SEND_OPTIONS_DEFAULT = SEND_OPTIONS_USE_ACK;\r
-\r
- \r
- /**\r
- * Adds an interceptor to the channel message chain.\r
- * @param interceptor ChannelInterceptor\r
- */\r
- public void addInterceptor(ChannelInterceptor interceptor);\r
- \r
- /**\r
- * Starts up the channel. This can be called multiple times for individual services to start\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * DEFAULT - will start all services <BR>\r
- * MBR_RX_SEQ - starts the membership receiver <BR>\r
- * MBR_TX_SEQ - starts the membership broadcaster <BR>\r
- * SND_TX_SEQ - starts the replication transmitter<BR>\r
- * SND_RX_SEQ - starts the replication receiver<BR>\r
- * <b>Note:</b> In order for the membership broadcaster to \r
- * transmit the correct information, it has to be started after the replication receiver.\r
- * @throws ChannelException if a startup error occurs or the service is already started or an error occurs.\r
- */\r
- public void start(int svc) throws ChannelException;\r
-\r
- /**\r
- * Shuts down the channel. This can be called multiple times for individual services to shutdown\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * DEFAULT - will shutdown all services <BR>\r
- * MBR_RX_SEQ - stops the membership receiver <BR>\r
- * MBR_TX_SEQ - stops the membership broadcaster <BR>\r
- * SND_TX_SEQ - stops the replication transmitter<BR>\r
- * SND_RX_SEQ - stops the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already stopped or an error occurs.\r
- */\r
- public void stop(int svc) throws ChannelException; \r
- \r
- /**\r
- * Send a message to one or more members in the cluster\r
- * @param destination Member[] - the destinations, can not be null or zero length, the reason for that\r
- * is that a membership change can occur and at that time the application is uncertain what group the message\r
- * actually got sent to.\r
- * @param msg Serializable - the message to send, has to be serializable, or a <code>ByteMessage</code> to \r
- * send a pure byte array\r
- * @param options int - sender options, see class documentation for each interceptor that is configured in order to trigger interceptors\r
- * @return a unique Id that identifies the message that is sent\r
- * @see ByteMessage\r
- * @see #SEND_OPTIONS_USE_ACK\r
- * @see #SEND_OPTIONS_ASYNCHRONOUS\r
- * @see #SEND_OPTIONS_SYNCHRONIZED_ACK\r
- */\r
- public UniqueId send(Member[] destination, Serializable msg, int options) throws ChannelException;\r
-\r
- /**\r
- * Send a message to one or more members in the cluster\r
- * @param destination Member[] - the destinations, null or zero length means all\r
- * @param msg ClusterMessage - the message to send\r
- * @param options int - sender options, see class documentation\r
- * @param handler ErrorHandler - handle errors through a callback, rather than throw it\r
- * @return a unique Id that identifies the message that is sent\r
- * @exception ChannelException - if a serialization error happens.\r
- */\r
- public UniqueId send(Member[] destination, Serializable msg, int options, ErrorHandler handler) throws ChannelException;\r
- \r
- /**\r
- * Sends a heart beat through the interceptor stacks\r
- * Use this method to alert interceptors and other components to \r
- * clean up garbage, timed out messages etc.<br>\r
- * If you application has a background thread, then you can save one thread,\r
- * by configuring your channel to not use an internal heartbeat thread\r
- * and invoking this method.\r
- * @see #setHeartbeat(boolean)\r
- */\r
- public void heartbeat();\r
- \r
- /**\r
- * Enables or disables internal heartbeat.\r
- * @param enable boolean - default value is implementation specific\r
- * @see #heartbeat()\r
- */\r
- public void setHeartbeat(boolean enable);\r
- \r
- /**\r
- * Add a membership listener, will get notified when a new member joins, leaves or crashes\r
- * <br>If the membership listener implements the Heartbeat interface\r
- * the <code>heartbeat()</code> method will be invoked when the heartbeat runs on the channel\r
- * @param listener MembershipListener\r
- * @see MembershipListener\r
- */\r
- public void addMembershipListener(MembershipListener listener);\r
- \r
- /**\r
- * Add a channel listener, this is a callback object when messages are received\r
- * <br>If the channel listener implements the Heartbeat interface\r
- * the <code>heartbeat()</code> method will be invoked when the heartbeat runs on the channel\r
- * @param listener ChannelListener\r
- * @see ChannelListener\r
- * @see Heartbeat\r
- */\r
- public void addChannelListener(ChannelListener listener);\r
-\r
- /**\r
- * remove a membership listener, listeners are removed based on Object.hashCode and Object.equals\r
- * @param listener MembershipListener\r
- * @see MembershipListener\r
- */\r
- public void removeMembershipListener(MembershipListener listener);\r
- /**\r
- * remove a channel listener, listeners are removed based on Object.hashCode and Object.equals\r
- * @param listener ChannelListener\r
- * @see ChannelListener\r
- */\r
- public void removeChannelListener(ChannelListener listener);\r
- \r
- /**\r
- * Returns true if there are any members in the group,\r
- * this call is the same as <code>getMembers().length>0</code>\r
- * @return boolean - true if there are any members automatically discovered\r
- */\r
- public boolean hasMembers() ;\r
-\r
- /**\r
- * Get all current group members\r
- * @return all members or empty array, never null \r
- */\r
- public Member[] getMembers() ;\r
-\r
- /**\r
- * Return the member that represents this node. This is also the data\r
- * that gets broadcasted through the membership broadcaster component\r
- * @param incAlive - optimization, true if you want it to calculate alive time\r
- * since the membership service started.\r
- * @return Member\r
- */\r
- public Member getLocalMember(boolean incAlive);\r
- \r
- /**\r
- * Returns the member from the membership service with complete and \r
- * recent data. Some implementations might serialize and send \r
- * membership information along with a message, and instead of sending\r
- * complete membership details, only send the primary identifier for the member\r
- * but not the payload or other information. When such message is received\r
- * the application can retrieve the cached member through this call.<br>\r
- * In most cases, this is not necessary.\r
- * @param mbr Member\r
- * @return Member\r
- */\r
- public Member getMember(Member mbr);\r
-\r
- \r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+import java.io.Serializable;
+
+/**
+ * Channel interface<br>
+ * A channel is a representation of a group of nodes all participating in some sort of
+ * communication with each other.<br>
+ * The channel is the main API class for Tribes, this is essentially the only class
+ * that an application needs to be aware of. Through the channel the application can:<br>
+ * 1. send messages<br>
+ * 2. receive message (by registering a <code>ChannelListener</code><br>
+ * 3. get all members of the group <code>getMembers()</code><br>
+ * 4. receive notifications of members added and members disappeared by
+ * registerering a <code>MembershipListener</code><br>
+ * <br>
+ * The channel has 5 major components:<br>
+ * 1. Data receiver, with a built in thread pool to receive messages from other peers<br>
+ * 2. Data sender, an implementation for sending data using NIO or java.io<br>
+ * 3. Membership listener,listens for membership broadcasts<br>
+ * 4. Membership broadcaster, broadcasts membership pings.<br>
+ * 5. Channel interceptors, the ability to manipulate messages as they are sent or arrive<br><br>
+ * The channel layout is:
+ * <pre><code>
+ * ChannelListener_1..ChannelListener_N MembershipListener_1..MembershipListener_N [Application Layer]
+ * \ \ / /
+ * \ \ / /
+ * \ \ / /
+ * \ \ / /
+ * \ \ / /
+ * \ \ / /
+ * ---------------------------------------
+ * |
+ * |
+ * Channel
+ * |
+ * ChannelInterceptor_1
+ * | [Channel stack]
+ * ChannelInterceptor_N
+ * |
+ * Coordinator (implements MessageListener,MembershipListener,ChannelInterceptor)
+ * --------------------
+ * / | \
+ * / | \
+ * / | \
+ * / | \
+ * / | \
+ * MembershipService ChannelSender ChannelReceiver [IO layer]
+ * </code></pre>
+ *
+ * For example usage @see org.apache.catalina.tribes.group.GroupChannel
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+public interface Channel {
+
+ /**
+ * Start and stop sequences can be controlled by these constants
+ * This allows you to start separate components of the channel <br>
+ * DEFAULT - starts or stops all components in the channel
+ * @see #start(int)
+ * @see #stop(int)
+ */
+ public static final int DEFAULT = 15;
+
+ /**
+ * Start and stop sequences can be controlled by these constants
+ * This allows you to start separate components of the channel <br>
+ * SND_RX_SEQ - starts or stops the data receiver. Start means opening a server socket
+ * in case of a TCP implementation
+ * @see #start(int)
+ * @see #stop(int)
+ */
+ public static final int SND_RX_SEQ = 1;
+
+ /**
+ * Start and stop sequences can be controlled by these constants
+ * This allows you to start separate components of the channel <br>
+ * SND_TX_SEQ - starts or stops the data sender. This should not open any sockets,
+ * as sockets are opened on demand when a message is being sent
+ * @see #start(int)
+ * @see #stop(int)
+ */
+ public static final int SND_TX_SEQ = 2;
+
+ /**
+ * Start and stop sequences can be controlled by these constants
+ * This allows you to start separate components of the channel <br>
+ * MBR_RX_SEQ - starts or stops the membership listener. In a multicast implementation
+ * this will open a datagram socket and join a group and listen for membership messages
+ * members joining
+ * @see #start(int)
+ * @see #stop(int)
+ */
+ public static final int MBR_RX_SEQ = 4;
+
+ /**
+ * Start and stop sequences can be controlled by these constants
+ * This allows you to start separate components of the channel <br>
+ * MBR_TX_SEQ - starts or stops the membership broadcaster. In a multicast implementation
+ * this will open a datagram socket and join a group and broadcast the local member information
+ * @see #start(int)
+ * @see #stop(int)
+ */
+ public static final int MBR_TX_SEQ = 8;
+
+ /**
+ * Send options, when a message is sent, it can have an option flag
+ * to trigger certain behavior. Most flags are used to trigger channel interceptors
+ * as the message passes through the channel stack. <br>
+ * However, there are five default flags that every channel implementation must implement<br>
+ * SEND_OPTIONS_BYTE_MESSAGE - The message is a pure byte message and no marshalling or unmarshalling will
+ * be performed.<br>
+ *
+ * @see #send(Member[], Serializable , int)
+ * @see #send(Member[], Serializable, int, ErrorHandler)
+ */
+ public static final int SEND_OPTIONS_BYTE_MESSAGE = 0x0001;
+
+ /**
+ * Send options, when a message is sent, it can have an option flag
+ * to trigger certain behavior. Most flags are used to trigger channel interceptors
+ * as the message passes through the channel stack. <br>
+ * However, there are five default flags that every channel implementation must implement<br>
+ * SEND_OPTIONS_USE_ACK - Message is sent and an ACK is received when the message has been received by the recipient<br>
+ * If no ack is received, the message is not considered successful<br>
+ * @see #send(Member[], Serializable , int)
+ * @see #send(Member[], Serializable, int, ErrorHandler)
+ */
+ public static final int SEND_OPTIONS_USE_ACK = 0x0002;
+
+ /**
+ * Send options, when a message is sent, it can have an option flag
+ * to trigger certain behavior. Most flags are used to trigger channel interceptors
+ * as the message passes through the channel stack. <br>
+ * However, there are five default flags that every channel implementation must implement<br>
+ * SEND_OPTIONS_SYNCHRONIZED_ACK - Message is sent and an ACK is received when the message has been received and
+ * processed by the recipient<br>
+ * If no ack is received, the message is not considered successful<br>
+ * @see #send(Member[], Serializable , int)
+ * @see #send(Member[], Serializable, int, ErrorHandler)
+ */
+ public static final int SEND_OPTIONS_SYNCHRONIZED_ACK = 0x0004;
+
+ /**
+ * Send options, when a message is sent, it can have an option flag
+ * to trigger certain behavior. Most flags are used to trigger channel interceptors
+ * as the message passes through the channel stack. <br>
+ * However, there are five default flags that every channel implementation must implement<br>
+ * SEND_OPTIONS_ASYNCHRONOUS - Message is sent and an ACK is received when the message has been received and
+ * processed by the recipient<br>
+ * If no ack is received, the message is not considered successful<br>
+ * @see #send(Member[], Serializable , int)
+ * @see #send(Member[], Serializable, int, ErrorHandler)
+ */
+ public static final int SEND_OPTIONS_ASYNCHRONOUS = 0x0008;
+
+ /**
+ * Send options, when a message is sent, it can have an option flag
+ * to trigger certain behavior. Most flags are used to trigger channel interceptors
+ * as the message passes through the channel stack. <br>
+ * However, there are five default flags that every channel implementation must implement<br>
+ * SEND_OPTIONS_SECURE - Message is sent over an encrypted channel<br>
+ * @see #send(Member[], Serializable , int)
+ * @see #send(Member[], Serializable, int, ErrorHandler)
+ */
+ public static final int SEND_OPTIONS_SECURE = 0x0010;
+
+
+ /**
+ * Send options, when a message is sent, it can have an option flag
+ * to trigger certain behavior. Most flags are used to trigger channel interceptors
+ * as the message passes through the channel stack. <br>
+ * However, there are five default flags that every channel implementation must implement<br>
+ * SEND_OPTIONS_DEFAULT - the default sending options, just a helper variable. <br>
+ * The default is <code>int SEND_OPTIONS_DEFAULT = SEND_OPTIONS_USE_ACK;</code><br>
+ * @see #SEND_OPTIONS_USE_ACK
+ * @see #send(Member[], Serializable , int)
+ * @see #send(Member[], Serializable, int, ErrorHandler)
+ */
+ public static final int SEND_OPTIONS_DEFAULT = SEND_OPTIONS_USE_ACK;
+
+
+ /**
+ * Adds an interceptor to the channel message chain.
+ * @param interceptor ChannelInterceptor
+ */
+ public void addInterceptor(ChannelInterceptor interceptor);
+
+ /**
+ * Starts up the channel. This can be called multiple times for individual services to start
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * DEFAULT - will start all services <BR>
+ * MBR_RX_SEQ - starts the membership receiver <BR>
+ * MBR_TX_SEQ - starts the membership broadcaster <BR>
+ * SND_TX_SEQ - starts the replication transmitter<BR>
+ * SND_RX_SEQ - starts the replication receiver<BR>
+ * <b>Note:</b> In order for the membership broadcaster to
+ * transmit the correct information, it has to be started after the replication receiver.
+ * @throws ChannelException if a startup error occurs or the service is already started or an error occurs.
+ */
+ public void start(int svc) throws ChannelException;
+
+ /**
+ * Shuts down the channel. This can be called multiple times for individual services to shutdown
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * DEFAULT - will shutdown all services <BR>
+ * MBR_RX_SEQ - stops the membership receiver <BR>
+ * MBR_TX_SEQ - stops the membership broadcaster <BR>
+ * SND_TX_SEQ - stops the replication transmitter<BR>
+ * SND_RX_SEQ - stops the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already stopped or an error occurs.
+ */
+ public void stop(int svc) throws ChannelException;
+
+ /**
+ * Send a message to one or more members in the cluster
+ * @param destination Member[] - the destinations, can not be null or zero length, the reason for that
+ * is that a membership change can occur and at that time the application is uncertain what group the message
+ * actually got sent to.
+ * @param msg Serializable - the message to send, has to be serializable, or a <code>ByteMessage</code> to
+ * send a pure byte array
+ * @param options int - sender options, see class documentation for each interceptor that is configured in order to trigger interceptors
+ * @return a unique Id that identifies the message that is sent
+ * @see ByteMessage
+ * @see #SEND_OPTIONS_USE_ACK
+ * @see #SEND_OPTIONS_ASYNCHRONOUS
+ * @see #SEND_OPTIONS_SYNCHRONIZED_ACK
+ */
+ public UniqueId send(Member[] destination, Serializable msg, int options) throws ChannelException;
+
+ /**
+ * Send a message to one or more members in the cluster
+ * @param destination Member[] - the destinations, null or zero length means all
+ * @param msg ClusterMessage - the message to send
+ * @param options int - sender options, see class documentation
+ * @param handler ErrorHandler - handle errors through a callback, rather than throw it
+ * @return a unique Id that identifies the message that is sent
+ * @exception ChannelException - if a serialization error happens.
+ */
+ public UniqueId send(Member[] destination, Serializable msg, int options, ErrorHandler handler) throws ChannelException;
+
+ /**
+ * Sends a heart beat through the interceptor stacks
+ * Use this method to alert interceptors and other components to
+ * clean up garbage, timed out messages etc.<br>
+ * If you application has a background thread, then you can save one thread,
+ * by configuring your channel to not use an internal heartbeat thread
+ * and invoking this method.
+ * @see #setHeartbeat(boolean)
+ */
+ public void heartbeat();
+
+ /**
+ * Enables or disables internal heartbeat.
+ * @param enable boolean - default value is implementation specific
+ * @see #heartbeat()
+ */
+ public void setHeartbeat(boolean enable);
+
+ /**
+ * Add a membership listener, will get notified when a new member joins, leaves or crashes
+ * <br>If the membership listener implements the Heartbeat interface
+ * the <code>heartbeat()</code> method will be invoked when the heartbeat runs on the channel
+ * @param listener MembershipListener
+ * @see MembershipListener
+ */
+ public void addMembershipListener(MembershipListener listener);
+
+ /**
+ * Add a channel listener, this is a callback object when messages are received
+ * <br>If the channel listener implements the Heartbeat interface
+ * the <code>heartbeat()</code> method will be invoked when the heartbeat runs on the channel
+ * @param listener ChannelListener
+ * @see ChannelListener
+ * @see Heartbeat
+ */
+ public void addChannelListener(ChannelListener listener);
+
+ /**
+ * remove a membership listener, listeners are removed based on Object.hashCode and Object.equals
+ * @param listener MembershipListener
+ * @see MembershipListener
+ */
+ public void removeMembershipListener(MembershipListener listener);
+ /**
+ * remove a channel listener, listeners are removed based on Object.hashCode and Object.equals
+ * @param listener ChannelListener
+ * @see ChannelListener
+ */
+ public void removeChannelListener(ChannelListener listener);
+
+ /**
+ * Returns true if there are any members in the group,
+ * this call is the same as <code>getMembers().length>0</code>
+ * @return boolean - true if there are any members automatically discovered
+ */
+ public boolean hasMembers() ;
+
+ /**
+ * Get all current group members
+ * @return all members or empty array, never null
+ */
+ public Member[] getMembers() ;
+
+ /**
+ * Return the member that represents this node. This is also the data
+ * that gets broadcasted through the membership broadcaster component
+ * @param incAlive - optimization, true if you want it to calculate alive time
+ * since the membership service started.
+ * @return Member
+ */
+ public Member getLocalMember(boolean incAlive);
+
+ /**
+ * Returns the member from the membership service with complete and
+ * recent data. Some implementations might serialize and send
+ * membership information along with a message, and instead of sending
+ * complete membership details, only send the primary identifier for the member
+ * but not the payload or other information. When such message is received
+ * the application can retrieve the cached member through this call.<br>
+ * In most cases, this is not necessary.
+ * @param mbr Member
+ * @return Member
+ */
+ public Member getMember(Member mbr);
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-import java.util.ArrayList;\r
-\r
-/**\r
- * Channel Exception<br>\r
- * A channel exception is thrown when an internal error happens\r
- * somewhere in the channel. <br>\r
- * When a global error happens, the cause can be retrieved using <code>getCause()</code><br><br>\r
- * If an application is sending a message and some of the recipients fail to receive it,\r
- * the application can retrieve what recipients failed by using the <code>getFaultyMembers()</code>\r
- * method. This way, an application will always know if a message was delivered successfully or not.\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-\r
-public class ChannelException extends Exception {\r
- /*\r
- * Holds a list of faulty members\r
- */\r
- private ArrayList faultyMembers=null;\r
- \r
- /**\r
- * Constructor, creates a ChannelException\r
- * @see java.lang.Exception#Exception()\r
- */\r
- public ChannelException() {\r
- super();\r
- }\r
-\r
- /**\r
- * Constructor, creates a ChannelException with an error message\r
- * @see java.lang.Exception#Exception(String)\r
- */\r
- public ChannelException(String message) {\r
- super(message);\r
- }\r
-\r
- /**\r
- * Constructor, creates a ChannelException with an error message and a cause\r
- * @param message String\r
- * @param cause Throwable\r
- * @see java.lang.Exception#Exception(String,Throwable)\r
- */\r
- public ChannelException(String message, Throwable cause) {\r
- super(message, cause);\r
- }\r
-\r
- /**\r
- * Constructor, creates a ChannelException with a cause\r
- * @param cause Throwable\r
- * @see java.lang.Exception#Exception(Throwable)\r
- */\r
- public ChannelException(Throwable cause) {\r
- super(cause);\r
- }\r
- \r
- /**\r
- * Returns the message for this exception\r
- * @return String\r
- * @see java.lang.Exception#getMessage()\r
- */\r
- public String getMessage() {\r
- StringBuffer buf = new StringBuffer(super.getMessage());\r
- if (faultyMembers==null || faultyMembers.size() == 0 ) {\r
- buf.append("; No faulty members identified.");\r
- } else {\r
- buf.append("; Faulty members:");\r
- for ( int i=0; i<faultyMembers.size(); i++ ) {\r
- FaultyMember mbr = (FaultyMember)faultyMembers.get(i);\r
- buf.append(mbr.getMember().getName());\r
- buf.append("; ");\r
- }\r
- }\r
- return buf.toString();\r
- }\r
- \r
- /**\r
- * Adds a faulty member, and the reason the member failed.\r
- * @param mbr Member\r
- * @param x Exception\r
- */\r
- public void addFaultyMember(Member mbr, Exception x ) {\r
- addFaultyMember(new FaultyMember(mbr,x));\r
- }\r
- \r
- /**\r
- * Adds a list of faulty members\r
- * @param mbrs FaultyMember[]\r
- */\r
- public void addFaultyMember(FaultyMember[] mbrs) {\r
- for (int i=0; mbrs!=null && i<mbrs.length; i++ ) {\r
- addFaultyMember(mbrs[i]);\r
- }\r
- }\r
-\r
- /**\r
- * Adds a faulty member\r
- * @param mbr FaultyMember\r
- */\r
- public void addFaultyMember(FaultyMember mbr) {\r
- if ( this.faultyMembers==null ) this.faultyMembers = new ArrayList();\r
- faultyMembers.add(mbr);\r
- }\r
- \r
- /**\r
- * Returns an array of members that failed and the reason they failed.\r
- * @return FaultyMember[]\r
- */\r
- public FaultyMember[] getFaultyMembers() {\r
- if ( this.faultyMembers==null ) return new FaultyMember[0];\r
- return (FaultyMember[])faultyMembers.toArray(new FaultyMember[faultyMembers.size()]);\r
- }\r
- \r
- /**\r
- * \r
- * <p>Title: FaultyMember class</p> \r
- * \r
- * <p>Description: Represent a failure to a specific member when a message was sent\r
- * to more than one member</p> \r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
- public static class FaultyMember {\r
- protected Exception cause;\r
- protected Member member;\r
- public FaultyMember(Member mbr, Exception x) { \r
- this.member = mbr;\r
- this.cause = x;\r
- }\r
- \r
- public Member getMember() {\r
- return member;\r
- }\r
- \r
- public Exception getCause() {\r
- return cause;\r
- }\r
- \r
- public String toString() {\r
- return "FaultyMember:"+member.toString();\r
- }\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+import java.util.ArrayList;
+
+/**
+ * Channel Exception<br>
+ * A channel exception is thrown when an internal error happens
+ * somewhere in the channel. <br>
+ * When a global error happens, the cause can be retrieved using <code>getCause()</code><br><br>
+ * If an application is sending a message and some of the recipients fail to receive it,
+ * the application can retrieve what recipients failed by using the <code>getFaultyMembers()</code>
+ * method. This way, an application will always know if a message was delivered successfully or not.
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+public class ChannelException extends Exception {
+ /*
+ * Holds a list of faulty members
+ */
+ private ArrayList faultyMembers=null;
+
+ /**
+ * Constructor, creates a ChannelException
+ * @see java.lang.Exception#Exception()
+ */
+ public ChannelException() {
+ super();
+ }
+
+ /**
+ * Constructor, creates a ChannelException with an error message
+ * @see java.lang.Exception#Exception(String)
+ */
+ public ChannelException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor, creates a ChannelException with an error message and a cause
+ * @param message String
+ * @param cause Throwable
+ * @see java.lang.Exception#Exception(String,Throwable)
+ */
+ public ChannelException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Constructor, creates a ChannelException with a cause
+ * @param cause Throwable
+ * @see java.lang.Exception#Exception(Throwable)
+ */
+ public ChannelException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Returns the message for this exception
+ * @return String
+ * @see java.lang.Exception#getMessage()
+ */
+ public String getMessage() {
+ StringBuffer buf = new StringBuffer(super.getMessage());
+ if (faultyMembers==null || faultyMembers.size() == 0 ) {
+ buf.append("; No faulty members identified.");
+ } else {
+ buf.append("; Faulty members:");
+ for ( int i=0; i<faultyMembers.size(); i++ ) {
+ FaultyMember mbr = (FaultyMember)faultyMembers.get(i);
+ buf.append(mbr.getMember().getName());
+ buf.append("; ");
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Adds a faulty member, and the reason the member failed.
+ * @param mbr Member
+ * @param x Exception
+ */
+ public void addFaultyMember(Member mbr, Exception x ) {
+ addFaultyMember(new FaultyMember(mbr,x));
+ }
+
+ /**
+ * Adds a list of faulty members
+ * @param mbrs FaultyMember[]
+ */
+ public void addFaultyMember(FaultyMember[] mbrs) {
+ for (int i=0; mbrs!=null && i<mbrs.length; i++ ) {
+ addFaultyMember(mbrs[i]);
+ }
+ }
+
+ /**
+ * Adds a faulty member
+ * @param mbr FaultyMember
+ */
+ public void addFaultyMember(FaultyMember mbr) {
+ if ( this.faultyMembers==null ) this.faultyMembers = new ArrayList();
+ faultyMembers.add(mbr);
+ }
+
+ /**
+ * Returns an array of members that failed and the reason they failed.
+ * @return FaultyMember[]
+ */
+ public FaultyMember[] getFaultyMembers() {
+ if ( this.faultyMembers==null ) return new FaultyMember[0];
+ return (FaultyMember[])faultyMembers.toArray(new FaultyMember[faultyMembers.size()]);
+ }
+
+ /**
+ *
+ * <p>Title: FaultyMember class</p>
+ *
+ * <p>Description: Represent a failure to a specific member when a message was sent
+ * to more than one member</p>
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+ public static class FaultyMember {
+ protected Exception cause;
+ protected Member member;
+ public FaultyMember(Member mbr, Exception x) {
+ this.member = mbr;
+ this.cause = x;
+ }
+
+ public Member getMember() {
+ return member;
+ }
+
+ public Exception getCause() {
+ return cause;
+ }
+
+ public String toString() {
+ return "FaultyMember:"+member.toString();
+ }
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-\r
-/**\r
- * A ChannelInterceptor is an interceptor that intercepts \r
- * messages and membership messages in the channel stack.\r
- * This allows interceptors to modify the message or perform\r
- * other actions when a message is sent or received.<br>\r
- * Interceptors are tied together in a linked list.\r
- * @see org.apache.catalina.tribes.group.ChannelInterceptorBase\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */ \r
-\r
-public interface ChannelInterceptor extends MembershipListener, Heartbeat {\r
-\r
- /**\r
- * An interceptor can react to a message based on a set bit on the \r
- * message options. <br>\r
- * When a message is sent, the options can be retrieved from ChannelMessage.getOptions()\r
- * and if the bit is set, this interceptor will react to it.<br>\r
- * A simple evaluation if an interceptor should react to the message would be:<br>\r
- * <code>boolean react = (getOptionFlag() == (getOptionFlag() & ChannelMessage.getOptions()));</code><br>\r
- * The default option is 0, meaning there is no way for the application to trigger the\r
- * interceptor. The interceptor itself will decide.<br>\r
- * @return int\r
- * @see ChannelMessage#getOptions()\r
- */\r
- public int getOptionFlag();\r
- \r
- /**\r
- * Sets the option flag\r
- * @param flag int\r
- * @see #getOptionFlag()\r
- */\r
- public void setOptionFlag(int flag);\r
-\r
- /**\r
- * Set the next interceptor in the list of interceptors\r
- * @param next ChannelInterceptor\r
- */\r
- public void setNext(ChannelInterceptor next) ;\r
-\r
- /**\r
- * Retrieve the next interceptor in the list\r
- * @return ChannelInterceptor - returns the next interceptor in the list or null if no more interceptors exist\r
- */\r
- public ChannelInterceptor getNext();\r
-\r
- /**\r
- * Set the previous interceptor in the list\r
- * @param previous ChannelInterceptor\r
- */\r
- public void setPrevious(ChannelInterceptor previous);\r
-\r
- /**\r
- * Retrieve the previous interceptor in the list\r
- * @return ChannelInterceptor - returns the previous interceptor in the list or null if no more interceptors exist\r
- */\r
- public ChannelInterceptor getPrevious();\r
-\r
- /**\r
- * The <code>sendMessage</code> method is called when a message is being sent to one more destinations.\r
- * The interceptor can modify any of the parameters and then pass on the message down the stack by\r
- * invoking <code>getNext().sendMessage(destination,msg,payload)</code><br>\r
- * Alternatively the interceptor can stop the message from being sent by not invoking \r
- * <code>getNext().sendMessage(destination,msg,payload)</code><br>\r
- * If the message is to be sent asynchronous the application can be notified of completion and \r
- * errors by passing in an error handler attached to a payload object.<br>\r
- * The ChannelMessage.getAddress contains Channel.getLocalMember, and can be overwritten \r
- * to simulate a message sent from another node.<br>\r
- * @param destination Member[] - the destination for this message\r
- * @param msg ChannelMessage - the message to be sent\r
- * @param payload InterceptorPayload - the payload, carrying an error handler and future useful data, can be null\r
- * @throws ChannelException\r
- * @see ErrorHandler\r
- * @see InterceptorPayload\r
- */\r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException;\r
- \r
- /**\r
- * the <code>messageReceived</code> is invoked when a message is received.\r
- * <code>ChannelMessage.getAddress()</code> is the sender, or the reply-to address\r
- * if it has been overwritten.\r
- * @param data ChannelMessage\r
- */\r
- public void messageReceived(ChannelMessage data);\r
- \r
- /**\r
- * The <code>heartbeat()</code> method gets invoked periodically\r
- * to allow interceptors to clean up resources, time out object and \r
- * perform actions that are unrelated to sending/receiving data.\r
- */\r
- public void heartbeat();\r
- \r
- /**\r
- * Intercepts the <code>Channel.hasMembers()</code> method\r
- * @return boolean - if the channel has members in its membership group\r
- * @see Channel#hasMembers()\r
- */\r
- public boolean hasMembers() ;\r
-\r
- /**\r
- * Intercepts the code>Channel.getMembers()</code> method\r
- * @return Member[]\r
- * @see Channel#getMembers()\r
- */\r
- public Member[] getMembers() ;\r
-\r
- /**\r
- * Intercepts the code>Channel.getLocalMember(boolean)</code> method\r
- * @param incAliveTime boolean\r
- * @return Member\r
- * @see Channel#getLocalMember(boolean)\r
- */\r
- public Member getLocalMember(boolean incAliveTime) ;\r
-\r
- /**\r
- * Intercepts the code>Channel.getMember(Member)</code> method\r
- * @param mbr Member\r
- * @return Member - the actual member information, including stay alive\r
- * @see Channel#getMember(Member)\r
- */\r
- public Member getMember(Member mbr);\r
- \r
- /**\r
- * Starts up the channel. This can be called multiple times for individual services to start\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * Channel.DEFAULT - will start all services <BR>\r
- * Channel.MBR_RX_SEQ - starts the membership receiver <BR>\r
- * Channel.MBR_TX_SEQ - starts the membership broadcaster <BR>\r
- * Channel.SND_TX_SEQ - starts the replication transmitter<BR>\r
- * Channel.SND_RX_SEQ - starts the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already started.\r
- * @see Channel\r
- */\r
- public void start(int svc) throws ChannelException;\r
-\r
- /**\r
- * Shuts down the channel. This can be called multiple times for individual services to shutdown\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * Channel.DEFAULT - will shutdown all services <BR>\r
- * Channel.MBR_RX_SEQ - stops the membership receiver <BR>\r
- * Channel.MBR_TX_SEQ - stops the membership broadcaster <BR>\r
- * Channel.SND_TX_SEQ - stops the replication transmitter<BR>\r
- * Channel.SND_RX_SEQ - stops the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already started.\r
- * @see Channel\r
- */\r
- public void stop(int svc) throws ChannelException;\r
- \r
- public void fireInterceptorEvent(InterceptorEvent event);\r
-\r
- interface InterceptorEvent {\r
- int getEventType();\r
- String getEventTypeDesc();\r
- ChannelInterceptor getInterceptor();\r
- }\r
- \r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+import org.apache.catalina.tribes.group.InterceptorPayload;
+
+/**
+ * A ChannelInterceptor is an interceptor that intercepts
+ * messages and membership messages in the channel stack.
+ * This allows interceptors to modify the message or perform
+ * other actions when a message is sent or received.<br>
+ * Interceptors are tied together in a linked list.
+ * @see org.apache.catalina.tribes.group.ChannelInterceptorBase
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+public interface ChannelInterceptor extends MembershipListener, Heartbeat {
+
+ /**
+ * An interceptor can react to a message based on a set bit on the
+ * message options. <br>
+ * When a message is sent, the options can be retrieved from ChannelMessage.getOptions()
+ * and if the bit is set, this interceptor will react to it.<br>
+ * A simple evaluation if an interceptor should react to the message would be:<br>
+ * <code>boolean react = (getOptionFlag() == (getOptionFlag() & ChannelMessage.getOptions()));</code><br>
+ * The default option is 0, meaning there is no way for the application to trigger the
+ * interceptor. The interceptor itself will decide.<br>
+ * @return int
+ * @see ChannelMessage#getOptions()
+ */
+ public int getOptionFlag();
+
+ /**
+ * Sets the option flag
+ * @param flag int
+ * @see #getOptionFlag()
+ */
+ public void setOptionFlag(int flag);
+
+ /**
+ * Set the next interceptor in the list of interceptors
+ * @param next ChannelInterceptor
+ */
+ public void setNext(ChannelInterceptor next) ;
+
+ /**
+ * Retrieve the next interceptor in the list
+ * @return ChannelInterceptor - returns the next interceptor in the list or null if no more interceptors exist
+ */
+ public ChannelInterceptor getNext();
+
+ /**
+ * Set the previous interceptor in the list
+ * @param previous ChannelInterceptor
+ */
+ public void setPrevious(ChannelInterceptor previous);
+
+ /**
+ * Retrieve the previous interceptor in the list
+ * @return ChannelInterceptor - returns the previous interceptor in the list or null if no more interceptors exist
+ */
+ public ChannelInterceptor getPrevious();
+
+ /**
+ * The <code>sendMessage</code> method is called when a message is being sent to one more destinations.
+ * The interceptor can modify any of the parameters and then pass on the message down the stack by
+ * invoking <code>getNext().sendMessage(destination,msg,payload)</code><br>
+ * Alternatively the interceptor can stop the message from being sent by not invoking
+ * <code>getNext().sendMessage(destination,msg,payload)</code><br>
+ * If the message is to be sent asynchronous the application can be notified of completion and
+ * errors by passing in an error handler attached to a payload object.<br>
+ * The ChannelMessage.getAddress contains Channel.getLocalMember, and can be overwritten
+ * to simulate a message sent from another node.<br>
+ * @param destination Member[] - the destination for this message
+ * @param msg ChannelMessage - the message to be sent
+ * @param payload InterceptorPayload - the payload, carrying an error handler and future useful data, can be null
+ * @throws ChannelException
+ * @see ErrorHandler
+ * @see InterceptorPayload
+ */
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException;
+
+ /**
+ * the <code>messageReceived</code> is invoked when a message is received.
+ * <code>ChannelMessage.getAddress()</code> is the sender, or the reply-to address
+ * if it has been overwritten.
+ * @param data ChannelMessage
+ */
+ public void messageReceived(ChannelMessage data);
+
+ /**
+ * The <code>heartbeat()</code> method gets invoked periodically
+ * to allow interceptors to clean up resources, time out object and
+ * perform actions that are unrelated to sending/receiving data.
+ */
+ public void heartbeat();
+
+ /**
+ * Intercepts the <code>Channel.hasMembers()</code> method
+ * @return boolean - if the channel has members in its membership group
+ * @see Channel#hasMembers()
+ */
+ public boolean hasMembers() ;
+
+ /**
+ * Intercepts the code>Channel.getMembers()</code> method
+ * @return Member[]
+ * @see Channel#getMembers()
+ */
+ public Member[] getMembers() ;
+
+ /**
+ * Intercepts the code>Channel.getLocalMember(boolean)</code> method
+ * @param incAliveTime boolean
+ * @return Member
+ * @see Channel#getLocalMember(boolean)
+ */
+ public Member getLocalMember(boolean incAliveTime) ;
+
+ /**
+ * Intercepts the code>Channel.getMember(Member)</code> method
+ * @param mbr Member
+ * @return Member - the actual member information, including stay alive
+ * @see Channel#getMember(Member)
+ */
+ public Member getMember(Member mbr);
+
+ /**
+ * Starts up the channel. This can be called multiple times for individual services to start
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * Channel.DEFAULT - will start all services <BR>
+ * Channel.MBR_RX_SEQ - starts the membership receiver <BR>
+ * Channel.MBR_TX_SEQ - starts the membership broadcaster <BR>
+ * Channel.SND_TX_SEQ - starts the replication transmitter<BR>
+ * Channel.SND_RX_SEQ - starts the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already started.
+ * @see Channel
+ */
+ public void start(int svc) throws ChannelException;
+
+ /**
+ * Shuts down the channel. This can be called multiple times for individual services to shutdown
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * Channel.DEFAULT - will shutdown all services <BR>
+ * Channel.MBR_RX_SEQ - stops the membership receiver <BR>
+ * Channel.MBR_TX_SEQ - stops the membership broadcaster <BR>
+ * Channel.SND_TX_SEQ - stops the replication transmitter<BR>
+ * Channel.SND_RX_SEQ - stops the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already started.
+ * @see Channel
+ */
+ public void stop(int svc) throws ChannelException;
+
+ public void fireInterceptorEvent(InterceptorEvent event);
+
+ interface InterceptorEvent {
+ int getEventType();
+ String getEventTypeDesc();
+ ChannelInterceptor getInterceptor();
+ }
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-import java.io.Serializable;\r
-/**\r
- * \r
- * <p>Title: ChannelListener</p> \r
- * \r
- * <p>Description: An interface to listens to incoming messages from a channel </p> \r
- * When a message is received, the Channel will invoke the channel listener in a conditional sequence.\r
- * <code>if ( listener.accept(msg,sender) ) listener.messageReceived(msg,sender);</code><br>\r
- * A ChannelListener implementation MUST NOT return true on <code>accept(Serializable, Member)</code>\r
- * if it doesn't intend to process the message. The channel can this way track whether a message\r
- * was processed by an above application or if it was just received and forgot about, a featuer required\r
- * to support message-response(RPC) calls<br>\r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-\r
-public interface ChannelListener {\r
-\r
- /**\r
- * Receive a message from the channel\r
- * @param msg Serializable\r
- * @param sender - the source of the message\r
- */\r
- public void messageReceived(Serializable msg, Member sender);\r
-\r
- /**\r
- * Invoked by the channel to determine if the listener will process this message or not.\r
- * @param msg Serializable\r
- * @param sender Member\r
- * @return boolean\r
- */\r
- public boolean accept(Serializable msg, Member sender);\r
-\r
- /**\r
- * \r
- * @param listener Object\r
- * @return boolean\r
- * @see Object#equals(Object)\r
- */\r
- public boolean equals(Object listener);\r
-\r
- /**\r
- * \r
- * @return int\r
- * @see Object#hashCode(int)\r
- */\r
- public int hashCode();\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+import java.io.Serializable;
+/**
+ *
+ * <p>Title: ChannelListener</p>
+ *
+ * <p>Description: An interface to listens to incoming messages from a channel </p>
+ * When a message is received, the Channel will invoke the channel listener in a conditional sequence.
+ * <code>if ( listener.accept(msg,sender) ) listener.messageReceived(msg,sender);</code><br>
+ * A ChannelListener implementation MUST NOT return true on <code>accept(Serializable, Member)</code>
+ * if it doesn't intend to process the message. The channel can this way track whether a message
+ * was processed by an above application or if it was just received and forgot about, a featuer required
+ * to support message-response(RPC) calls<br>
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+
+public interface ChannelListener {
+
+ /**
+ * Receive a message from the channel
+ * @param msg Serializable
+ * @param sender - the source of the message
+ */
+ public void messageReceived(Serializable msg, Member sender);
+
+ /**
+ * Invoked by the channel to determine if the listener will process this message or not.
+ * @param msg Serializable
+ * @param sender Member
+ * @return boolean
+ */
+ public boolean accept(Serializable msg, Member sender);
+
+ /**
+ *
+ * @param listener Object
+ * @return boolean
+ * @see Object#equals(Object)
+ */
+ public boolean equals(Object listener);
+
+ /**
+ *
+ * @return int
+ * @see Object#hashCode(int)
+ */
+ public int hashCode();
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-import java.io.Serializable;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-\r
-/**\r
- * Message that is passed through the interceptor stack after the \r
- * data serialized in the Channel object and then passed down to the \r
- * interceptor and eventually down to the ChannelSender component\r
- * @author Filip Hanik\r
- * \r
- */\r
-public interface ChannelMessage extends Serializable {\r
- \r
- \r
- \r
- \r
- /**\r
- * Get the address that this message originated from. \r
- * Almost always <code>Channel.getLocalMember(boolean)</code><br>\r
- * This would be set to a different address \r
- * if the message was being relayed from a host other than the one\r
- * that originally sent it.\r
- * @return the source or reply-to address of this message\r
- */\r
- public Member getAddress();\r
-\r
- /**\r
- * Sets the source or reply-to address of this message\r
- * @param member Member\r
- */\r
- public void setAddress(Member member);\r
-\r
- /**\r
- * Timestamp of when the message was created.\r
- * @return long timestamp in milliseconds\r
- */\r
- public long getTimestamp();\r
-\r
- /**\r
- *\r
- * Sets the timestamp of this message\r
- * @param timestamp The timestamp\r
- */\r
- public void setTimestamp(long timestamp);\r
-\r
- /**\r
- * Each message must have a globally unique Id.\r
- * interceptors heavily depend on this id for message processing\r
- * @return byte\r
- */\r
- public byte[] getUniqueId();\r
- \r
- /**\r
- * The byte buffer that contains the actual message payload\r
- * @param buf XByteBuffer\r
- */\r
- public void setMessage(XByteBuffer buf);\r
- \r
- /**\r
- * returns the byte buffer that contains the actual message payload\r
- * @return XByteBuffer\r
- */\r
- public XByteBuffer getMessage();\r
- \r
- /**\r
- * The message options is a 32 bit flag set\r
- * that triggers interceptors and message behavior.\r
- * @see Channel#send(Member[], Serializable, int) \r
- * @see ChannelInterceptor#getOptionFlag\r
- * @return int - the option bits set for this message\r
- */\r
- public int getOptions();\r
- \r
- /**\r
- * sets the option bits for this message\r
- * @param options int\r
- * @see #getOptions()\r
- */\r
- public void setOptions(int options);\r
- \r
- /**\r
- * Shallow clone, what gets cloned depends on the implementation\r
- * @return ChannelMessage\r
- */\r
- public Object clone();\r
-\r
- /**\r
- * Deep clone, all fields MUST get cloned\r
- * @return ChannelMessage\r
- */\r
- public Object deepclone();\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+import java.io.Serializable;
+import org.apache.catalina.tribes.io.XByteBuffer;
+
+/**
+ * Message that is passed through the interceptor stack after the
+ * data serialized in the Channel object and then passed down to the
+ * interceptor and eventually down to the ChannelSender component
+ * @author Filip Hanik
+ *
+ */
+public interface ChannelMessage extends Serializable {
+
+
+
+
+ /**
+ * Get the address that this message originated from.
+ * Almost always <code>Channel.getLocalMember(boolean)</code><br>
+ * This would be set to a different address
+ * if the message was being relayed from a host other than the one
+ * that originally sent it.
+ * @return the source or reply-to address of this message
+ */
+ public Member getAddress();
+
+ /**
+ * Sets the source or reply-to address of this message
+ * @param member Member
+ */
+ public void setAddress(Member member);
+
+ /**
+ * Timestamp of when the message was created.
+ * @return long timestamp in milliseconds
+ */
+ public long getTimestamp();
+
+ /**
+ *
+ * Sets the timestamp of this message
+ * @param timestamp The timestamp
+ */
+ public void setTimestamp(long timestamp);
+
+ /**
+ * Each message must have a globally unique Id.
+ * interceptors heavily depend on this id for message processing
+ * @return byte
+ */
+ public byte[] getUniqueId();
+
+ /**
+ * The byte buffer that contains the actual message payload
+ * @param buf XByteBuffer
+ */
+ public void setMessage(XByteBuffer buf);
+
+ /**
+ * returns the byte buffer that contains the actual message payload
+ * @return XByteBuffer
+ */
+ public XByteBuffer getMessage();
+
+ /**
+ * The message options is a 32 bit flag set
+ * that triggers interceptors and message behavior.
+ * @see Channel#send(Member[], Serializable, int)
+ * @see ChannelInterceptor#getOptionFlag
+ * @return int - the option bits set for this message
+ */
+ public int getOptions();
+
+ /**
+ * sets the option bits for this message
+ * @param options int
+ * @see #getOptions()
+ */
+ public void setOptions(int options);
+
+ /**
+ * Shallow clone, what gets cloned depends on the implementation
+ * @return ChannelMessage
+ */
+ public Object clone();
+
+ /**
+ * Deep clone, all fields MUST get cloned
+ * @return ChannelMessage
+ */
+ public Object deepclone();
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes;\r
-\r
-\r
-/**\r
- * ChannelReceiver Interface<br>\r
- * The <code>ChannelReceiver</code> interface is the data receiver component \r
- * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel} interface).\r
- * This class may optionally implement a thread pool for parallel processing of incoming messages.\r
- * @author Filip Hanik\r
- * @version $Revision: 379904 $, $Date: 2006-02-22 15:16:25 -0600 (Wed, 22 Feb 2006) $\r
- */\r
-public interface ChannelReceiver extends Heartbeat {\r
- /**\r
- * Start listening for incoming messages on the host/port\r
- * @throws java.io.IOException\r
- */\r
- public void start() throws java.io.IOException;\r
-\r
- /**\r
- * Stop listening for messages\r
- */\r
- public void stop();\r
-\r
- /**\r
- * String representation of the IPv4 or IPv6 address that this host is listening\r
- * to.\r
- * @return the host that this receiver is listening to\r
- */\r
- public String getHost();\r
- \r
- \r
- /**\r
- * Returns the listening port\r
- * @return port\r
- */\r
- public int getPort();\r
- \r
- /**\r
- * Returns the secure listening port\r
- * @return port, -1 if a secure port is not activated\r
- */\r
- public int getSecurePort();\r
- \r
- /**\r
- * Sets the message listener to receive notification of incoming\r
- * @param listener MessageListener\r
- * @see MessageListener\r
- */\r
- public void setMessageListener(MessageListener listener);\r
- \r
- /**\r
- * Returns the message listener that is associated with this receiver\r
- * @return MessageListener\r
- * @see MessageListener\r
- */\r
- public MessageListener getMessageListener();\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+
+/**
+ * ChannelReceiver Interface<br>
+ * The <code>ChannelReceiver</code> interface is the data receiver component
+ * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel} interface).
+ * This class may optionally implement a thread pool for parallel processing of incoming messages.
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+public interface ChannelReceiver extends Heartbeat {
+ /**
+ * Start listening for incoming messages on the host/port
+ * @throws java.io.IOException
+ */
+ public void start() throws java.io.IOException;
+
+ /**
+ * Stop listening for messages
+ */
+ public void stop();
+
+ /**
+ * String representation of the IPv4 or IPv6 address that this host is listening
+ * to.
+ * @return the host that this receiver is listening to
+ */
+ public String getHost();
+
+
+ /**
+ * Returns the listening port
+ * @return port
+ */
+ public int getPort();
+
+ /**
+ * Returns the secure listening port
+ * @return port, -1 if a secure port is not activated
+ */
+ public int getSecurePort();
+
+ /**
+ * Sets the message listener to receive notification of incoming
+ * @param listener MessageListener
+ * @see MessageListener
+ */
+ public void setMessageListener(MessageListener listener);
+
+ /**
+ * Returns the message listener that is associated with this receiver
+ * @return MessageListener
+ * @see MessageListener
+ */
+ public MessageListener getMessageListener();
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes;\r
-\r
-\r
-/**\r
- * ChannelReceiver Interface<br>\r
- * The <code>ChannelSender</code> interface is the data sender component \r
- * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel} interface).<br>\r
- * The channel sender must support "silent" members, ie, be able to send a message to a member\r
- * that is not in the membership, but is part of the destination parameter\r
- * @author Filip Hanik\r
- * @version $Revision: 379904 $, $Date: 2006-02-22 15:16:25 -0600 (Wed, 22 Feb 2006) $\r
- */\r
-public interface ChannelSender extends Heartbeat\r
-{\r
- /**\r
- * Notify the sender of a member being added to the group.<br>\r
- * Optional. This can be an empty implementation, that does nothing\r
- * @param member Member\r
- */\r
- public void add(Member member);\r
- /**\r
- * Notification that a member has been removed or crashed.\r
- * Can be used to clean up open connections etc\r
- * @param member Member\r
- */\r
- public void remove(Member member);\r
- \r
- /**\r
- * Start the channel sender\r
- * @throws IOException if preprocessing takes place and an error happens\r
- */\r
- public void start() throws java.io.IOException;\r
- /**\r
- * Stop the channel sender\r
- */\r
- public void stop();\r
- \r
- /**\r
- * A channel heartbeat, use this method to clean up resources\r
- */\r
- public void heartbeat() ;\r
- \r
- /**\r
- * Send a message to one or more recipients.\r
- * @param message ChannelMessage - the message to be sent\r
- * @param destination Member[] - the destinations\r
- * @throws ChannelException - if an error happens, the ChannelSender MUST report\r
- * individual send failures on a per member basis, using ChannelException.addFaultyMember\r
- * @see ChannelException#addFaultyMember(Member,java.lang.Exception)\r
- */\r
- public void sendMessage(ChannelMessage message, Member[] destination) throws ChannelException;\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+
+/**
+ * ChannelReceiver Interface<br>
+ * The <code>ChannelSender</code> interface is the data sender component
+ * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel} interface).<br>
+ * The channel sender must support "silent" members, ie, be able to send a message to a member
+ * that is not in the membership, but is part of the destination parameter
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+public interface ChannelSender extends Heartbeat
+{
+ /**
+ * Notify the sender of a member being added to the group.<br>
+ * Optional. This can be an empty implementation, that does nothing
+ * @param member Member
+ */
+ public void add(Member member);
+ /**
+ * Notification that a member has been removed or crashed.
+ * Can be used to clean up open connections etc
+ * @param member Member
+ */
+ public void remove(Member member);
+
+ /**
+ * Start the channel sender
+ * @throws IOException if preprocessing takes place and an error happens
+ */
+ public void start() throws java.io.IOException;
+ /**
+ * Stop the channel sender
+ */
+ public void stop();
+
+ /**
+ * A channel heartbeat, use this method to clean up resources
+ */
+ public void heartbeat() ;
+
+ /**
+ * Send a message to one or more recipients.
+ * @param message ChannelMessage - the message to be sent
+ * @param destination Member[] - the destinations
+ * @throws ChannelException - if an error happens, the ChannelSender MUST report
+ * individual send failures on a per member basis, using ChannelException.addFaultyMember
+ * @see ChannelException#addFaultyMember(Member,java.lang.Exception)
+ */
+ public void sendMessage(ChannelMessage message, Member[] destination) throws ChannelException;
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.tribes;\r
-\r
-/**\r
- * Manifest constants for the <code>org.apache.catalina.tribes</code>\r
- * package.\r
- *\r
- * @author Bip Thelin\r
- * @author Filip Hanik\r
- * @version $Revision: 302726 $, $Date: 2004-02-27 08:59:07 -0600 (Fri, 27 Feb 2004) $\r
- */\r
-\r
-public final class Constants {\r
- public static final String Package = "org.apache.catalina.tribes";\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.tribes</code>
+ * package.
+ *
+ * @author Bip Thelin
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+public final class Constants {
+ public static final String Package = "org.apache.catalina.tribes";
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes;\r
-\r
-\r
-\r
-/**\r
- * The <code>ErrorHandler</code> class is used when sending messages\r
- * that are sent asynchronously and the application still needs to get \r
- * confirmation when the message was sent successfully or when a message errored out.\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public interface ErrorHandler {\r
- \r
- /**\r
- * Invoked if the message is dispatched asynch, and an error occurs\r
- * @param x ChannelException - the error that happened\r
- * @param id - the unique id for the message\r
- * @see Channel#send(Member[], Serializable, int, ErrorHandler)\r
- */\r
- public void handleError(ChannelException x, UniqueId id);\r
- \r
- /**\r
- * Invoked when the message has been sent successfully.\r
- * @param id - the unique id for the message\r
- * @see Channel#send(Member[], Serializable, int, ErrorHandler)\r
- */\r
- public void handleCompletion(UniqueId id);\r
- \r
+/*
+ * 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.apache.catalina.tribes;
+
+
+
+/**
+ * The <code>ErrorHandler</code> class is used when sending messages
+ * that are sent asynchronously and the application still needs to get
+ * confirmation when the message was sent successfully or when a message errored out.
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public interface ErrorHandler {
+
+ /**
+ * Invoked if the message is dispatched asynch, and an error occurs
+ * @param x ChannelException - the error that happened
+ * @param id - the unique id for the message
+ * @see Channel#send(Member[], Serializable, int, ErrorHandler)
+ */
+ public void handleError(ChannelException x, UniqueId id);
+
+ /**
+ * Invoked when the message has been sent successfully.
+ * @param id - the unique id for the message
+ * @see Channel#send(Member[], Serializable, int, ErrorHandler)
+ */
+ public void handleCompletion(UniqueId id);
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-/**\r
- * Can be implemented by the ChannelListener and Membership listeners to receive heartbeat\r
- * notifications from the Channel\r
- * @author Filip Hanik\r
- * @version 1.0\r
- * @see Channel\r
- * @see Channel#heartbeat()\r
- */\r
-public interface Heartbeat {\r
- \r
- /**\r
- * Heartbeat invokation for resources cleanup etc\r
- */\r
- public void heartbeat();\r
-\r
+/*
+ * 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.apache.catalina.tribes;
+
+/**
+ * Can be implemented by the ChannelListener and Membership listeners to receive heartbeat
+ * notifications from the Channel
+ * @author Filip Hanik
+ * @version 1.0
+ * @see Channel
+ * @see Channel#heartbeat()
+ */
+public interface Heartbeat {
+
+ /**
+ * Heartbeat invokation for resources cleanup etc
+ */
+ public void heartbeat();
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-import java.util.Iterator;\r
-\r
-/**\r
- * Channel interface\r
- * A managed channel interface gives you access to the components of the channels\r
- * such as senders, receivers, interceptors etc for configurations purposes\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-public interface ManagedChannel extends Channel {\r
-\r
- /**\r
- * Sets the channel sender\r
- * @param sender ChannelSender\r
- * @see ChannelSender\r
- */\r
- public void setChannelSender(ChannelSender sender);\r
-\r
- /**\r
- * Sets the channel receiver\r
- * @param receiver ChannelReceiver\r
- * @see ChannelReceiver\r
- */\r
- public void setChannelReceiver(ChannelReceiver receiver);\r
- \r
- /**\r
- * Sets the membership service\r
- * @param service MembershipService\r
- * @see MembershipService\r
- */\r
- public void setMembershipService(MembershipService service);\r
-\r
- /**\r
- * returns the channel sender\r
- * @return ChannelSender\r
- * @see ChannelSender\r
- */\r
- public ChannelSender getChannelSender();\r
- \r
- /**\r
- * returns the channel receiver\r
- * @return ChannelReceiver\r
- * @see ChannelReceiver\r
- */\r
- public ChannelReceiver getChannelReceiver();\r
- \r
- /**\r
- * Returns the membership service\r
- * @return MembershipService\r
- * @see MembershipService\r
- */\r
- public MembershipService getMembershipService();\r
-\r
- /**\r
- * Returns the interceptor stack\r
- * @return Iterator\r
- * @see Channel#addInterceptor(ChannelInterceptor)\r
- */\r
- public Iterator getInterceptors();\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+import java.util.Iterator;
+
+/**
+ * Channel interface
+ * A managed channel interface gives you access to the components of the channels
+ * such as senders, receivers, interceptors etc for configurations purposes
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+public interface ManagedChannel extends Channel {
+
+ /**
+ * Sets the channel sender
+ * @param sender ChannelSender
+ * @see ChannelSender
+ */
+ public void setChannelSender(ChannelSender sender);
+
+ /**
+ * Sets the channel receiver
+ * @param receiver ChannelReceiver
+ * @see ChannelReceiver
+ */
+ public void setChannelReceiver(ChannelReceiver receiver);
+
+ /**
+ * Sets the membership service
+ * @param service MembershipService
+ * @see MembershipService
+ */
+ public void setMembershipService(MembershipService service);
+
+ /**
+ * returns the channel sender
+ * @return ChannelSender
+ * @see ChannelSender
+ */
+ public ChannelSender getChannelSender();
+
+ /**
+ * returns the channel receiver
+ * @return ChannelReceiver
+ * @see ChannelReceiver
+ */
+ public ChannelReceiver getChannelReceiver();
+
+ /**
+ * Returns the membership service
+ * @return MembershipService
+ * @see MembershipService
+ */
+ public MembershipService getMembershipService();
+
+ /**
+ * Returns the interceptor stack
+ * @return Iterator
+ * @see Channel#addInterceptor(ChannelInterceptor)
+ */
+ public Iterator getInterceptors();
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes;\r
-\r
-/**\r
- * The Member interface, defines a member in the group.\r
- * Each member can carry a set of properties, defined by the actual implementation.<BR>\r
- * A member is identified by the host/ip/uniqueId<br>\r
- * The host is what interface the member is listening to, to receive data<br>\r
- * The port is what port the member is listening to, to receive data<br>\r
- * The uniqueId defines the session id for the member. This is an important feature\r
- * since a member that has crashed and the starts up again on the same port/host is \r
- * not guaranteed to be the same member, so no state transfers will ever be confused\r
- * @author Filip Hanik\r
- * @version $Revision: 303950 $, $Date: 2005-06-09 15:38:30 -0500 (Thu, 09 Jun 2005) $\r
- */\r
-\r
-\r
-public interface Member {\r
- \r
- /**\r
- * When a member leaves the cluster, the payload of the memberDisappeared member\r
- * will be the following bytes. This indicates a soft shutdown, and not a crash\r
- */\r
- public static final byte[] SHUTDOWN_PAYLOAD = new byte[] {66, 65, 66, 89, 45, 65, 76, 69, 88};\r
- \r
- /**\r
- * Returns the name of this node, should be unique within the group.\r
- */\r
- public String getName();\r
- \r
- /**\r
- * Returns the listen host for the ChannelReceiver implementation\r
- * @return IPv4 or IPv6 representation of the host address this member listens to incoming data\r
- * @see ChannelReceiver\r
- */\r
- public byte[] getHost();\r
-\r
- /**\r
- * Returns the listen port for the ChannelReceiver implementation\r
- * @return the listen port for this member, -1 if its not listening on an unsecure port\r
- * @see ChannelReceiver\r
- */\r
- public int getPort();\r
- \r
- /**\r
- * Returns the secure listen port for the ChannelReceiver implementation.\r
- * Returns -1 if its not listening to a secure port.\r
- * @return the listen port for this member, -1 if its not listening on a secure port\r
- * @see ChannelReceiver\r
- */\r
- public int getSecurePort();\r
- \r
-\r
- /**\r
- * Contains information on how long this member has been online.\r
- * The result is the number of milli seconds this member has been\r
- * broadcasting its membership to the group.\r
- * @return nr of milliseconds since this member started.\r
- */\r
- public long getMemberAliveTime();\r
- \r
- /**\r
- * The current state of the member\r
- * @return boolean - true if the member is functioning correctly\r
- */\r
- public boolean isReady();\r
- /**\r
- * The current state of the member\r
- * @return boolean - true if the member is suspect, but the crash has not been confirmed\r
- */\r
- public boolean isSuspect();\r
- \r
- /**\r
- * \r
- * @return boolean - true if the member has been confirmed to malfunction \r
- */\r
- public boolean isFailing();\r
- \r
- /**\r
- * returns a UUID unique for this member over all sessions.\r
- * If the member crashes and restarts, the uniqueId will be different.\r
- * @return byte[]\r
- */\r
- public byte[] getUniqueId();\r
- \r
- /**\r
- * returns the payload associated with this member\r
- * @return byte[]\r
- */\r
- public byte[] getPayload();\r
- \r
- /**\r
- * returns the command associated with this member\r
- * @return byte[]\r
- */\r
- public byte[] getCommand();\r
- \r
- /**\r
- * Domain for this cluster\r
- * @return byte[]\r
- */\r
- public byte[] getDomain();\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+/**
+ * The Member interface, defines a member in the group.
+ * Each member can carry a set of properties, defined by the actual implementation.<BR>
+ * A member is identified by the host/ip/uniqueId<br>
+ * The host is what interface the member is listening to, to receive data<br>
+ * The port is what port the member is listening to, to receive data<br>
+ * The uniqueId defines the session id for the member. This is an important feature
+ * since a member that has crashed and the starts up again on the same port/host is
+ * not guaranteed to be the same member, so no state transfers will ever be confused
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public interface Member {
+
+ /**
+ * When a member leaves the cluster, the payload of the memberDisappeared member
+ * will be the following bytes. This indicates a soft shutdown, and not a crash
+ */
+ public static final byte[] SHUTDOWN_PAYLOAD = new byte[] {66, 65, 66, 89, 45, 65, 76, 69, 88};
+
+ /**
+ * Returns the name of this node, should be unique within the group.
+ */
+ public String getName();
+
+ /**
+ * Returns the listen host for the ChannelReceiver implementation
+ * @return IPv4 or IPv6 representation of the host address this member listens to incoming data
+ * @see ChannelReceiver
+ */
+ public byte[] getHost();
+
+ /**
+ * Returns the listen port for the ChannelReceiver implementation
+ * @return the listen port for this member, -1 if its not listening on an unsecure port
+ * @see ChannelReceiver
+ */
+ public int getPort();
+
+ /**
+ * Returns the secure listen port for the ChannelReceiver implementation.
+ * Returns -1 if its not listening to a secure port.
+ * @return the listen port for this member, -1 if its not listening on a secure port
+ * @see ChannelReceiver
+ */
+ public int getSecurePort();
+
+
+ /**
+ * Contains information on how long this member has been online.
+ * The result is the number of milli seconds this member has been
+ * broadcasting its membership to the group.
+ * @return nr of milliseconds since this member started.
+ */
+ public long getMemberAliveTime();
+
+ /**
+ * The current state of the member
+ * @return boolean - true if the member is functioning correctly
+ */
+ public boolean isReady();
+ /**
+ * The current state of the member
+ * @return boolean - true if the member is suspect, but the crash has not been confirmed
+ */
+ public boolean isSuspect();
+
+ /**
+ *
+ * @return boolean - true if the member has been confirmed to malfunction
+ */
+ public boolean isFailing();
+
+ /**
+ * returns a UUID unique for this member over all sessions.
+ * If the member crashes and restarts, the uniqueId will be different.
+ * @return byte[]
+ */
+ public byte[] getUniqueId();
+
+ /**
+ * returns the payload associated with this member
+ * @return byte[]
+ */
+ public byte[] getPayload();
+
+ /**
+ * returns the command associated with this member
+ * @return byte[]
+ */
+ public byte[] getCommand();
+
+ /**
+ * Domain for this cluster
+ * @return byte[]
+ */
+ public byte[] getDomain();
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes;\r
-\r
-/**\r
- * The MembershipListener interface is used as a callback to the\r
- * membership service. It has two methods that will notify the listener\r
- * when a member has joined the group and when a member has disappeared (crashed)\r
- *\r
- * @author Filip Hanik\r
- * @version $Revision: 302726 $, $Date: 2004-02-27 08:59:07 -0600 (Fri, 27 Feb 2004) $\r
- */\r
-\r
-\r
-public interface MembershipListener {\r
- /**\r
- * A member was added to the group\r
- * @param member Member - the member that was added\r
- */\r
- public void memberAdded(Member member);\r
- \r
- /**\r
- * A member was removed from the group<br>\r
- * If the member left voluntarily, the Member.getCommand will contain the Member.SHUTDOWN_PAYLOAD data\r
- * @param member Member\r
- * @see Member#SHUTDOWN_PAYLOAD\r
- */\r
- public void memberDisappeared(Member member);\r
-\r
+/*
+ * 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.apache.catalina.tribes;
+
+/**
+ * The MembershipListener interface is used as a callback to the
+ * membership service. It has two methods that will notify the listener
+ * when a member has joined the group and when a member has disappeared (crashed)
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public interface MembershipListener {
+ /**
+ * A member was added to the group
+ * @param member Member - the member that was added
+ */
+ public void memberAdded(Member member);
+
+ /**
+ * A member was removed from the group<br>
+ * If the member left voluntarily, the Member.getCommand will contain the Member.SHUTDOWN_PAYLOAD data
+ * @param member Member
+ * @see Member#SHUTDOWN_PAYLOAD
+ */
+ public void memberDisappeared(Member member);
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes;\r
-\r
-\r
-/**\r
- * MembershipService Interface<br>\r
- * The <code>MembershipService</code> interface is the membership component \r
- * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel} interface).<br>\r
- * @author Filip Hanik\r
- * @version $Revision: 378093 $, $Date: 2006-02-15 15:13:45 -0600 (Wed, 15 Feb 2006) $\r
- */\r
-\r
-\r
-public interface MembershipService {\r
- \r
- public static final int MBR_RX = Channel.MBR_RX_SEQ;\r
- public static final int MBR_TX = Channel.MBR_TX_SEQ;\r
- \r
- /**\r
- * Sets the properties for the membership service. This must be called before\r
- * the <code>start()</code> method is called.\r
- * The properties are implementation specific.\r
- * @param properties - to be used to configure the membership service.\r
- */\r
- public void setProperties(java.util.Properties properties);\r
- /**\r
- * Returns the properties for the configuration used.\r
- */\r
- public java.util.Properties getProperties();\r
- /**\r
- * Starts the membership service. If a membership listeners is added\r
- * the listener will start to receive membership events.\r
- * Performs a start level 1 and 2\r
- * @throws java.lang.Exception if the service fails to start.\r
- */\r
- public void start() throws java.lang.Exception;\r
-\r
- /**\r
- * Starts the membership service. If a membership listeners is added\r
- * the listener will start to receive membership events.\r
- * @param level - level MBR_RX starts listening for members, level MBR_TX \r
- * starts broad casting the server\r
- * @throws java.lang.Exception if the service fails to start.\r
- * @throws java.lang.IllegalArgumentException if the level is incorrect.\r
- */\r
- public void start(int level) throws java.lang.Exception;\r
-\r
-\r
- /**\r
- * Starts the membership service. If a membership listeners is added\r
- * the listener will start to receive membership events.\r
- * @param level - level MBR_RX stops listening for members, level MBR_TX \r
- * stops broad casting the server\r
- * @throws java.lang.Exception if the service fails to stop\r
- * @throws java.lang.IllegalArgumentException if the level is incorrect.\r
- */\r
-\r
- public void stop(int level);\r
- \r
- /**\r
- * @return true if the the group contains members\r
- */\r
- public boolean hasMembers();\r
- \r
- \r
- /**\r
- * \r
- * @param mbr Member\r
- * @return Member\r
- */\r
- public Member getMember(Member mbr);\r
- /**\r
- * Returns a list of all the members in the cluster.\r
- */\r
- \r
- public Member[] getMembers();\r
- \r
- /**\r
- * Returns the member object that defines this member\r
- */\r
- public Member getLocalMember(boolean incAliveTime);\r
-\r
- /**\r
- * Return all members by name\r
- */\r
- public String[] getMembersByName() ; \r
- \r
- /**\r
- * Return the member by name\r
- */\r
- public Member findMemberByName(String name) ;\r
-\r
- /**\r
- * Sets the local member properties for broadcasting\r
- */\r
- public void setLocalMemberProperties(String listenHost, int listenPort);\r
- \r
- /**\r
- * Sets the membership listener, only one listener can be added.\r
- * If you call this method twice, the last listener will be used.\r
- * @param listener The listener\r
- */\r
- public void setMembershipListener(MembershipListener listener);\r
- \r
- /**\r
- * removes the membership listener.\r
- */\r
- public void removeMembershipListener();\r
- \r
- /**\r
- * Set a payload to be broadcasted with each membership \r
- * broadcast.\r
- * @param payload byte[]\r
- */\r
- public void setPayload(byte[] payload);\r
- \r
- public void setDomain(byte[] domain);\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+
+/**
+ * MembershipService Interface<br>
+ * The <code>MembershipService</code> interface is the membership component
+ * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel} interface).<br>
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public interface MembershipService {
+
+ public static final int MBR_RX = Channel.MBR_RX_SEQ;
+ public static final int MBR_TX = Channel.MBR_TX_SEQ;
+
+ /**
+ * Sets the properties for the membership service. This must be called before
+ * the <code>start()</code> method is called.
+ * The properties are implementation specific.
+ * @param properties - to be used to configure the membership service.
+ */
+ public void setProperties(java.util.Properties properties);
+ /**
+ * Returns the properties for the configuration used.
+ */
+ public java.util.Properties getProperties();
+ /**
+ * Starts the membership service. If a membership listeners is added
+ * the listener will start to receive membership events.
+ * Performs a start level 1 and 2
+ * @throws java.lang.Exception if the service fails to start.
+ */
+ public void start() throws java.lang.Exception;
+
+ /**
+ * Starts the membership service. If a membership listeners is added
+ * the listener will start to receive membership events.
+ * @param level - level MBR_RX starts listening for members, level MBR_TX
+ * starts broad casting the server
+ * @throws java.lang.Exception if the service fails to start.
+ * @throws java.lang.IllegalArgumentException if the level is incorrect.
+ */
+ public void start(int level) throws java.lang.Exception;
+
+
+ /**
+ * Starts the membership service. If a membership listeners is added
+ * the listener will start to receive membership events.
+ * @param level - level MBR_RX stops listening for members, level MBR_TX
+ * stops broad casting the server
+ * @throws java.lang.Exception if the service fails to stop
+ * @throws java.lang.IllegalArgumentException if the level is incorrect.
+ */
+
+ public void stop(int level);
+
+ /**
+ * @return true if the the group contains members
+ */
+ public boolean hasMembers();
+
+
+ /**
+ *
+ * @param mbr Member
+ * @return Member
+ */
+ public Member getMember(Member mbr);
+ /**
+ * Returns a list of all the members in the cluster.
+ */
+
+ public Member[] getMembers();
+
+ /**
+ * Returns the member object that defines this member
+ */
+ public Member getLocalMember(boolean incAliveTime);
+
+ /**
+ * Return all members by name
+ */
+ public String[] getMembersByName() ;
+
+ /**
+ * Return the member by name
+ */
+ public Member findMemberByName(String name) ;
+
+ /**
+ * Sets the local member properties for broadcasting
+ */
+ public void setLocalMemberProperties(String listenHost, int listenPort);
+
+ /**
+ * Sets the membership listener, only one listener can be added.
+ * If you call this method twice, the last listener will be used.
+ * @param listener The listener
+ */
+ public void setMembershipListener(MembershipListener listener);
+
+ /**
+ * removes the membership listener.
+ */
+ public void removeMembershipListener();
+
+ /**
+ * Set a payload to be broadcasted with each membership
+ * broadcast.
+ * @param payload byte[]
+ */
+ public void setPayload(byte[] payload);
+
+ public void setDomain(byte[] domain);
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-/**\r
- * \r
- * <p>Title: MessageListener</p> \r
- * \r
- * <p>Description: The listener to be registered with the ChannelReceiver, internal Tribes component</p> \r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-\r
-public interface MessageListener {\r
- \r
- /**\r
- * Receive a message from the IO components in the Channel stack\r
- * @param msg ChannelMessage\r
- */\r
- public void messageReceived(ChannelMessage msg);\r
- \r
- public boolean accept(ChannelMessage msg);\r
- \r
- public boolean equals(Object listener);\r
- \r
- public int hashCode();\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes;
+
+/**
+ *
+ * <p>Title: MessageListener</p>
+ *
+ * <p>Description: The listener to be registered with the ChannelReceiver, internal Tribes component</p>
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+
+public interface MessageListener {
+
+ /**
+ * Receive a message from the IO components in the Channel stack
+ * @param msg ChannelMessage
+ */
+ public void messageReceived(ChannelMessage msg);
+
+ public boolean accept(ChannelMessage msg);
+
+ public boolean equals(Object listener);
+
+ public int hashCode();
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes;\r
-\r
-import org.apache.catalina.tribes.util.Arrays;\r
-import java.io.Serializable;\r
-\r
-/**\r
- * <p>Title: Represents a globabally unique Id</p>\r
- *\r
- * <p>Company: </p>\r
- *\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public final class UniqueId implements Serializable{\r
- protected byte[] id;\r
- \r
- public UniqueId() {\r
- }\r
-\r
- public UniqueId(byte[] id) {\r
- this.id = id;\r
- }\r
- \r
- public UniqueId(byte[] id, int offset, int length) {\r
- this.id = new byte[length];\r
- System.arraycopy(id,offset,this.id,0,length);\r
- }\r
- \r
- public int hashCode() {\r
- if ( id == null ) return 0;\r
- return Arrays.hashCode(id);\r
- }\r
- \r
- public boolean equals(Object other) {\r
- boolean result = (other instanceof UniqueId);\r
- if ( result ) {\r
- UniqueId uid = (UniqueId)other;\r
- if ( this.id == null && uid.id == null ) result = true;\r
- else if ( this.id == null && uid.id != null ) result = false;\r
- else if ( this.id != null && uid.id == null ) result = false;\r
- else result = Arrays.equals(this.id,uid.id);\r
- }//end if\r
- return result;\r
- }\r
- \r
- public byte[] getBytes() {\r
- return id;\r
- }\r
- \r
- public String toString() {\r
- StringBuffer buf = new StringBuffer("UniqueId");\r
- buf.append(org.apache.catalina.tribes.util.Arrays.toString(id));\r
- return buf.toString();\r
- }\r
-\r
+/*
+ * 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.apache.catalina.tribes;
+
+import org.apache.catalina.tribes.util.Arrays;
+import java.io.Serializable;
+
+/**
+ * <p>Title: Represents a globabally unique Id</p>
+ *
+ * <p>Company: </p>
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public final class UniqueId implements Serializable{
+ protected byte[] id;
+
+ public UniqueId() {
+ }
+
+ public UniqueId(byte[] id) {
+ this.id = id;
+ }
+
+ public UniqueId(byte[] id, int offset, int length) {
+ this.id = new byte[length];
+ System.arraycopy(id,offset,this.id,0,length);
+ }
+
+ public int hashCode() {
+ if ( id == null ) return 0;
+ return Arrays.hashCode(id);
+ }
+
+ public boolean equals(Object other) {
+ boolean result = (other instanceof UniqueId);
+ if ( result ) {
+ UniqueId uid = (UniqueId)other;
+ if ( this.id == null && uid.id == null ) result = true;
+ else if ( this.id == null && uid.id != null ) result = false;
+ else if ( this.id != null && uid.id == null ) result = false;
+ else result = Arrays.equals(this.id,uid.id);
+ }//end if
+ return result;
+ }
+
+ public byte[] getBytes() {
+ return id;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer("UniqueId");
+ buf.append(org.apache.catalina.tribes.util.Arrays.toString(id));
+ return buf.toString();
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.group;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.ChannelReceiver;\r
-import org.apache.catalina.tribes.ChannelSender;\r
-\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.MembershipService;\r
-import org.apache.catalina.tribes.MessageListener;\r
-import org.apache.catalina.tribes.transport.SenderState;\r
-import org.apache.catalina.tribes.transport.ReplicationTransmitter;\r
-import org.apache.catalina.tribes.membership.McastService;\r
-import org.apache.catalina.tribes.transport.nio.NioReceiver;\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.util.Logs;\r
-import org.apache.catalina.tribes.UniqueId;\r
-import org.apache.catalina.tribes.util.Arrays;\r
-\r
-\r
-/**\r
- * The channel coordinator object coordinates the membership service,\r
- * the sender and the receiver.\r
- * This is the last interceptor in the chain.\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-public class ChannelCoordinator extends ChannelInterceptorBase implements MessageListener {\r
- private ChannelReceiver clusterReceiver = new NioReceiver();\r
- private ChannelSender clusterSender = new ReplicationTransmitter();\r
- private MembershipService membershipService = new McastService();\r
- \r
- //override optionflag\r
- protected int optionFlag = Channel.SEND_OPTIONS_BYTE_MESSAGE|Channel.SEND_OPTIONS_USE_ACK|Channel.SEND_OPTIONS_SYNCHRONIZED_ACK;\r
- public int getOptionFlag() {return optionFlag;}\r
- public void setOptionFlag(int flag) {optionFlag=flag;}\r
- \r
- private int startLevel = 0;\r
-\r
- public ChannelCoordinator() {\r
- \r
- }\r
- \r
- public ChannelCoordinator(ChannelReceiver receiver,\r
- ChannelSender sender,\r
- MembershipService service) {\r
- this();\r
- this.setClusterReceiver(receiver);\r
- this.setClusterSender(sender);\r
- this.setMembershipService(service);\r
- }\r
- \r
- /**\r
- * Send a message to one or more members in the cluster\r
- * @param destination Member[] - the destinations, null or zero length means all\r
- * @param msg ClusterMessage - the message to send\r
- * @param options int - sender options, see class documentation\r
- * @return ClusterMessage[] - the replies from the members, if any.\r
- */\r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- if ( destination == null ) destination = membershipService.getMembers();\r
- clusterSender.sendMessage(msg,destination);\r
- if ( Logs.MESSAGES.isTraceEnabled() ) {\r
- Logs.MESSAGES.trace("ChannelCoordinator - Sent msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " to "+Arrays.toNameString(destination));\r
- }\r
- }\r
- \r
-\r
- /**\r
- * Starts up the channel. This can be called multiple times for individual services to start\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * DEFAULT - will start all services <BR>\r
- * MBR_RX_SEQ - starts the membership receiver <BR>\r
- * MBR_TX_SEQ - starts the membership broadcaster <BR>\r
- * SND_TX_SEQ - starts the replication transmitter<BR>\r
- * SND_RX_SEQ - starts the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already started.\r
- */\r
- public void start(int svc) throws ChannelException {\r
- this.internalStart(svc);\r
- }\r
-\r
- /**\r
- * Shuts down the channel. This can be called multiple times for individual services to shutdown\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * DEFAULT - will shutdown all services <BR>\r
- * MBR_RX_SEQ - stops the membership receiver <BR>\r
- * MBR_TX_SEQ - stops the membership broadcaster <BR>\r
- * SND_TX_SEQ - stops the replication transmitter<BR>\r
- * SND_RX_SEQ - stops the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already started.\r
- */\r
- public void stop(int svc) throws ChannelException {\r
- this.internalStop(svc);\r
- } \r
-\r
-\r
- /**\r
- * Starts up the channel. This can be called multiple times for individual services to start\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * DEFAULT - will start all services <BR>\r
- * MBR_RX_SEQ - starts the membership receiver <BR>\r
- * MBR_TX_SEQ - starts the membership broadcaster <BR>\r
- * SND_TX_SEQ - starts the replication transmitter<BR>\r
- * SND_RX_SEQ - starts the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already started.\r
- */\r
- protected synchronized void internalStart(int svc) throws ChannelException {\r
- try {\r
- boolean valid = false;\r
- //make sure we don't pass down any flags that are unrelated to the bottom layer\r
- svc = svc & Channel.DEFAULT;\r
-\r
- if (startLevel == Channel.DEFAULT) return; //we have already started up all components\r
- if (svc == 0 ) return;//nothing to start\r
- \r
- if (svc == (svc & startLevel)) throw new ChannelException("Channel already started for level:"+svc);\r
-\r
- //must start the receiver first so that we can coordinate the port it\r
- //listens to with the local membership settings\r
- if ( Channel.SND_RX_SEQ==(svc & Channel.SND_RX_SEQ) ) {\r
- clusterReceiver.setMessageListener(this);\r
- clusterReceiver.start();\r
- //synchronize, big time FIXME\r
- membershipService.setLocalMemberProperties(getClusterReceiver().getHost(), getClusterReceiver().getPort());\r
- valid = true;\r
- }\r
- if ( Channel.SND_TX_SEQ==(svc & Channel.SND_TX_SEQ) ) {\r
- clusterSender.start();\r
- valid = true;\r
- }\r
- \r
- if ( Channel.MBR_RX_SEQ==(svc & Channel.MBR_RX_SEQ) ) {\r
- membershipService.setMembershipListener(this);\r
- membershipService.start(MembershipService.MBR_RX);\r
- valid = true;\r
- }\r
- if ( Channel.MBR_TX_SEQ==(svc & Channel.MBR_TX_SEQ) ) {\r
- membershipService.start(MembershipService.MBR_TX);\r
- valid = true;\r
- }\r
- \r
- if ( !valid) {\r
- throw new IllegalArgumentException("Invalid start level, valid levels are:SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ");\r
- }\r
- startLevel = (startLevel | svc);\r
- }catch ( ChannelException cx ) {\r
- throw cx;\r
- }catch ( Exception x ) {\r
- throw new ChannelException(x);\r
- }\r
- }\r
-\r
- /**\r
- * Shuts down the channel. This can be called multiple times for individual services to shutdown\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * DEFAULT - will shutdown all services <BR>\r
- * MBR_RX_SEQ - starts the membership receiver <BR>\r
- * MBR_TX_SEQ - starts the membership broadcaster <BR>\r
- * SND_TX_SEQ - starts the replication transmitter<BR>\r
- * SND_RX_SEQ - starts the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already started.\r
- */\r
- protected synchronized void internalStop(int svc) throws ChannelException {\r
- try {\r
- //make sure we don't pass down any flags that are unrelated to the bottom layer\r
- svc = svc & Channel.DEFAULT;\r
-\r
- if (startLevel == 0) return; //we have already stopped up all components\r
- if (svc == 0 ) return;//nothing to stop\r
-\r
- boolean valid = false;\r
- if ( Channel.SND_RX_SEQ==(svc & Channel.SND_RX_SEQ) ) {\r
- clusterReceiver.stop();\r
- clusterReceiver.setMessageListener(null);\r
- valid = true;\r
- }\r
- if ( Channel.SND_TX_SEQ==(svc & Channel.SND_TX_SEQ) ) {\r
- clusterSender.stop();\r
- valid = true;\r
- }\r
-\r
- if ( Channel.MBR_RX_SEQ==(svc & Channel.MBR_RX_SEQ) ) {\r
- membershipService.stop(MembershipService.MBR_RX);\r
- membershipService.setMembershipListener(null);\r
- valid = true;\r
- \r
- }\r
- if ( Channel.MBR_TX_SEQ==(svc & Channel.MBR_TX_SEQ) ) {\r
- valid = true;\r
- membershipService.stop(MembershipService.MBR_TX);\r
- } \r
- if ( !valid) {\r
- throw new IllegalArgumentException("Invalid start level, valid levels are:SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ");\r
- }\r
-\r
- startLevel = (startLevel & (~svc));\r
- \r
- }catch ( Exception x ) {\r
- throw new ChannelException(x);\r
- } finally {\r
- \r
- }\r
-\r
- }\r
- \r
- public void memberAdded(Member member){\r
- SenderState.getSenderState(member);\r
- if ( clusterSender!=null ) clusterSender.add(member);\r
- super.memberAdded(member);\r
- }\r
- \r
- public void memberDisappeared(Member member){\r
- SenderState.removeSenderState(member);\r
- if ( clusterSender!=null ) clusterSender.remove(member);\r
- super.memberDisappeared(member);\r
- }\r
- \r
- public void messageReceived(ChannelMessage msg) {\r
- if ( Logs.MESSAGES.isTraceEnabled() ) {\r
- Logs.MESSAGES.trace("ChannelCoordinator - Received msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " from "+msg.getAddress().getName());\r
- }\r
- super.messageReceived(msg);\r
- }\r
-\r
-\r
- public ChannelReceiver getClusterReceiver() {\r
- return clusterReceiver;\r
- }\r
-\r
- public ChannelSender getClusterSender() {\r
- return clusterSender;\r
- }\r
-\r
- public MembershipService getMembershipService() {\r
- return membershipService;\r
- }\r
-\r
- public void setClusterReceiver(ChannelReceiver clusterReceiver) {\r
- if ( clusterReceiver != null ) {\r
- this.clusterReceiver = clusterReceiver;\r
- this.clusterReceiver.setMessageListener(this);\r
- } else {\r
- if (this.clusterReceiver!=null ) this.clusterReceiver.setMessageListener(null);\r
- this.clusterReceiver = null;\r
- }\r
- }\r
-\r
- public void setClusterSender(ChannelSender clusterSender) {\r
- this.clusterSender = clusterSender;\r
- }\r
-\r
- public void setMembershipService(MembershipService membershipService) {\r
- this.membershipService = membershipService;\r
- this.membershipService.setMembershipListener(this);\r
- }\r
- \r
- public void hearbeat() {\r
- if ( clusterSender!=null ) clusterSender.heartbeat();\r
- super.heartbeat();\r
- }\r
- \r
- /**\r
- * has members\r
- */\r
- public boolean hasMembers() {\r
- return this.getMembershipService().hasMembers();\r
- }\r
-\r
- /**\r
- * Get all current cluster members\r
- * @return all members or empty array\r
- */\r
- public Member[] getMembers() {\r
- return this.getMembershipService().getMembers();\r
- }\r
-\r
- /**\r
- * \r
- * @param mbr Member\r
- * @return Member\r
- */\r
- public Member getMember(Member mbr){\r
- return this.getMembershipService().getMember(mbr);\r
- }\r
-\r
-\r
- /**\r
- * Return the member that represents this node.\r
- *\r
- * @return Member\r
- */\r
- public Member getLocalMember(boolean incAlive) {\r
- return this.getMembershipService().getLocalMember(incAlive);\r
- }\r
-\r
- \r
-}\r
+/*
+ * 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.apache.catalina.tribes.group;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.ChannelReceiver;
+import org.apache.catalina.tribes.ChannelSender;
+
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipService;
+import org.apache.catalina.tribes.MessageListener;
+import org.apache.catalina.tribes.transport.SenderState;
+import org.apache.catalina.tribes.transport.ReplicationTransmitter;
+import org.apache.catalina.tribes.membership.McastService;
+import org.apache.catalina.tribes.transport.nio.NioReceiver;
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.util.Logs;
+import org.apache.catalina.tribes.UniqueId;
+import org.apache.catalina.tribes.util.Arrays;
+
+
+/**
+ * The channel coordinator object coordinates the membership service,
+ * the sender and the receiver.
+ * This is the last interceptor in the chain.
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+public class ChannelCoordinator extends ChannelInterceptorBase implements MessageListener {
+ private ChannelReceiver clusterReceiver = new NioReceiver();
+ private ChannelSender clusterSender = new ReplicationTransmitter();
+ private MembershipService membershipService = new McastService();
+
+ //override optionflag
+ protected int optionFlag = Channel.SEND_OPTIONS_BYTE_MESSAGE|Channel.SEND_OPTIONS_USE_ACK|Channel.SEND_OPTIONS_SYNCHRONIZED_ACK;
+ public int getOptionFlag() {return optionFlag;}
+ public void setOptionFlag(int flag) {optionFlag=flag;}
+
+ private int startLevel = 0;
+
+ public ChannelCoordinator() {
+
+ }
+
+ public ChannelCoordinator(ChannelReceiver receiver,
+ ChannelSender sender,
+ MembershipService service) {
+ this();
+ this.setClusterReceiver(receiver);
+ this.setClusterSender(sender);
+ this.setMembershipService(service);
+ }
+
+ /**
+ * Send a message to one or more members in the cluster
+ * @param destination Member[] - the destinations, null or zero length means all
+ * @param msg ClusterMessage - the message to send
+ * @param options int - sender options, see class documentation
+ * @return ClusterMessage[] - the replies from the members, if any.
+ */
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ if ( destination == null ) destination = membershipService.getMembers();
+ clusterSender.sendMessage(msg,destination);
+ if ( Logs.MESSAGES.isTraceEnabled() ) {
+ Logs.MESSAGES.trace("ChannelCoordinator - Sent msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " to "+Arrays.toNameString(destination));
+ }
+ }
+
+
+ /**
+ * Starts up the channel. This can be called multiple times for individual services to start
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * DEFAULT - will start all services <BR>
+ * MBR_RX_SEQ - starts the membership receiver <BR>
+ * MBR_TX_SEQ - starts the membership broadcaster <BR>
+ * SND_TX_SEQ - starts the replication transmitter<BR>
+ * SND_RX_SEQ - starts the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already started.
+ */
+ public void start(int svc) throws ChannelException {
+ this.internalStart(svc);
+ }
+
+ /**
+ * Shuts down the channel. This can be called multiple times for individual services to shutdown
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * DEFAULT - will shutdown all services <BR>
+ * MBR_RX_SEQ - stops the membership receiver <BR>
+ * MBR_TX_SEQ - stops the membership broadcaster <BR>
+ * SND_TX_SEQ - stops the replication transmitter<BR>
+ * SND_RX_SEQ - stops the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already started.
+ */
+ public void stop(int svc) throws ChannelException {
+ this.internalStop(svc);
+ }
+
+
+ /**
+ * Starts up the channel. This can be called multiple times for individual services to start
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * DEFAULT - will start all services <BR>
+ * MBR_RX_SEQ - starts the membership receiver <BR>
+ * MBR_TX_SEQ - starts the membership broadcaster <BR>
+ * SND_TX_SEQ - starts the replication transmitter<BR>
+ * SND_RX_SEQ - starts the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already started.
+ */
+ protected synchronized void internalStart(int svc) throws ChannelException {
+ try {
+ boolean valid = false;
+ //make sure we don't pass down any flags that are unrelated to the bottom layer
+ svc = svc & Channel.DEFAULT;
+
+ if (startLevel == Channel.DEFAULT) return; //we have already started up all components
+ if (svc == 0 ) return;//nothing to start
+
+ if (svc == (svc & startLevel)) throw new ChannelException("Channel already started for level:"+svc);
+
+ //must start the receiver first so that we can coordinate the port it
+ //listens to with the local membership settings
+ if ( Channel.SND_RX_SEQ==(svc & Channel.SND_RX_SEQ) ) {
+ clusterReceiver.setMessageListener(this);
+ clusterReceiver.start();
+ //synchronize, big time FIXME
+ membershipService.setLocalMemberProperties(getClusterReceiver().getHost(), getClusterReceiver().getPort());
+ valid = true;
+ }
+ if ( Channel.SND_TX_SEQ==(svc & Channel.SND_TX_SEQ) ) {
+ clusterSender.start();
+ valid = true;
+ }
+
+ if ( Channel.MBR_RX_SEQ==(svc & Channel.MBR_RX_SEQ) ) {
+ membershipService.setMembershipListener(this);
+ membershipService.start(MembershipService.MBR_RX);
+ valid = true;
+ }
+ if ( Channel.MBR_TX_SEQ==(svc & Channel.MBR_TX_SEQ) ) {
+ membershipService.start(MembershipService.MBR_TX);
+ valid = true;
+ }
+
+ if ( !valid) {
+ throw new IllegalArgumentException("Invalid start level, valid levels are:SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ");
+ }
+ startLevel = (startLevel | svc);
+ }catch ( ChannelException cx ) {
+ throw cx;
+ }catch ( Exception x ) {
+ throw new ChannelException(x);
+ }
+ }
+
+ /**
+ * Shuts down the channel. This can be called multiple times for individual services to shutdown
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * DEFAULT - will shutdown all services <BR>
+ * MBR_RX_SEQ - starts the membership receiver <BR>
+ * MBR_TX_SEQ - starts the membership broadcaster <BR>
+ * SND_TX_SEQ - starts the replication transmitter<BR>
+ * SND_RX_SEQ - starts the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already started.
+ */
+ protected synchronized void internalStop(int svc) throws ChannelException {
+ try {
+ //make sure we don't pass down any flags that are unrelated to the bottom layer
+ svc = svc & Channel.DEFAULT;
+
+ if (startLevel == 0) return; //we have already stopped up all components
+ if (svc == 0 ) return;//nothing to stop
+
+ boolean valid = false;
+ if ( Channel.SND_RX_SEQ==(svc & Channel.SND_RX_SEQ) ) {
+ clusterReceiver.stop();
+ clusterReceiver.setMessageListener(null);
+ valid = true;
+ }
+ if ( Channel.SND_TX_SEQ==(svc & Channel.SND_TX_SEQ) ) {
+ clusterSender.stop();
+ valid = true;
+ }
+
+ if ( Channel.MBR_RX_SEQ==(svc & Channel.MBR_RX_SEQ) ) {
+ membershipService.stop(MembershipService.MBR_RX);
+ membershipService.setMembershipListener(null);
+ valid = true;
+
+ }
+ if ( Channel.MBR_TX_SEQ==(svc & Channel.MBR_TX_SEQ) ) {
+ valid = true;
+ membershipService.stop(MembershipService.MBR_TX);
+ }
+ if ( !valid) {
+ throw new IllegalArgumentException("Invalid start level, valid levels are:SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ");
+ }
+
+ startLevel = (startLevel & (~svc));
+
+ }catch ( Exception x ) {
+ throw new ChannelException(x);
+ } finally {
+
+ }
+
+ }
+
+ public void memberAdded(Member member){
+ SenderState.getSenderState(member);
+ if ( clusterSender!=null ) clusterSender.add(member);
+ super.memberAdded(member);
+ }
+
+ public void memberDisappeared(Member member){
+ SenderState.removeSenderState(member);
+ if ( clusterSender!=null ) clusterSender.remove(member);
+ super.memberDisappeared(member);
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ if ( Logs.MESSAGES.isTraceEnabled() ) {
+ Logs.MESSAGES.trace("ChannelCoordinator - Received msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " from "+msg.getAddress().getName());
+ }
+ super.messageReceived(msg);
+ }
+
+
+ public ChannelReceiver getClusterReceiver() {
+ return clusterReceiver;
+ }
+
+ public ChannelSender getClusterSender() {
+ return clusterSender;
+ }
+
+ public MembershipService getMembershipService() {
+ return membershipService;
+ }
+
+ public void setClusterReceiver(ChannelReceiver clusterReceiver) {
+ if ( clusterReceiver != null ) {
+ this.clusterReceiver = clusterReceiver;
+ this.clusterReceiver.setMessageListener(this);
+ } else {
+ if (this.clusterReceiver!=null ) this.clusterReceiver.setMessageListener(null);
+ this.clusterReceiver = null;
+ }
+ }
+
+ public void setClusterSender(ChannelSender clusterSender) {
+ this.clusterSender = clusterSender;
+ }
+
+ public void setMembershipService(MembershipService membershipService) {
+ this.membershipService = membershipService;
+ this.membershipService.setMembershipListener(this);
+ }
+
+ public void hearbeat() {
+ if ( clusterSender!=null ) clusterSender.heartbeat();
+ super.heartbeat();
+ }
+
+ /**
+ * has members
+ */
+ public boolean hasMembers() {
+ return this.getMembershipService().hasMembers();
+ }
+
+ /**
+ * Get all current cluster members
+ * @return all members or empty array
+ */
+ public Member[] getMembers() {
+ return this.getMembershipService().getMembers();
+ }
+
+ /**
+ *
+ * @param mbr Member
+ * @return Member
+ */
+ public Member getMember(Member mbr){
+ return this.getMembershipService().getMember(mbr);
+ }
+
+
+ /**
+ * Return the member that represents this node.
+ *
+ * @return Member
+ */
+ public Member getLocalMember(boolean incAlive) {
+ return this.getMembershipService().getLocalMember(incAlive);
+ }
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.group;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelInterceptor;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-\r
-/**\r
- * Abstract class for the interceptor base class.\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-\r
-public abstract class ChannelInterceptorBase implements ChannelInterceptor {\r
-\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(\r
- ChannelInterceptorBase.class);\r
-\r
- private ChannelInterceptor next;\r
- private ChannelInterceptor previous;\r
- //default value, always process\r
- protected int optionFlag = 0;\r
-\r
- public ChannelInterceptorBase() {\r
-\r
- }\r
- \r
- public boolean okToProcess(int messageFlags) { \r
- if (this.optionFlag == 0 ) return true;\r
- return ((optionFlag&messageFlags) == optionFlag);\r
- }\r
-\r
- public final void setNext(ChannelInterceptor next) {\r
- this.next = next;\r
- }\r
-\r
- public final ChannelInterceptor getNext() {\r
- return next;\r
- }\r
-\r
- public final void setPrevious(ChannelInterceptor previous) {\r
- this.previous = previous;\r
- }\r
-\r
- public void setOptionFlag(int optionFlag) {\r
- this.optionFlag = optionFlag;\r
- }\r
-\r
- public final ChannelInterceptor getPrevious() {\r
- return previous;\r
- }\r
-\r
- public int getOptionFlag() {\r
- return optionFlag;\r
- }\r
-\r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws\r
- ChannelException {\r
- if (getNext() != null) getNext().sendMessage(destination, msg, payload);\r
- }\r
-\r
- public void messageReceived(ChannelMessage msg) {\r
- if (getPrevious() != null) getPrevious().messageReceived(msg);\r
- }\r
-\r
- public boolean accept(ChannelMessage msg) {\r
- return true;\r
- }\r
-\r
- public void memberAdded(Member member) {\r
- //notify upwards\r
- if (getPrevious() != null) getPrevious().memberAdded(member);\r
- }\r
-\r
- public void memberDisappeared(Member member) {\r
- //notify upwards\r
- if (getPrevious() != null) getPrevious().memberDisappeared(member);\r
- }\r
-\r
- public void heartbeat() {\r
- if (getNext() != null) getNext().heartbeat();\r
- }\r
-\r
- /**\r
- * has members\r
- */\r
- public boolean hasMembers() {\r
- if ( getNext()!=null )return getNext().hasMembers();\r
- else return false;\r
- }\r
-\r
- /**\r
- * Get all current cluster members\r
- * @return all members or empty array\r
- */\r
- public Member[] getMembers() {\r
- if ( getNext()!=null ) return getNext().getMembers();\r
- else return null;\r
- }\r
-\r
- /**\r
- *\r
- * @param mbr Member\r
- * @return Member\r
- */\r
- public Member getMember(Member mbr) {\r
- if ( getNext()!=null) return getNext().getMember(mbr);\r
- else return null;\r
- }\r
-\r
- /**\r
- * Return the member that represents this node.\r
- *\r
- * @return Member\r
- */\r
- public Member getLocalMember(boolean incAlive) {\r
- if ( getNext()!=null ) return getNext().getLocalMember(incAlive);\r
- else return null;\r
- }\r
- \r
- /**\r
- * Starts up the channel. This can be called multiple times for individual services to start\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * DEFAULT - will start all services <BR>\r
- * MBR_RX_SEQ - starts the membership receiver <BR>\r
- * MBR_TX_SEQ - starts the membership broadcaster <BR>\r
- * SND_TX_SEQ - starts the replication transmitter<BR>\r
- * SND_RX_SEQ - starts the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already started.\r
- */\r
- public void start(int svc) throws ChannelException {\r
- if ( getNext()!=null ) getNext().start(svc);\r
- }\r
-\r
- /**\r
- * Shuts down the channel. This can be called multiple times for individual services to shutdown\r
- * The svc parameter can be the logical or value of any constants\r
- * @param svc int value of <BR>\r
- * DEFAULT - will shutdown all services <BR>\r
- * MBR_RX_SEQ - stops the membership receiver <BR>\r
- * MBR_TX_SEQ - stops the membership broadcaster <BR>\r
- * SND_TX_SEQ - stops the replication transmitter<BR>\r
- * SND_RX_SEQ - stops the replication receiver<BR>\r
- * @throws ChannelException if a startup error occurs or the service is already started.\r
- */\r
- public void stop(int svc) throws ChannelException {\r
- if (getNext() != null) getNext().stop(svc);\r
- }\r
- \r
- public void fireInterceptorEvent(InterceptorEvent event) {\r
- //empty operation\r
- }\r
-\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.group;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelInterceptor;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+
+/**
+ * Abstract class for the interceptor base class.
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+public abstract class ChannelInterceptorBase implements ChannelInterceptor {
+
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(
+ ChannelInterceptorBase.class);
+
+ private ChannelInterceptor next;
+ private ChannelInterceptor previous;
+ //default value, always process
+ protected int optionFlag = 0;
+
+ public ChannelInterceptorBase() {
+
+ }
+
+ public boolean okToProcess(int messageFlags) {
+ if (this.optionFlag == 0 ) return true;
+ return ((optionFlag&messageFlags) == optionFlag);
+ }
+
+ public final void setNext(ChannelInterceptor next) {
+ this.next = next;
+ }
+
+ public final ChannelInterceptor getNext() {
+ return next;
+ }
+
+ public final void setPrevious(ChannelInterceptor previous) {
+ this.previous = previous;
+ }
+
+ public void setOptionFlag(int optionFlag) {
+ this.optionFlag = optionFlag;
+ }
+
+ public final ChannelInterceptor getPrevious() {
+ return previous;
+ }
+
+ public int getOptionFlag() {
+ return optionFlag;
+ }
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws
+ ChannelException {
+ if (getNext() != null) getNext().sendMessage(destination, msg, payload);
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ if (getPrevious() != null) getPrevious().messageReceived(msg);
+ }
+
+ public boolean accept(ChannelMessage msg) {
+ return true;
+ }
+
+ public void memberAdded(Member member) {
+ //notify upwards
+ if (getPrevious() != null) getPrevious().memberAdded(member);
+ }
+
+ public void memberDisappeared(Member member) {
+ //notify upwards
+ if (getPrevious() != null) getPrevious().memberDisappeared(member);
+ }
+
+ public void heartbeat() {
+ if (getNext() != null) getNext().heartbeat();
+ }
+
+ /**
+ * has members
+ */
+ public boolean hasMembers() {
+ if ( getNext()!=null )return getNext().hasMembers();
+ else return false;
+ }
+
+ /**
+ * Get all current cluster members
+ * @return all members or empty array
+ */
+ public Member[] getMembers() {
+ if ( getNext()!=null ) return getNext().getMembers();
+ else return null;
+ }
+
+ /**
+ *
+ * @param mbr Member
+ * @return Member
+ */
+ public Member getMember(Member mbr) {
+ if ( getNext()!=null) return getNext().getMember(mbr);
+ else return null;
+ }
+
+ /**
+ * Return the member that represents this node.
+ *
+ * @return Member
+ */
+ public Member getLocalMember(boolean incAlive) {
+ if ( getNext()!=null ) return getNext().getLocalMember(incAlive);
+ else return null;
+ }
+
+ /**
+ * Starts up the channel. This can be called multiple times for individual services to start
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * DEFAULT - will start all services <BR>
+ * MBR_RX_SEQ - starts the membership receiver <BR>
+ * MBR_TX_SEQ - starts the membership broadcaster <BR>
+ * SND_TX_SEQ - starts the replication transmitter<BR>
+ * SND_RX_SEQ - starts the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already started.
+ */
+ public void start(int svc) throws ChannelException {
+ if ( getNext()!=null ) getNext().start(svc);
+ }
+
+ /**
+ * Shuts down the channel. This can be called multiple times for individual services to shutdown
+ * The svc parameter can be the logical or value of any constants
+ * @param svc int value of <BR>
+ * DEFAULT - will shutdown all services <BR>
+ * MBR_RX_SEQ - stops the membership receiver <BR>
+ * MBR_TX_SEQ - stops the membership broadcaster <BR>
+ * SND_TX_SEQ - stops the replication transmitter<BR>
+ * SND_RX_SEQ - stops the replication receiver<BR>
+ * @throws ChannelException if a startup error occurs or the service is already started.
+ */
+ public void stop(int svc) throws ChannelException {
+ if (getNext() != null) getNext().stop(svc);
+ }
+
+ public void fireInterceptorEvent(InterceptorEvent event) {
+ //empty operation
+ }
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.group;\r
-\r
-\r
-import java.io.Serializable;\r
-import java.util.ArrayList;\r
-import java.util.Iterator;\r
-\r
-import org.apache.catalina.tribes.ByteMessage;\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelInterceptor;\r
-import org.apache.catalina.tribes.ChannelListener;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.ChannelReceiver;\r
-import org.apache.catalina.tribes.ChannelSender;\r
-import org.apache.catalina.tribes.ErrorHandler;\r
-import org.apache.catalina.tribes.ManagedChannel;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.MembershipListener;\r
-import org.apache.catalina.tribes.MembershipService;\r
-import org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor;\r
-import org.apache.catalina.tribes.io.ChannelData;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-import org.apache.catalina.tribes.UniqueId;\r
-import org.apache.catalina.tribes.Heartbeat;\r
-import org.apache.catalina.tribes.io.BufferPool;\r
-import java.io.IOException;\r
-import org.apache.catalina.tribes.RemoteProcessException;\r
-import org.apache.catalina.tribes.util.Logs;\r
-import org.apache.catalina.tribes.util.Arrays;\r
-\r
-/**\r
- * The default implementation of a Channel.<br>\r
- * The GroupChannel manages the replication channel. It coordinates\r
- * message being sent and received with membership announcements.\r
- * The channel has an chain of interceptors that can modify the message or perform other logic.<br>\r
- * It manages a complete group, both membership and replication.\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-public class GroupChannel extends ChannelInterceptorBase implements ManagedChannel {\r
- /**\r
- * Flag to determine if the channel manages its own heartbeat\r
- * If set to true, the channel will start a local thread for the heart beat.\r
- */\r
- protected boolean heartbeat = true;\r
- /**\r
- * If <code>heartbeat == true</code> then how often do we want this\r
- * heartbeat to run. default is one minute\r
- */\r
- protected long heartbeatSleeptime = 5*1000;//every 5 seconds\r
-\r
- /**\r
- * Internal heartbeat thread\r
- */\r
- protected HeartbeatThread hbthread = null;\r
-\r
- /**\r
- * The <code>ChannelCoordinator</code> coordinates the bottom layer components:<br>\r
- * - MembershipService<br>\r
- * - ChannelSender <br>\r
- * - ChannelReceiver<br>\r
- */\r
- protected ChannelCoordinator coordinator = new ChannelCoordinator();\r
-\r
- /**\r
- * The first interceptor in the inteceptor stack.\r
- * The interceptors are chained in a linked list, so we only need a reference to the\r
- * first one\r
- */\r
- protected ChannelInterceptor interceptors = null;\r
-\r
- /**\r
- * A list of membership listeners that subscribe to membership announcements\r
- */\r
- protected ArrayList membershipListeners = new ArrayList();\r
-\r
- /**\r
- * A list of channel listeners that subscribe to incoming messages\r
- */\r
- protected ArrayList channelListeners = new ArrayList();\r
-\r
- /**\r
- * If set to true, the GroupChannel will check to make sure that\r
- */\r
- protected boolean optionCheck = false;\r
-\r
- /**\r
- * Creates a GroupChannel. This constructor will also\r
- * add the first interceptor in the GroupChannel.<br>\r
- * The first interceptor is always the channel itself.\r
- */\r
- public GroupChannel() {\r
- addInterceptor(this);\r
- }\r
-\r
-\r
- /**\r
- * Adds an interceptor to the stack for message processing<br>\r
- * Interceptors are ordered in the way they are added.<br>\r
- * <code>channel.addInterceptor(A);</code><br>\r
- * <code>channel.addInterceptor(C);</code><br>\r
- * <code>channel.addInterceptor(B);</code><br>\r
- * Will result in a interceptor stack like this:<br>\r
- * <code>A -> C -> B</code><br>\r
- * The complete stack will look like this:<br>\r
- * <code>Channel -> A -> C -> B -> ChannelCoordinator</code><br>\r
- * @param interceptor ChannelInterceptorBase\r
- */\r
- public void addInterceptor(ChannelInterceptor interceptor) {\r
- if ( interceptors == null ) {\r
- interceptors = interceptor;\r
- interceptors.setNext(coordinator);\r
- interceptors.setPrevious(null);\r
- coordinator.setPrevious(interceptors);\r
- } else {\r
- ChannelInterceptor last = interceptors;\r
- while ( last.getNext() != coordinator ) {\r
- last = last.getNext();\r
- }\r
- last.setNext(interceptor);\r
- interceptor.setNext(coordinator);\r
- interceptor.setPrevious(last);\r
- coordinator.setPrevious(interceptor);\r
- }\r
- }\r
-\r
- /**\r
- * Sends a heartbeat through the interceptor stack.<br>\r
- * Invoke this method from the application on a periodic basis if\r
- * you have turned off internal heartbeats <code>channel.setHeartbeat(false)</code>\r
- */\r
- public void heartbeat() {\r
- super.heartbeat();\r
- Iterator i = membershipListeners.iterator();\r
- while ( i.hasNext() ) {\r
- Object o = i.next();\r
- if ( o instanceof Heartbeat ) ((Heartbeat)o).heartbeat();\r
- }\r
- i = channelListeners.iterator();\r
- while ( i.hasNext() ) {\r
- Object o = i.next();\r
- if ( o instanceof Heartbeat ) ((Heartbeat)o).heartbeat();\r
- }\r
-\r
- }\r
-\r
-\r
- /**\r
- * Send a message to the destinations specified\r
- * @param destination Member[] - destination.length > 1\r
- * @param msg Serializable - the message to send\r
- * @param options int - sender options, options can trigger guarantee levels and different interceptors to\r
- * react to the message see class documentation for the <code>Channel</code> object.<br>\r
- * @return UniqueId - the unique Id that was assigned to this message\r
- * @throws ChannelException - if an error occurs processing the message\r
- * @see org.apache.catalina.tribes.Channel\r
- */\r
- public UniqueId send(Member[] destination, Serializable msg, int options) throws ChannelException {\r
- return send(destination,msg,options,null);\r
- }\r
-\r
- /**\r
- *\r
- * @param destination Member[] - destination.length > 1\r
- * @param msg Serializable - the message to send\r
- * @param options int - sender options, options can trigger guarantee levels and different interceptors to\r
- * react to the message see class documentation for the <code>Channel</code> object.<br>\r
- * @param handler - callback object for error handling and completion notification, used when a message is\r
- * sent asynchronously using the <code>Channel.SEND_OPTIONS_ASYNCHRONOUS</code> flag enabled.\r
- * @return UniqueId - the unique Id that was assigned to this message\r
- * @throws ChannelException - if an error occurs processing the message\r
- * @see org.apache.catalina.tribes.Channel\r
- */\r
- public UniqueId send(Member[] destination, Serializable msg, int options, ErrorHandler handler) throws ChannelException {\r
- if ( msg == null ) throw new ChannelException("Cant send a NULL message");\r
- XByteBuffer buffer = null;\r
- try {\r
- if ( destination == null || destination.length == 0) throw new ChannelException("No destination given");\r
- ChannelData data = new ChannelData(true);//generates a unique Id\r
- data.setAddress(getLocalMember(false));\r
- data.setTimestamp(System.currentTimeMillis());\r
- byte[] b = null;\r
- if ( msg instanceof ByteMessage ){\r
- b = ((ByteMessage)msg).getMessage();\r
- options = options | SEND_OPTIONS_BYTE_MESSAGE;\r
- } else {\r
- b = XByteBuffer.serialize(msg);\r
- options = options & (~SEND_OPTIONS_BYTE_MESSAGE);\r
- }\r
- data.setOptions(options);\r
- //XByteBuffer buffer = new XByteBuffer(b.length+128,false);\r
- buffer = BufferPool.getBufferPool().getBuffer(b.length+128, false);\r
- buffer.append(b,0,b.length);\r
- data.setMessage(buffer);\r
- InterceptorPayload payload = null;\r
- if ( handler != null ) {\r
- payload = new InterceptorPayload();\r
- payload.setErrorHandler(handler);\r
- }\r
- getFirstInterceptor().sendMessage(destination, data, payload);\r
- if ( Logs.MESSAGES.isTraceEnabled() ) {\r
- Logs.MESSAGES.trace("GroupChannel - Sent msg:" + new UniqueId(data.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " to "+Arrays.toNameString(destination));\r
- Logs.MESSAGES.trace("GroupChannel - Send Message:" + new UniqueId(data.getUniqueId()) + " is " +msg);\r
- }\r
-\r
- return new UniqueId(data.getUniqueId());\r
- }catch ( Exception x ) {\r
- if ( x instanceof ChannelException ) throw (ChannelException)x;\r
- throw new ChannelException(x);\r
- } finally {\r
- if ( buffer != null ) BufferPool.getBufferPool().returnBuffer(buffer);\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Callback from the interceptor stack. <br>\r
- * When a message is received from a remote node, this method will be invoked by\r
- * the previous interceptor.<br>\r
- * This method can also be used to send a message to other components within the same application,\r
- * but its an extreme case, and you're probably better off doing that logic between the applications itself.\r
- * @param msg ChannelMessage\r
- */\r
- public void messageReceived(ChannelMessage msg) {\r
- if ( msg == null ) return;\r
- try {\r
- if ( Logs.MESSAGES.isTraceEnabled() ) {\r
- Logs.MESSAGES.trace("GroupChannel - Received msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " from "+msg.getAddress().getName());\r
- }\r
-\r
- Serializable fwd = null;\r
- if ( (msg.getOptions() & SEND_OPTIONS_BYTE_MESSAGE) == SEND_OPTIONS_BYTE_MESSAGE ) {\r
- fwd = new ByteMessage(msg.getMessage().getBytes());\r
- } else {\r
- fwd = XByteBuffer.deserialize(msg.getMessage().getBytesDirect(),0,msg.getMessage().getLength());\r
- }\r
- if ( Logs.MESSAGES.isTraceEnabled() ) {\r
- Logs.MESSAGES.trace("GroupChannel - Receive Message:" + new UniqueId(msg.getUniqueId()) + " is " +fwd);\r
- }\r
-\r
- //get the actual member with the correct alive time\r
- Member source = msg.getAddress();\r
- boolean rx = false;\r
- boolean delivered = false;\r
- for ( int i=0; i<channelListeners.size(); i++ ) {\r
- ChannelListener channelListener = (ChannelListener)channelListeners.get(i);\r
- if (channelListener != null && channelListener.accept(fwd, source)) {\r
- channelListener.messageReceived(fwd, source);\r
- delivered = true;\r
- //if the message was accepted by an RPC channel, that channel\r
- //is responsible for returning the reply, otherwise we send an absence reply\r
- if ( channelListener instanceof RpcChannel ) rx = true;\r
- }\r
- }//for\r
- if ((!rx) && (fwd instanceof RpcMessage)) {\r
- //if we have a message that requires a response,\r
- //but none was given, send back an immediate one\r
- sendNoRpcChannelReply((RpcMessage)fwd,source);\r
- }\r
- if ( Logs.MESSAGES.isTraceEnabled() ) {\r
- Logs.MESSAGES.trace("GroupChannel delivered["+delivered+"] id:"+new UniqueId(msg.getUniqueId()));\r
- }\r
-\r
- } catch ( Exception x ) {\r
- if ( log.isDebugEnabled() ) log.error("Unable to process channel:IOException.",x);\r
- throw new RemoteProcessException("IOException:"+x.getMessage(),x);\r
- }\r
- }\r
-\r
- /**\r
- * Sends a <code>NoRpcChannelReply</code> message to a member<br>\r
- * This method gets invoked by the channel if a RPC message comes in\r
- * and no channel listener accepts the message. This avoids timeout\r
- * @param msg RpcMessage\r
- * @param destination Member - the destination for the reply\r
- */\r
- protected void sendNoRpcChannelReply(RpcMessage msg, Member destination) {\r
- try {\r
- //avoid circular loop\r
- if ( msg instanceof RpcMessage.NoRpcChannelReply) return;\r
- RpcMessage.NoRpcChannelReply reply = new RpcMessage.NoRpcChannelReply(msg.rpcId,msg.uuid);\r
- send(new Member[]{destination},reply,Channel.SEND_OPTIONS_ASYNCHRONOUS);\r
- } catch ( Exception x ) {\r
- log.error("Unable to find rpc channel, failed to send NoRpcChannelReply.",x);\r
- }\r
- }\r
-\r
- /**\r
- * memberAdded gets invoked by the interceptor below the channel\r
- * and the channel will broadcast it to the membership listeners\r
- * @param member Member - the new member\r
- */\r
- public void memberAdded(Member member) {\r
- //notify upwards\r
- for (int i=0; i<membershipListeners.size(); i++ ) {\r
- MembershipListener membershipListener = (MembershipListener)membershipListeners.get(i);\r
- if (membershipListener != null) membershipListener.memberAdded(member);\r
- }\r
- }\r
-\r
- /**\r
- * memberDisappeared gets invoked by the interceptor below the channel\r
- * and the channel will broadcast it to the membership listeners\r
- * @param member Member - the member that left or crashed\r
- */\r
- public void memberDisappeared(Member member) {\r
- //notify upwards\r
- for (int i=0; i<membershipListeners.size(); i++ ) {\r
- MembershipListener membershipListener = (MembershipListener)membershipListeners.get(i);\r
- if (membershipListener != null) membershipListener.memberDisappeared(member);\r
- }\r
- }\r
-\r
- /**\r
- * Sets up the default implementation interceptor stack\r
- * if no interceptors have been added\r
- * @throws ChannelException\r
- */\r
- protected synchronized void setupDefaultStack() throws ChannelException {\r
-\r
- if ( getFirstInterceptor() != null &&\r
- ((getFirstInterceptor().getNext() instanceof ChannelCoordinator))) {\r
- ChannelInterceptor interceptor = null;\r
- Class clazz = null;\r
- try {\r
- clazz = Class.forName("org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor",\r
- true,GroupChannel.class.getClassLoader());\r
- clazz.newInstance();\r
- } catch ( Throwable x ) {\r
- clazz = MessageDispatchInterceptor.class;\r
- }//catch\r
- try {\r
- interceptor = (ChannelInterceptor) clazz.newInstance();\r
- } catch (Exception x) {\r
- throw new ChannelException("Unable to add MessageDispatchInterceptor to interceptor chain.",x);\r
- }\r
- this.addInterceptor(interceptor);\r
- }\r
- }\r
-\r
- /**\r
- * Validates the option flags that each interceptor is using and reports\r
- * an error if two interceptor share the same flag.\r
- * @throws ChannelException\r
- */\r
- protected void checkOptionFlags() throws ChannelException {\r
- StringBuffer conflicts = new StringBuffer();\r
- ChannelInterceptor first = interceptors;\r
- while ( first != null ) {\r
- int flag = first.getOptionFlag();\r
- if ( flag != 0 ) {\r
- ChannelInterceptor next = first.getNext();\r
- while ( next != null ) {\r
- int nflag = next.getOptionFlag();\r
- if (nflag!=0 && (((flag & nflag) == flag ) || ((flag & nflag) == nflag)) ) {\r
- conflicts.append("[");\r
- conflicts.append(first.getClass().getName());\r
- conflicts.append(":");\r
- conflicts.append(flag);\r
- conflicts.append(" == ");\r
- conflicts.append(next.getClass().getName());\r
- conflicts.append(":");\r
- conflicts.append(nflag);\r
- conflicts.append("] ");\r
- }//end if\r
- next = next.getNext();\r
- }//while\r
- }//end if\r
- first = first.getNext();\r
- }//while\r
- if ( conflicts.length() > 0 ) throw new ChannelException("Interceptor option flag conflict: "+conflicts.toString());\r
-\r
- }\r
-\r
- /**\r
- * Starts the channel\r
- * @param svc int - what service to start\r
- * @throws ChannelException\r
- * @see org.apache.catalina.tribes.Channel#start(int)\r
- */\r
- public synchronized void start(int svc) throws ChannelException {\r
- setupDefaultStack();\r
- if (optionCheck) checkOptionFlags();\r
- super.start(svc);\r
- if ( hbthread == null && heartbeat ) {\r
- hbthread = new HeartbeatThread(this,heartbeatSleeptime);\r
- hbthread.start();\r
- }\r
- }\r
-\r
- /**\r
- * Stops the channel\r
- * @param svc int\r
- * @throws ChannelException\r
- * @see org.apache.catalina.tribes.Channel#stop(int)\r
- */\r
- public synchronized void stop(int svc) throws ChannelException {\r
- if (hbthread != null) {\r
- hbthread.stopHeartbeat();\r
- hbthread = null;\r
- }\r
- super.stop(svc);\r
- }\r
-\r
- /**\r
- * Returns the first interceptor of the stack. Useful for traversal.\r
- * @return ChannelInterceptor\r
- */\r
- public ChannelInterceptor getFirstInterceptor() {\r
- if (interceptors != null) return interceptors;\r
- else return coordinator;\r
- }\r
-\r
- /**\r
- * Returns the channel receiver component\r
- * @return ChannelReceiver\r
- */\r
- public ChannelReceiver getChannelReceiver() {\r
- return coordinator.getClusterReceiver();\r
- }\r
-\r
- /**\r
- * Returns the channel sender component\r
- * @return ChannelSender\r
- */\r
- public ChannelSender getChannelSender() {\r
- return coordinator.getClusterSender();\r
- }\r
-\r
- /**\r
- * Returns the membership service component\r
- * @return MembershipService\r
- */\r
- public MembershipService getMembershipService() {\r
- return coordinator.getMembershipService();\r
- }\r
-\r
- /**\r
- * Sets the channel receiver component\r
- * @param clusterReceiver ChannelReceiver\r
- */\r
- public void setChannelReceiver(ChannelReceiver clusterReceiver) {\r
- coordinator.setClusterReceiver(clusterReceiver);\r
- }\r
-\r
- /**\r
- * Sets the channel sender component\r
- * @param clusterSender ChannelSender\r
- */\r
- public void setChannelSender(ChannelSender clusterSender) {\r
- coordinator.setClusterSender(clusterSender);\r
- }\r
-\r
- /**\r
- * Sets the membership component\r
- * @param membershipService MembershipService\r
- */\r
- public void setMembershipService(MembershipService membershipService) {\r
- coordinator.setMembershipService(membershipService);\r
- }\r
-\r
- /**\r
- * Adds a membership listener to the channel.<br>\r
- * Membership listeners are uniquely identified using the equals(Object) method\r
- * @param membershipListener MembershipListener\r
- */\r
- public void addMembershipListener(MembershipListener membershipListener) {\r
- if (!this.membershipListeners.contains(membershipListener) )\r
- this.membershipListeners.add(membershipListener);\r
- }\r
-\r
- /**\r
- * Removes a membership listener from the channel.<br>\r
- * Membership listeners are uniquely identified using the equals(Object) method\r
- * @param membershipListener MembershipListener\r
- */\r
-\r
- public void removeMembershipListener(MembershipListener membershipListener) {\r
- membershipListeners.remove(membershipListener);\r
- }\r
-\r
- /**\r
- * Adds a channel listener to the channel.<br>\r
- * Channel listeners are uniquely identified using the equals(Object) method\r
- * @param channelListener ChannelListener\r
- */\r
- public void addChannelListener(ChannelListener channelListener) {\r
- if (!this.channelListeners.contains(channelListener) ) {\r
- this.channelListeners.add(channelListener);\r
- } else {\r
- throw new IllegalArgumentException("Listener already exists:"+channelListener+"["+channelListener.getClass().getName()+"]");\r
- }\r
- }\r
-\r
- /**\r
- *\r
- * Removes a channel listener from the channel.<br>\r
- * Channel listeners are uniquely identified using the equals(Object) method\r
- * @param channelListener ChannelListener\r
- */\r
- public void removeChannelListener(ChannelListener channelListener) {\r
- channelListeners.remove(channelListener);\r
- }\r
-\r
- /**\r
- * Returns an iterator of all the interceptors in this stack\r
- * @return Iterator\r
- */\r
- public Iterator getInterceptors() {\r
- return new InterceptorIterator(this.getNext(),this.coordinator);\r
- }\r
-\r
- /**\r
- * Enables/disables the option check<br>\r
- * Setting this to true, will make the GroupChannel perform a conflict check\r
- * on the interceptors. If two interceptors are using the same option flag\r
- * and throw an error upon start.\r
- * @param optionCheck boolean\r
- */\r
- public void setOptionCheck(boolean optionCheck) {\r
- this.optionCheck = optionCheck;\r
- }\r
-\r
- /**\r
- * Configure local heartbeat sleep time<br>\r
- * Only used when <code>getHeartbeat()==true</code>\r
- * @param heartbeatSleeptime long - time in milliseconds to sleep between heartbeats\r
- */\r
- public void setHeartbeatSleeptime(long heartbeatSleeptime) {\r
- this.heartbeatSleeptime = heartbeatSleeptime;\r
- }\r
-\r
- /**\r
- * Enables or disables local heartbeat.\r
- * if <code>setHeartbeat(true)</code> is invoked then the channel will start an internal\r
- * thread to invoke <code>Channel.heartbeat()</code> every <code>getHeartbeatSleeptime</code> milliseconds\r
- * @param heartbeat boolean\r
- */\r
- public void setHeartbeat(boolean heartbeat) {\r
- this.heartbeat = heartbeat;\r
- }\r
-\r
- /**\r
- * @see #setOptionCheck(boolean)\r
- * @return boolean\r
- */\r
- public boolean getOptionCheck() {\r
- return optionCheck;\r
- }\r
-\r
- /**\r
- * @see #setHeartbeat(boolean)\r
- * @return boolean\r
- */\r
- public boolean getHeartbeat() {\r
- return heartbeat;\r
- }\r
-\r
- /**\r
- * Returns the sleep time in milliseconds that the internal heartbeat will\r
- * sleep in between invokations of <code>Channel.heartbeat()</code>\r
- * @return long\r
- */\r
- public long getHeartbeatSleeptime() {\r
- return heartbeatSleeptime;\r
- }\r
-\r
- /**\r
- *\r
- * <p>Title: Interceptor Iterator</p>\r
- *\r
- * <p>Description: An iterator to loop through the interceptors in a channel</p>\r
- *\r
- * @version 1.0\r
- */\r
- public static class InterceptorIterator implements Iterator {\r
- private ChannelInterceptor end;\r
- private ChannelInterceptor start;\r
- public InterceptorIterator(ChannelInterceptor start, ChannelInterceptor end) {\r
- this.end = end;\r
- this.start = start;\r
- }\r
-\r
- public boolean hasNext() {\r
- return start!=null && start != end;\r
- }\r
-\r
- public Object next() {\r
- Object result = null;\r
- if ( hasNext() ) {\r
- result = start;\r
- start = start.getNext();\r
- }\r
- return result;\r
- }\r
-\r
- public void remove() {\r
- //empty operation\r
- }\r
- }\r
-\r
- /**\r
- *\r
- * <p>Title: Internal heartbeat thread</p>\r
- *\r
- * <p>Description: if <code>Channel.getHeartbeat()==true</code> then a thread of this class\r
- * is created</p>\r
- *\r
- * @version 1.0\r
- */\r
- public static class HeartbeatThread extends Thread {\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(HeartbeatThread.class);\r
- protected static int counter = 1;\r
- protected static synchronized int inc() {\r
- return counter++;\r
- }\r
-\r
- protected boolean doRun = true;\r
- protected GroupChannel channel;\r
- protected long sleepTime;\r
- public HeartbeatThread(GroupChannel channel, long sleepTime) {\r
- super();\r
- this.setPriority(MIN_PRIORITY);\r
- setName("GroupChannel-Heartbeat-"+inc());\r
- setDaemon(true);\r
- this.channel = channel;\r
- this.sleepTime = sleepTime;\r
- }\r
- public void stopHeartbeat() {\r
- doRun = false;\r
- interrupt();\r
- }\r
-\r
- public void run() {\r
- while (doRun) {\r
- try {\r
- Thread.sleep(sleepTime);\r
- channel.heartbeat();\r
- } catch ( InterruptedException x ) {\r
- interrupted();\r
- } catch ( Exception x ) {\r
- log.error("Unable to send heartbeat through Tribes interceptor stack. Will try to sleep again.",x);\r
- }//catch\r
- }//while\r
- }//run\r
- }//HeartbeatThread\r
-\r
-\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.group;
+
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.catalina.tribes.ByteMessage;
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelInterceptor;
+import org.apache.catalina.tribes.ChannelListener;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.ChannelReceiver;
+import org.apache.catalina.tribes.ChannelSender;
+import org.apache.catalina.tribes.ErrorHandler;
+import org.apache.catalina.tribes.ManagedChannel;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipListener;
+import org.apache.catalina.tribes.MembershipService;
+import org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor;
+import org.apache.catalina.tribes.io.ChannelData;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.catalina.tribes.UniqueId;
+import org.apache.catalina.tribes.Heartbeat;
+import org.apache.catalina.tribes.io.BufferPool;
+import java.io.IOException;
+import org.apache.catalina.tribes.RemoteProcessException;
+import org.apache.catalina.tribes.util.Logs;
+import org.apache.catalina.tribes.util.Arrays;
+
+/**
+ * The default implementation of a Channel.<br>
+ * The GroupChannel manages the replication channel. It coordinates
+ * message being sent and received with membership announcements.
+ * The channel has an chain of interceptors that can modify the message or perform other logic.<br>
+ * It manages a complete group, both membership and replication.
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+public class GroupChannel extends ChannelInterceptorBase implements ManagedChannel {
+ /**
+ * Flag to determine if the channel manages its own heartbeat
+ * If set to true, the channel will start a local thread for the heart beat.
+ */
+ protected boolean heartbeat = true;
+ /**
+ * If <code>heartbeat == true</code> then how often do we want this
+ * heartbeat to run. default is one minute
+ */
+ protected long heartbeatSleeptime = 5*1000;//every 5 seconds
+
+ /**
+ * Internal heartbeat thread
+ */
+ protected HeartbeatThread hbthread = null;
+
+ /**
+ * The <code>ChannelCoordinator</code> coordinates the bottom layer components:<br>
+ * - MembershipService<br>
+ * - ChannelSender <br>
+ * - ChannelReceiver<br>
+ */
+ protected ChannelCoordinator coordinator = new ChannelCoordinator();
+
+ /**
+ * The first interceptor in the inteceptor stack.
+ * The interceptors are chained in a linked list, so we only need a reference to the
+ * first one
+ */
+ protected ChannelInterceptor interceptors = null;
+
+ /**
+ * A list of membership listeners that subscribe to membership announcements
+ */
+ protected ArrayList membershipListeners = new ArrayList();
+
+ /**
+ * A list of channel listeners that subscribe to incoming messages
+ */
+ protected ArrayList channelListeners = new ArrayList();
+
+ /**
+ * If set to true, the GroupChannel will check to make sure that
+ */
+ protected boolean optionCheck = false;
+
+ /**
+ * Creates a GroupChannel. This constructor will also
+ * add the first interceptor in the GroupChannel.<br>
+ * The first interceptor is always the channel itself.
+ */
+ public GroupChannel() {
+ addInterceptor(this);
+ }
+
+
+ /**
+ * Adds an interceptor to the stack for message processing<br>
+ * Interceptors are ordered in the way they are added.<br>
+ * <code>channel.addInterceptor(A);</code><br>
+ * <code>channel.addInterceptor(C);</code><br>
+ * <code>channel.addInterceptor(B);</code><br>
+ * Will result in a interceptor stack like this:<br>
+ * <code>A -> C -> B</code><br>
+ * The complete stack will look like this:<br>
+ * <code>Channel -> A -> C -> B -> ChannelCoordinator</code><br>
+ * @param interceptor ChannelInterceptorBase
+ */
+ public void addInterceptor(ChannelInterceptor interceptor) {
+ if ( interceptors == null ) {
+ interceptors = interceptor;
+ interceptors.setNext(coordinator);
+ interceptors.setPrevious(null);
+ coordinator.setPrevious(interceptors);
+ } else {
+ ChannelInterceptor last = interceptors;
+ while ( last.getNext() != coordinator ) {
+ last = last.getNext();
+ }
+ last.setNext(interceptor);
+ interceptor.setNext(coordinator);
+ interceptor.setPrevious(last);
+ coordinator.setPrevious(interceptor);
+ }
+ }
+
+ /**
+ * Sends a heartbeat through the interceptor stack.<br>
+ * Invoke this method from the application on a periodic basis if
+ * you have turned off internal heartbeats <code>channel.setHeartbeat(false)</code>
+ */
+ public void heartbeat() {
+ super.heartbeat();
+ Iterator i = membershipListeners.iterator();
+ while ( i.hasNext() ) {
+ Object o = i.next();
+ if ( o instanceof Heartbeat ) ((Heartbeat)o).heartbeat();
+ }
+ i = channelListeners.iterator();
+ while ( i.hasNext() ) {
+ Object o = i.next();
+ if ( o instanceof Heartbeat ) ((Heartbeat)o).heartbeat();
+ }
+
+ }
+
+
+ /**
+ * Send a message to the destinations specified
+ * @param destination Member[] - destination.length > 1
+ * @param msg Serializable - the message to send
+ * @param options int - sender options, options can trigger guarantee levels and different interceptors to
+ * react to the message see class documentation for the <code>Channel</code> object.<br>
+ * @return UniqueId - the unique Id that was assigned to this message
+ * @throws ChannelException - if an error occurs processing the message
+ * @see org.apache.catalina.tribes.Channel
+ */
+ public UniqueId send(Member[] destination, Serializable msg, int options) throws ChannelException {
+ return send(destination,msg,options,null);
+ }
+
+ /**
+ *
+ * @param destination Member[] - destination.length > 1
+ * @param msg Serializable - the message to send
+ * @param options int - sender options, options can trigger guarantee levels and different interceptors to
+ * react to the message see class documentation for the <code>Channel</code> object.<br>
+ * @param handler - callback object for error handling and completion notification, used when a message is
+ * sent asynchronously using the <code>Channel.SEND_OPTIONS_ASYNCHRONOUS</code> flag enabled.
+ * @return UniqueId - the unique Id that was assigned to this message
+ * @throws ChannelException - if an error occurs processing the message
+ * @see org.apache.catalina.tribes.Channel
+ */
+ public UniqueId send(Member[] destination, Serializable msg, int options, ErrorHandler handler) throws ChannelException {
+ if ( msg == null ) throw new ChannelException("Cant send a NULL message");
+ XByteBuffer buffer = null;
+ try {
+ if ( destination == null || destination.length == 0) throw new ChannelException("No destination given");
+ ChannelData data = new ChannelData(true);//generates a unique Id
+ data.setAddress(getLocalMember(false));
+ data.setTimestamp(System.currentTimeMillis());
+ byte[] b = null;
+ if ( msg instanceof ByteMessage ){
+ b = ((ByteMessage)msg).getMessage();
+ options = options | SEND_OPTIONS_BYTE_MESSAGE;
+ } else {
+ b = XByteBuffer.serialize(msg);
+ options = options & (~SEND_OPTIONS_BYTE_MESSAGE);
+ }
+ data.setOptions(options);
+ //XByteBuffer buffer = new XByteBuffer(b.length+128,false);
+ buffer = BufferPool.getBufferPool().getBuffer(b.length+128, false);
+ buffer.append(b,0,b.length);
+ data.setMessage(buffer);
+ InterceptorPayload payload = null;
+ if ( handler != null ) {
+ payload = new InterceptorPayload();
+ payload.setErrorHandler(handler);
+ }
+ getFirstInterceptor().sendMessage(destination, data, payload);
+ if ( Logs.MESSAGES.isTraceEnabled() ) {
+ Logs.MESSAGES.trace("GroupChannel - Sent msg:" + new UniqueId(data.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " to "+Arrays.toNameString(destination));
+ Logs.MESSAGES.trace("GroupChannel - Send Message:" + new UniqueId(data.getUniqueId()) + " is " +msg);
+ }
+
+ return new UniqueId(data.getUniqueId());
+ }catch ( Exception x ) {
+ if ( x instanceof ChannelException ) throw (ChannelException)x;
+ throw new ChannelException(x);
+ } finally {
+ if ( buffer != null ) BufferPool.getBufferPool().returnBuffer(buffer);
+ }
+ }
+
+
+ /**
+ * Callback from the interceptor stack. <br>
+ * When a message is received from a remote node, this method will be invoked by
+ * the previous interceptor.<br>
+ * This method can also be used to send a message to other components within the same application,
+ * but its an extreme case, and you're probably better off doing that logic between the applications itself.
+ * @param msg ChannelMessage
+ */
+ public void messageReceived(ChannelMessage msg) {
+ if ( msg == null ) return;
+ try {
+ if ( Logs.MESSAGES.isTraceEnabled() ) {
+ Logs.MESSAGES.trace("GroupChannel - Received msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " from "+msg.getAddress().getName());
+ }
+
+ Serializable fwd = null;
+ if ( (msg.getOptions() & SEND_OPTIONS_BYTE_MESSAGE) == SEND_OPTIONS_BYTE_MESSAGE ) {
+ fwd = new ByteMessage(msg.getMessage().getBytes());
+ } else {
+ fwd = XByteBuffer.deserialize(msg.getMessage().getBytesDirect(),0,msg.getMessage().getLength());
+ }
+ if ( Logs.MESSAGES.isTraceEnabled() ) {
+ Logs.MESSAGES.trace("GroupChannel - Receive Message:" + new UniqueId(msg.getUniqueId()) + " is " +fwd);
+ }
+
+ //get the actual member with the correct alive time
+ Member source = msg.getAddress();
+ boolean rx = false;
+ boolean delivered = false;
+ for ( int i=0; i<channelListeners.size(); i++ ) {
+ ChannelListener channelListener = (ChannelListener)channelListeners.get(i);
+ if (channelListener != null && channelListener.accept(fwd, source)) {
+ channelListener.messageReceived(fwd, source);
+ delivered = true;
+ //if the message was accepted by an RPC channel, that channel
+ //is responsible for returning the reply, otherwise we send an absence reply
+ if ( channelListener instanceof RpcChannel ) rx = true;
+ }
+ }//for
+ if ((!rx) && (fwd instanceof RpcMessage)) {
+ //if we have a message that requires a response,
+ //but none was given, send back an immediate one
+ sendNoRpcChannelReply((RpcMessage)fwd,source);
+ }
+ if ( Logs.MESSAGES.isTraceEnabled() ) {
+ Logs.MESSAGES.trace("GroupChannel delivered["+delivered+"] id:"+new UniqueId(msg.getUniqueId()));
+ }
+
+ } catch ( Exception x ) {
+ if ( log.isDebugEnabled() ) log.error("Unable to process channel:IOException.",x);
+ throw new RemoteProcessException("IOException:"+x.getMessage(),x);
+ }
+ }
+
+ /**
+ * Sends a <code>NoRpcChannelReply</code> message to a member<br>
+ * This method gets invoked by the channel if a RPC message comes in
+ * and no channel listener accepts the message. This avoids timeout
+ * @param msg RpcMessage
+ * @param destination Member - the destination for the reply
+ */
+ protected void sendNoRpcChannelReply(RpcMessage msg, Member destination) {
+ try {
+ //avoid circular loop
+ if ( msg instanceof RpcMessage.NoRpcChannelReply) return;
+ RpcMessage.NoRpcChannelReply reply = new RpcMessage.NoRpcChannelReply(msg.rpcId,msg.uuid);
+ send(new Member[]{destination},reply,Channel.SEND_OPTIONS_ASYNCHRONOUS);
+ } catch ( Exception x ) {
+ log.error("Unable to find rpc channel, failed to send NoRpcChannelReply.",x);
+ }
+ }
+
+ /**
+ * memberAdded gets invoked by the interceptor below the channel
+ * and the channel will broadcast it to the membership listeners
+ * @param member Member - the new member
+ */
+ public void memberAdded(Member member) {
+ //notify upwards
+ for (int i=0; i<membershipListeners.size(); i++ ) {
+ MembershipListener membershipListener = (MembershipListener)membershipListeners.get(i);
+ if (membershipListener != null) membershipListener.memberAdded(member);
+ }
+ }
+
+ /**
+ * memberDisappeared gets invoked by the interceptor below the channel
+ * and the channel will broadcast it to the membership listeners
+ * @param member Member - the member that left or crashed
+ */
+ public void memberDisappeared(Member member) {
+ //notify upwards
+ for (int i=0; i<membershipListeners.size(); i++ ) {
+ MembershipListener membershipListener = (MembershipListener)membershipListeners.get(i);
+ if (membershipListener != null) membershipListener.memberDisappeared(member);
+ }
+ }
+
+ /**
+ * Sets up the default implementation interceptor stack
+ * if no interceptors have been added
+ * @throws ChannelException
+ */
+ protected synchronized void setupDefaultStack() throws ChannelException {
+
+ if ( getFirstInterceptor() != null &&
+ ((getFirstInterceptor().getNext() instanceof ChannelCoordinator))) {
+ ChannelInterceptor interceptor = null;
+ Class clazz = null;
+ try {
+ clazz = Class.forName("org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor",
+ true,GroupChannel.class.getClassLoader());
+ clazz.newInstance();
+ } catch ( Throwable x ) {
+ clazz = MessageDispatchInterceptor.class;
+ }//catch
+ try {
+ interceptor = (ChannelInterceptor) clazz.newInstance();
+ } catch (Exception x) {
+ throw new ChannelException("Unable to add MessageDispatchInterceptor to interceptor chain.",x);
+ }
+ this.addInterceptor(interceptor);
+ }
+ }
+
+ /**
+ * Validates the option flags that each interceptor is using and reports
+ * an error if two interceptor share the same flag.
+ * @throws ChannelException
+ */
+ protected void checkOptionFlags() throws ChannelException {
+ StringBuffer conflicts = new StringBuffer();
+ ChannelInterceptor first = interceptors;
+ while ( first != null ) {
+ int flag = first.getOptionFlag();
+ if ( flag != 0 ) {
+ ChannelInterceptor next = first.getNext();
+ while ( next != null ) {
+ int nflag = next.getOptionFlag();
+ if (nflag!=0 && (((flag & nflag) == flag ) || ((flag & nflag) == nflag)) ) {
+ conflicts.append("[");
+ conflicts.append(first.getClass().getName());
+ conflicts.append(":");
+ conflicts.append(flag);
+ conflicts.append(" == ");
+ conflicts.append(next.getClass().getName());
+ conflicts.append(":");
+ conflicts.append(nflag);
+ conflicts.append("] ");
+ }//end if
+ next = next.getNext();
+ }//while
+ }//end if
+ first = first.getNext();
+ }//while
+ if ( conflicts.length() > 0 ) throw new ChannelException("Interceptor option flag conflict: "+conflicts.toString());
+
+ }
+
+ /**
+ * Starts the channel
+ * @param svc int - what service to start
+ * @throws ChannelException
+ * @see org.apache.catalina.tribes.Channel#start(int)
+ */
+ public synchronized void start(int svc) throws ChannelException {
+ setupDefaultStack();
+ if (optionCheck) checkOptionFlags();
+ super.start(svc);
+ if ( hbthread == null && heartbeat ) {
+ hbthread = new HeartbeatThread(this,heartbeatSleeptime);
+ hbthread.start();
+ }
+ }
+
+ /**
+ * Stops the channel
+ * @param svc int
+ * @throws ChannelException
+ * @see org.apache.catalina.tribes.Channel#stop(int)
+ */
+ public synchronized void stop(int svc) throws ChannelException {
+ if (hbthread != null) {
+ hbthread.stopHeartbeat();
+ hbthread = null;
+ }
+ super.stop(svc);
+ }
+
+ /**
+ * Returns the first interceptor of the stack. Useful for traversal.
+ * @return ChannelInterceptor
+ */
+ public ChannelInterceptor getFirstInterceptor() {
+ if (interceptors != null) return interceptors;
+ else return coordinator;
+ }
+
+ /**
+ * Returns the channel receiver component
+ * @return ChannelReceiver
+ */
+ public ChannelReceiver getChannelReceiver() {
+ return coordinator.getClusterReceiver();
+ }
+
+ /**
+ * Returns the channel sender component
+ * @return ChannelSender
+ */
+ public ChannelSender getChannelSender() {
+ return coordinator.getClusterSender();
+ }
+
+ /**
+ * Returns the membership service component
+ * @return MembershipService
+ */
+ public MembershipService getMembershipService() {
+ return coordinator.getMembershipService();
+ }
+
+ /**
+ * Sets the channel receiver component
+ * @param clusterReceiver ChannelReceiver
+ */
+ public void setChannelReceiver(ChannelReceiver clusterReceiver) {
+ coordinator.setClusterReceiver(clusterReceiver);
+ }
+
+ /**
+ * Sets the channel sender component
+ * @param clusterSender ChannelSender
+ */
+ public void setChannelSender(ChannelSender clusterSender) {
+ coordinator.setClusterSender(clusterSender);
+ }
+
+ /**
+ * Sets the membership component
+ * @param membershipService MembershipService
+ */
+ public void setMembershipService(MembershipService membershipService) {
+ coordinator.setMembershipService(membershipService);
+ }
+
+ /**
+ * Adds a membership listener to the channel.<br>
+ * Membership listeners are uniquely identified using the equals(Object) method
+ * @param membershipListener MembershipListener
+ */
+ public void addMembershipListener(MembershipListener membershipListener) {
+ if (!this.membershipListeners.contains(membershipListener) )
+ this.membershipListeners.add(membershipListener);
+ }
+
+ /**
+ * Removes a membership listener from the channel.<br>
+ * Membership listeners are uniquely identified using the equals(Object) method
+ * @param membershipListener MembershipListener
+ */
+
+ public void removeMembershipListener(MembershipListener membershipListener) {
+ membershipListeners.remove(membershipListener);
+ }
+
+ /**
+ * Adds a channel listener to the channel.<br>
+ * Channel listeners are uniquely identified using the equals(Object) method
+ * @param channelListener ChannelListener
+ */
+ public void addChannelListener(ChannelListener channelListener) {
+ if (!this.channelListeners.contains(channelListener) ) {
+ this.channelListeners.add(channelListener);
+ } else {
+ throw new IllegalArgumentException("Listener already exists:"+channelListener+"["+channelListener.getClass().getName()+"]");
+ }
+ }
+
+ /**
+ *
+ * Removes a channel listener from the channel.<br>
+ * Channel listeners are uniquely identified using the equals(Object) method
+ * @param channelListener ChannelListener
+ */
+ public void removeChannelListener(ChannelListener channelListener) {
+ channelListeners.remove(channelListener);
+ }
+
+ /**
+ * Returns an iterator of all the interceptors in this stack
+ * @return Iterator
+ */
+ public Iterator getInterceptors() {
+ return new InterceptorIterator(this.getNext(),this.coordinator);
+ }
+
+ /**
+ * Enables/disables the option check<br>
+ * Setting this to true, will make the GroupChannel perform a conflict check
+ * on the interceptors. If two interceptors are using the same option flag
+ * and throw an error upon start.
+ * @param optionCheck boolean
+ */
+ public void setOptionCheck(boolean optionCheck) {
+ this.optionCheck = optionCheck;
+ }
+
+ /**
+ * Configure local heartbeat sleep time<br>
+ * Only used when <code>getHeartbeat()==true</code>
+ * @param heartbeatSleeptime long - time in milliseconds to sleep between heartbeats
+ */
+ public void setHeartbeatSleeptime(long heartbeatSleeptime) {
+ this.heartbeatSleeptime = heartbeatSleeptime;
+ }
+
+ /**
+ * Enables or disables local heartbeat.
+ * if <code>setHeartbeat(true)</code> is invoked then the channel will start an internal
+ * thread to invoke <code>Channel.heartbeat()</code> every <code>getHeartbeatSleeptime</code> milliseconds
+ * @param heartbeat boolean
+ */
+ public void setHeartbeat(boolean heartbeat) {
+ this.heartbeat = heartbeat;
+ }
+
+ /**
+ * @see #setOptionCheck(boolean)
+ * @return boolean
+ */
+ public boolean getOptionCheck() {
+ return optionCheck;
+ }
+
+ /**
+ * @see #setHeartbeat(boolean)
+ * @return boolean
+ */
+ public boolean getHeartbeat() {
+ return heartbeat;
+ }
+
+ /**
+ * Returns the sleep time in milliseconds that the internal heartbeat will
+ * sleep in between invokations of <code>Channel.heartbeat()</code>
+ * @return long
+ */
+ public long getHeartbeatSleeptime() {
+ return heartbeatSleeptime;
+ }
+
+ /**
+ *
+ * <p>Title: Interceptor Iterator</p>
+ *
+ * <p>Description: An iterator to loop through the interceptors in a channel</p>
+ *
+ * @version 1.0
+ */
+ public static class InterceptorIterator implements Iterator {
+ private ChannelInterceptor end;
+ private ChannelInterceptor start;
+ public InterceptorIterator(ChannelInterceptor start, ChannelInterceptor end) {
+ this.end = end;
+ this.start = start;
+ }
+
+ public boolean hasNext() {
+ return start!=null && start != end;
+ }
+
+ public Object next() {
+ Object result = null;
+ if ( hasNext() ) {
+ result = start;
+ start = start.getNext();
+ }
+ return result;
+ }
+
+ public void remove() {
+ //empty operation
+ }
+ }
+
+ /**
+ *
+ * <p>Title: Internal heartbeat thread</p>
+ *
+ * <p>Description: if <code>Channel.getHeartbeat()==true</code> then a thread of this class
+ * is created</p>
+ *
+ * @version 1.0
+ */
+ public static class HeartbeatThread extends Thread {
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(HeartbeatThread.class);
+ protected static int counter = 1;
+ protected static synchronized int inc() {
+ return counter++;
+ }
+
+ protected boolean doRun = true;
+ protected GroupChannel channel;
+ protected long sleepTime;
+ public HeartbeatThread(GroupChannel channel, long sleepTime) {
+ super();
+ this.setPriority(MIN_PRIORITY);
+ setName("GroupChannel-Heartbeat-"+inc());
+ setDaemon(true);
+ this.channel = channel;
+ this.sleepTime = sleepTime;
+ }
+ public void stopHeartbeat() {
+ doRun = false;
+ interrupt();
+ }
+
+ public void run() {
+ while (doRun) {
+ try {
+ Thread.sleep(sleepTime);
+ channel.heartbeat();
+ } catch ( InterruptedException x ) {
+ interrupted();
+ } catch ( Exception x ) {
+ log.error("Unable to send heartbeat through Tribes interceptor stack. Will try to sleep again.",x);
+ }//catch
+ }//while
+ }//run
+ }//HeartbeatThread
+
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.group;\r
-\r
-import org.apache.catalina.tribes.ErrorHandler;\r
-\r
-/**\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class InterceptorPayload {\r
- private ErrorHandler errorHandler;\r
- \r
- public ErrorHandler getErrorHandler() {\r
- return errorHandler;\r
- }\r
-\r
- public void setErrorHandler(ErrorHandler errorHandler) {\r
- this.errorHandler = errorHandler;\r
- }\r
+/*
+ * 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.apache.catalina.tribes.group;
+
+import org.apache.catalina.tribes.ErrorHandler;
+
+/**
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class InterceptorPayload {
+ private ErrorHandler errorHandler;
+
+ public ErrorHandler getErrorHandler() {
+ return errorHandler;
+ }
+
+ public void setErrorHandler(ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.group;\r
-\r
-import java.io.Serializable;\r
-\r
-import org.apache.catalina.tribes.Member;\r
-\r
-/**\r
- * A response object holds a message from a responding partner.\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class Response {\r
- private Member source;\r
- private Serializable message;\r
- public Response() {\r
- }\r
- \r
- public Response(Member source, Serializable message) {\r
- this.source = source;\r
- this.message = message;\r
- }\r
-\r
- public void setSource(Member source) {\r
- this.source = source;\r
- }\r
-\r
- public void setMessage(Serializable message) {\r
- this.message = message;\r
- }\r
-\r
- public Member getSource() {\r
- return source;\r
- }\r
-\r
- public Serializable getMessage() {\r
- return message;\r
- }\r
+/*
+ * 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.apache.catalina.tribes.group;
+
+import java.io.Serializable;
+
+import org.apache.catalina.tribes.Member;
+
+/**
+ * A response object holds a message from a responding partner.
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class Response {
+ private Member source;
+ private Serializable message;
+ public Response() {
+ }
+
+ public Response(Member source, Serializable message) {
+ this.source = source;
+ this.message = message;
+ }
+
+ public void setSource(Member source) {
+ this.source = source;
+ }
+
+ public void setMessage(Serializable message) {
+ this.message = message;
+ }
+
+ public Member getSource() {
+ return source;
+ }
+
+ public Serializable getMessage() {
+ return message;
+ }
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.group;\r
-\r
-import java.io.Serializable;\r
-\r
-import org.apache.catalina.tribes.Member;\r
-\r
-/**\r
- * The RpcCallback interface is an interface for the Tribes channel to request a\r
- * response object to a request that came in.\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-public interface RpcCallback {\r
- \r
- /**\r
- * \r
- * @param msg Serializable\r
- * @return Serializable - null if no reply should be sent\r
- */\r
- public Serializable replyRequest(Serializable msg, Member sender);\r
- \r
- /**\r
- * If the reply has already been sent to the requesting thread,\r
- * the rpc callback can handle any data that comes in after the fact.\r
- * @param msg Serializable\r
- * @param sender Member\r
- */\r
- public void leftOver(Serializable msg, Member sender);\r
- \r
+/*
+ * 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.apache.catalina.tribes.group;
+
+import java.io.Serializable;
+
+import org.apache.catalina.tribes.Member;
+
+/**
+ * The RpcCallback interface is an interface for the Tribes channel to request a
+ * response object to a request that came in.
+ * @author not attributable
+ * @version 1.0
+ */
+public interface RpcCallback {
+
+ /**
+ *
+ * @param msg Serializable
+ * @return Serializable - null if no reply should be sent
+ */
+ public Serializable replyRequest(Serializable msg, Member sender);
+
+ /**
+ * If the reply has already been sent to the requesting thread,
+ * the rpc callback can handle any data that comes in after the fact.
+ * @param msg Serializable
+ * @param sender Member
+ */
+ public void leftOver(Serializable msg, Member sender);
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.group;\r
-\r
-import java.io.Serializable;\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelListener;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.util.UUIDGenerator;\r
-\r
-/**\r
- * A channel to handle RPC messaging\r
- * @author Filip Hanik\r
- */\r
-public class RpcChannel implements ChannelListener{\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(RpcChannel.class);\r
- \r
- public static final int FIRST_REPLY = 1;\r
- public static final int MAJORITY_REPLY = 2;\r
- public static final int ALL_REPLY = 3;\r
- public static final int NO_REPLY = 4;\r
- \r
- private Channel channel;\r
- private RpcCallback callback;\r
- private byte[] rpcId;\r
- \r
- private HashMap responseMap = new HashMap();\r
-\r
- /**\r
- * Create an RPC channel. You can have several RPC channels attached to a group\r
- * all separated out by the uniqueness\r
- * @param rpcId - the unique Id for this RPC group\r
- * @param channel Channel\r
- * @param callback RpcCallback\r
- */\r
- public RpcChannel(byte[] rpcId, Channel channel, RpcCallback callback) {\r
- this.channel = channel;\r
- this.callback = callback;\r
- this.rpcId = rpcId;\r
- channel.addChannelListener(this);\r
- }\r
- \r
- \r
- /**\r
- * Send a message and wait for the response.\r
- * @param destination Member[] - the destination for the message, and the members you request a reply from\r
- * @param message Serializable - the message you are sending out\r
- * @param options int - FIRST_REPLY, MAJORITY_REPLY or ALL_REPLY\r
- * @param timeout long - timeout in milliseconds, if no reply is received within this time null is returned\r
- * @return Response[] - an array of response objects.\r
- * @throws ChannelException\r
- */\r
- public Response[] send(Member[] destination, \r
- Serializable message,\r
- int rpcOptions, \r
- int channelOptions,\r
- long timeout) throws ChannelException {\r
- \r
- if ( destination==null || destination.length == 0 ) return new Response[0];\r
- \r
- //avoid dead lock\r
- channelOptions = channelOptions & ~Channel.SEND_OPTIONS_SYNCHRONIZED_ACK;\r
- \r
- RpcCollectorKey key = new RpcCollectorKey(UUIDGenerator.randomUUID(false));\r
- RpcCollector collector = new RpcCollector(key,rpcOptions,destination.length,timeout);\r
- try {\r
- synchronized (collector) {\r
- if ( rpcOptions != NO_REPLY ) responseMap.put(key, collector);\r
- RpcMessage rmsg = new RpcMessage(rpcId, key.id, message);\r
- channel.send(destination, rmsg, channelOptions);\r
- if ( rpcOptions != NO_REPLY ) collector.wait(timeout);\r
- }\r
- } catch ( InterruptedException ix ) {\r
- Thread.currentThread().interrupted();\r
- //throw new ChannelException(ix);\r
- }finally {\r
- responseMap.remove(key);\r
- }\r
- return collector.getResponses();\r
- }\r
- \r
- public void messageReceived(Serializable msg, Member sender) {\r
- RpcMessage rmsg = (RpcMessage)msg;\r
- RpcCollectorKey key = new RpcCollectorKey(rmsg.uuid);\r
- if ( rmsg.reply ) {\r
- RpcCollector collector = (RpcCollector)responseMap.get(key);\r
- if (collector == null) {\r
- callback.leftOver(rmsg.message, sender);\r
- } else {\r
- synchronized (collector) {\r
- //make sure it hasn't been removed\r
- if ( responseMap.containsKey(key) ) {\r
- if ( (rmsg instanceof RpcMessage.NoRpcChannelReply) ) \r
- collector.destcnt--;\r
- else \r
- collector.addResponse(rmsg.message, sender);\r
- if (collector.isComplete()) collector.notifyAll();\r
- } else {\r
- if (! (rmsg instanceof RpcMessage.NoRpcChannelReply) ) \r
- callback.leftOver(rmsg.message, sender);\r
- }\r
- }//synchronized\r
- }//end if\r
- } else{\r
- Serializable reply = callback.replyRequest(rmsg.message,sender);\r
- rmsg.reply = true;\r
- rmsg.message = reply;\r
- try {\r
- channel.send(new Member[] {sender}, rmsg,0);\r
- }catch ( Exception x ) {\r
- log.error("Unable to send back reply in RpcChannel.",x);\r
- }\r
- }//end if\r
- }\r
- \r
- public void breakdown() {\r
- channel.removeChannelListener(this);\r
- }\r
- \r
- public void finalize() {\r
- breakdown();\r
- }\r
- \r
- public boolean accept(Serializable msg, Member sender) {\r
- if ( msg instanceof RpcMessage ) {\r
- RpcMessage rmsg = (RpcMessage)msg;\r
- return Arrays.equals(rmsg.rpcId,rpcId);\r
- }else return false;\r
- }\r
- \r
- public Channel getChannel() {\r
- return channel;\r
- }\r
-\r
- public RpcCallback getCallback() {\r
- return callback;\r
- }\r
-\r
- public byte[] getRpcId() {\r
- return rpcId;\r
- }\r
-\r
- public void setChannel(Channel channel) {\r
- this.channel = channel;\r
- }\r
-\r
- public void setCallback(RpcCallback callback) {\r
- this.callback = callback;\r
- }\r
-\r
- public void setRpcId(byte[] rpcId) {\r
- this.rpcId = rpcId;\r
- }\r
- \r
-\r
-\r
- /**\r
- * \r
- * Class that holds all response.\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
- public static class RpcCollector {\r
- public ArrayList responses = new ArrayList(); \r
- public RpcCollectorKey key;\r
- public int options;\r
- public int destcnt;\r
- public long timeout;\r
- \r
- public RpcCollector(RpcCollectorKey key, int options, int destcnt, long timeout) {\r
- this.key = key;\r
- this.options = options;\r
- this.destcnt = destcnt;\r
- this.timeout = timeout;\r
- }\r
- \r
- public void addResponse(Serializable message, Member sender){\r
- Response resp = new Response(sender,message);\r
- responses.add(resp);\r
- }\r
- \r
- public boolean isComplete() {\r
- if ( destcnt <= 0 ) return true;\r
- switch (options) {\r
- case ALL_REPLY:\r
- return destcnt == responses.size();\r
- case MAJORITY_REPLY:\r
- {\r
- float perc = ((float)responses.size()) / ((float)destcnt);\r
- return perc >= 0.50f;\r
- }\r
- case FIRST_REPLY:\r
- return responses.size()>0;\r
- default:\r
- return false;\r
- }\r
- }\r
- \r
- public int hashCode() {\r
- return key.hashCode();\r
- }\r
- \r
- public boolean equals(Object o) {\r
- if ( o instanceof RpcCollector ) {\r
- RpcCollector r = (RpcCollector)o;\r
- return r.key.equals(this.key);\r
- } else return false;\r
- }\r
- \r
- public Response[] getResponses() {\r
- return (Response[])responses.toArray(new Response[responses.size()]);\r
- }\r
- }\r
- \r
- public static class RpcCollectorKey {\r
- byte[] id;\r
- public RpcCollectorKey(byte[] id) {\r
- this.id = id;\r
- }\r
- \r
- public int hashCode() {\r
- return id[0]+id[1]+id[2]+id[3];\r
- }\r
-\r
- public boolean equals(Object o) {\r
- if ( o instanceof RpcCollectorKey ) {\r
- RpcCollectorKey r = (RpcCollectorKey)o;\r
- return Arrays.equals(id,r.id);\r
- } else return false;\r
- }\r
- \r
- }\r
- \r
- protected static String bToS(byte[] data) {\r
- StringBuffer buf = new StringBuffer(4*16);\r
- buf.append("{");\r
- for (int i=0; data!=null && i<data.length; i++ ) buf.append(String.valueOf(data[i])).append(" ");\r
- buf.append("}");\r
- return buf.toString();\r
- }\r
-\r
-\r
+/*
+ * 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.apache.catalina.tribes.group;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelListener;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.util.UUIDGenerator;
+
+/**
+ * A channel to handle RPC messaging
+ * @author Filip Hanik
+ */
+public class RpcChannel implements ChannelListener{
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(RpcChannel.class);
+
+ public static final int FIRST_REPLY = 1;
+ public static final int MAJORITY_REPLY = 2;
+ public static final int ALL_REPLY = 3;
+ public static final int NO_REPLY = 4;
+
+ private Channel channel;
+ private RpcCallback callback;
+ private byte[] rpcId;
+
+ private HashMap responseMap = new HashMap();
+
+ /**
+ * Create an RPC channel. You can have several RPC channels attached to a group
+ * all separated out by the uniqueness
+ * @param rpcId - the unique Id for this RPC group
+ * @param channel Channel
+ * @param callback RpcCallback
+ */
+ public RpcChannel(byte[] rpcId, Channel channel, RpcCallback callback) {
+ this.channel = channel;
+ this.callback = callback;
+ this.rpcId = rpcId;
+ channel.addChannelListener(this);
+ }
+
+
+ /**
+ * Send a message and wait for the response.
+ * @param destination Member[] - the destination for the message, and the members you request a reply from
+ * @param message Serializable - the message you are sending out
+ * @param options int - FIRST_REPLY, MAJORITY_REPLY or ALL_REPLY
+ * @param timeout long - timeout in milliseconds, if no reply is received within this time null is returned
+ * @return Response[] - an array of response objects.
+ * @throws ChannelException
+ */
+ public Response[] send(Member[] destination,
+ Serializable message,
+ int rpcOptions,
+ int channelOptions,
+ long timeout) throws ChannelException {
+
+ if ( destination==null || destination.length == 0 ) return new Response[0];
+
+ //avoid dead lock
+ channelOptions = channelOptions & ~Channel.SEND_OPTIONS_SYNCHRONIZED_ACK;
+
+ RpcCollectorKey key = new RpcCollectorKey(UUIDGenerator.randomUUID(false));
+ RpcCollector collector = new RpcCollector(key,rpcOptions,destination.length,timeout);
+ try {
+ synchronized (collector) {
+ if ( rpcOptions != NO_REPLY ) responseMap.put(key, collector);
+ RpcMessage rmsg = new RpcMessage(rpcId, key.id, message);
+ channel.send(destination, rmsg, channelOptions);
+ if ( rpcOptions != NO_REPLY ) collector.wait(timeout);
+ }
+ } catch ( InterruptedException ix ) {
+ Thread.currentThread().interrupted();
+ //throw new ChannelException(ix);
+ }finally {
+ responseMap.remove(key);
+ }
+ return collector.getResponses();
+ }
+
+ public void messageReceived(Serializable msg, Member sender) {
+ RpcMessage rmsg = (RpcMessage)msg;
+ RpcCollectorKey key = new RpcCollectorKey(rmsg.uuid);
+ if ( rmsg.reply ) {
+ RpcCollector collector = (RpcCollector)responseMap.get(key);
+ if (collector == null) {
+ callback.leftOver(rmsg.message, sender);
+ } else {
+ synchronized (collector) {
+ //make sure it hasn't been removed
+ if ( responseMap.containsKey(key) ) {
+ if ( (rmsg instanceof RpcMessage.NoRpcChannelReply) )
+ collector.destcnt--;
+ else
+ collector.addResponse(rmsg.message, sender);
+ if (collector.isComplete()) collector.notifyAll();
+ } else {
+ if (! (rmsg instanceof RpcMessage.NoRpcChannelReply) )
+ callback.leftOver(rmsg.message, sender);
+ }
+ }//synchronized
+ }//end if
+ } else{
+ Serializable reply = callback.replyRequest(rmsg.message,sender);
+ rmsg.reply = true;
+ rmsg.message = reply;
+ try {
+ channel.send(new Member[] {sender}, rmsg,0);
+ }catch ( Exception x ) {
+ log.error("Unable to send back reply in RpcChannel.",x);
+ }
+ }//end if
+ }
+
+ public void breakdown() {
+ channel.removeChannelListener(this);
+ }
+
+ public void finalize() {
+ breakdown();
+ }
+
+ public boolean accept(Serializable msg, Member sender) {
+ if ( msg instanceof RpcMessage ) {
+ RpcMessage rmsg = (RpcMessage)msg;
+ return Arrays.equals(rmsg.rpcId,rpcId);
+ }else return false;
+ }
+
+ public Channel getChannel() {
+ return channel;
+ }
+
+ public RpcCallback getCallback() {
+ return callback;
+ }
+
+ public byte[] getRpcId() {
+ return rpcId;
+ }
+
+ public void setChannel(Channel channel) {
+ this.channel = channel;
+ }
+
+ public void setCallback(RpcCallback callback) {
+ this.callback = callback;
+ }
+
+ public void setRpcId(byte[] rpcId) {
+ this.rpcId = rpcId;
+ }
+
+
+
+ /**
+ *
+ * Class that holds all response.
+ * @author not attributable
+ * @version 1.0
+ */
+ public static class RpcCollector {
+ public ArrayList responses = new ArrayList();
+ public RpcCollectorKey key;
+ public int options;
+ public int destcnt;
+ public long timeout;
+
+ public RpcCollector(RpcCollectorKey key, int options, int destcnt, long timeout) {
+ this.key = key;
+ this.options = options;
+ this.destcnt = destcnt;
+ this.timeout = timeout;
+ }
+
+ public void addResponse(Serializable message, Member sender){
+ Response resp = new Response(sender,message);
+ responses.add(resp);
+ }
+
+ public boolean isComplete() {
+ if ( destcnt <= 0 ) return true;
+ switch (options) {
+ case ALL_REPLY:
+ return destcnt == responses.size();
+ case MAJORITY_REPLY:
+ {
+ float perc = ((float)responses.size()) / ((float)destcnt);
+ return perc >= 0.50f;
+ }
+ case FIRST_REPLY:
+ return responses.size()>0;
+ default:
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if ( o instanceof RpcCollector ) {
+ RpcCollector r = (RpcCollector)o;
+ return r.key.equals(this.key);
+ } else return false;
+ }
+
+ public Response[] getResponses() {
+ return (Response[])responses.toArray(new Response[responses.size()]);
+ }
+ }
+
+ public static class RpcCollectorKey {
+ byte[] id;
+ public RpcCollectorKey(byte[] id) {
+ this.id = id;
+ }
+
+ public int hashCode() {
+ return id[0]+id[1]+id[2]+id[3];
+ }
+
+ public boolean equals(Object o) {
+ if ( o instanceof RpcCollectorKey ) {
+ RpcCollectorKey r = (RpcCollectorKey)o;
+ return Arrays.equals(id,r.id);
+ } else return false;
+ }
+
+ }
+
+ protected static String bToS(byte[] data) {
+ StringBuffer buf = new StringBuffer(4*16);
+ buf.append("{");
+ for (int i=0; data!=null && i<data.length; i++ ) buf.append(String.valueOf(data[i])).append(" ");
+ buf.append("}");
+ return buf.toString();
+ }
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.group;\r
-\r
-import java.io.ObjectInput;\r
-import java.io.Serializable;\r
-import java.io.Externalizable;\r
-import java.io.IOException;\r
-import java.io.ObjectOutput;\r
-import org.apache.catalina.tribes.util.Arrays;\r
-\r
-/**\r
- * <p>Title: </p>\r
- *\r
- * <p>Description: </p>\r
- *\r
- * <p>Copyright: Copyright (c) 2005</p>\r
- *\r
- * <p>Company: </p>\r
- *\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-public class RpcMessage implements Externalizable {\r
-\r
- protected Serializable message;\r
- protected byte[] uuid;\r
- protected byte[] rpcId;\r
- protected boolean reply = false;\r
-\r
- public RpcMessage() {\r
- //for serialization\r
- }\r
-\r
- public RpcMessage(byte[] rpcId, byte[] uuid, Serializable message) {\r
- this.rpcId = rpcId;\r
- this.uuid = uuid;\r
- this.message = message;\r
- }\r
-\r
- public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {\r
- reply = in.readBoolean();\r
- int length = in.readInt();\r
- uuid = new byte[length];\r
- in.read(uuid, 0, length);\r
- length = in.readInt();\r
- rpcId = new byte[length];\r
- in.read(rpcId, 0, length);\r
- message = (Serializable)in.readObject();\r
- }\r
-\r
- public void writeExternal(ObjectOutput out) throws IOException {\r
- out.writeBoolean(reply);\r
- out.writeInt(uuid.length);\r
- out.write(uuid, 0, uuid.length);\r
- out.writeInt(rpcId.length);\r
- out.write(rpcId, 0, rpcId.length);\r
- out.writeObject(message);\r
- }\r
- \r
- public String toString() {\r
- StringBuffer buf = new StringBuffer("RpcMessage[");\r
- buf.append(super.toString());\r
- buf.append("] rpcId=");\r
- buf.append(Arrays.toString(rpcId));\r
- buf.append("; uuid=");\r
- buf.append(Arrays.toString(uuid));\r
- buf.append("; msg=");\r
- buf.append(message);\r
- return buf.toString();\r
- }\r
- \r
- public static class NoRpcChannelReply extends RpcMessage {\r
- public NoRpcChannelReply() {\r
- \r
- }\r
-\r
- public NoRpcChannelReply(byte[] rpcid, byte[] uuid) {\r
- super(rpcid,uuid,null);\r
- reply = true;\r
- }\r
-\r
- public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {\r
- reply = true;\r
- int length = in.readInt();\r
- uuid = new byte[length];\r
- in.read(uuid, 0, length);\r
- length = in.readInt();\r
- rpcId = new byte[length];\r
- in.read(rpcId, 0, length);\r
- }\r
-\r
- public void writeExternal(ObjectOutput out) throws IOException {\r
- out.writeInt(uuid.length);\r
- out.write(uuid, 0, uuid.length);\r
- out.writeInt(rpcId.length);\r
- out.write(rpcId, 0, rpcId.length);\r
- }\r
- } \r
-\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.group;
+
+import java.io.ObjectInput;
+import java.io.Serializable;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectOutput;
+import org.apache.catalina.tribes.util.Arrays;
+
+/**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2005</p>
+ *
+ * <p>Company: </p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public class RpcMessage implements Externalizable {
+
+ protected Serializable message;
+ protected byte[] uuid;
+ protected byte[] rpcId;
+ protected boolean reply = false;
+
+ public RpcMessage() {
+ //for serialization
+ }
+
+ public RpcMessage(byte[] rpcId, byte[] uuid, Serializable message) {
+ this.rpcId = rpcId;
+ this.uuid = uuid;
+ this.message = message;
+ }
+
+ public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {
+ reply = in.readBoolean();
+ int length = in.readInt();
+ uuid = new byte[length];
+ in.read(uuid, 0, length);
+ length = in.readInt();
+ rpcId = new byte[length];
+ in.read(rpcId, 0, length);
+ message = (Serializable)in.readObject();
+ }
+
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeBoolean(reply);
+ out.writeInt(uuid.length);
+ out.write(uuid, 0, uuid.length);
+ out.writeInt(rpcId.length);
+ out.write(rpcId, 0, rpcId.length);
+ out.writeObject(message);
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer("RpcMessage[");
+ buf.append(super.toString());
+ buf.append("] rpcId=");
+ buf.append(Arrays.toString(rpcId));
+ buf.append("; uuid=");
+ buf.append(Arrays.toString(uuid));
+ buf.append("; msg=");
+ buf.append(message);
+ return buf.toString();
+ }
+
+ public static class NoRpcChannelReply extends RpcMessage {
+ public NoRpcChannelReply() {
+
+ }
+
+ public NoRpcChannelReply(byte[] rpcid, byte[] uuid) {
+ super(rpcid,uuid,null);
+ reply = true;
+ }
+
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ reply = true;
+ int length = in.readInt();
+ uuid = new byte[length];
+ in.read(uuid, 0, length);
+ length = in.readInt();
+ rpcId = new byte[length];
+ in.read(rpcId, 0, length);
+ }
+
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeInt(uuid.length);
+ out.write(uuid, 0, uuid.length);
+ out.writeInt(rpcId.length);
+ out.write(rpcId, 0, rpcId.length);
+ }
+ }
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.membership.MemberImpl;\r
-import org.apache.catalina.tribes.membership.Membership;\r
-import java.util.Arrays;\r
-\r
-/**\r
- * <p>Title: Member domain filter interceptor </p>\r
- *\r
- * <p>Description: Filters membership based on domain.\r
- * </p>\r
- *\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class DomainFilterInterceptor extends ChannelInterceptorBase {\r
-\r
- private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( DomainFilterInterceptor.class );\r
-\r
- protected Membership membership = null;\r
- \r
- protected byte[] domain = new byte[0];\r
-\r
- public void messageReceived(ChannelMessage msg) {\r
- //should we filter incoming based on domain?\r
- super.messageReceived(msg);\r
- }//messageReceived\r
-\r
-\r
- public void memberAdded(Member member) {\r
- if ( membership == null ) setupMembership();\r
- boolean notify = false;\r
- synchronized (membership) {\r
- notify = Arrays.equals(domain,member.getDomain());\r
- if ( notify ) notify = membership.memberAlive((MemberImpl)member);\r
- }\r
- if ( notify ) super.memberAdded(member);\r
- }\r
-\r
- public void memberDisappeared(Member member) {\r
- if ( membership == null ) setupMembership();\r
- boolean notify = false;\r
- synchronized (membership) {\r
- notify = Arrays.equals(domain,member.getDomain());\r
- membership.removeMember((MemberImpl)member);\r
- }\r
- if ( notify ) super.memberDisappeared(member);\r
- }\r
-\r
- public boolean hasMembers() {\r
- if ( membership == null ) setupMembership();\r
- return membership.hasMembers();\r
- }\r
-\r
- public Member[] getMembers() {\r
- if ( membership == null ) setupMembership();\r
- return membership.getMembers();\r
- }\r
-\r
- public Member getMember(Member mbr) {\r
- if ( membership == null ) setupMembership();\r
- return membership.getMember(mbr);\r
- }\r
-\r
- public Member getLocalMember(boolean incAlive) {\r
- return super.getLocalMember(incAlive);\r
- }\r
-\r
-\r
- protected synchronized void setupMembership() {\r
- if ( membership == null ) {\r
- membership = new Membership((MemberImpl)super.getLocalMember(true));\r
- }\r
-\r
- }\r
-\r
- public byte[] getDomain() {\r
- return domain;\r
- }\r
-\r
- public void setDomain(byte[] domain) {\r
- this.domain = domain;\r
- }\r
-}\r
+/*
+ * 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
+ */
+package org.apache.catalina.tribes.group.interceptors;
+
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.membership.MemberImpl;
+import org.apache.catalina.tribes.membership.Membership;
+import java.util.Arrays;
+
+/**
+ * <p>Title: Member domain filter interceptor </p>
+ *
+ * <p>Description: Filters membership based on domain.
+ * </p>
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class DomainFilterInterceptor extends ChannelInterceptorBase {
+
+ private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( DomainFilterInterceptor.class );
+
+ protected Membership membership = null;
+
+ protected byte[] domain = new byte[0];
+
+ public void messageReceived(ChannelMessage msg) {
+ //should we filter incoming based on domain?
+ super.messageReceived(msg);
+ }//messageReceived
+
+
+ public void memberAdded(Member member) {
+ if ( membership == null ) setupMembership();
+ boolean notify = false;
+ synchronized (membership) {
+ notify = Arrays.equals(domain,member.getDomain());
+ if ( notify ) notify = membership.memberAlive((MemberImpl)member);
+ }
+ if ( notify ) super.memberAdded(member);
+ }
+
+ public void memberDisappeared(Member member) {
+ if ( membership == null ) setupMembership();
+ boolean notify = false;
+ synchronized (membership) {
+ notify = Arrays.equals(domain,member.getDomain());
+ membership.removeMember((MemberImpl)member);
+ }
+ if ( notify ) super.memberDisappeared(member);
+ }
+
+ public boolean hasMembers() {
+ if ( membership == null ) setupMembership();
+ return membership.hasMembers();
+ }
+
+ public Member[] getMembers() {
+ if ( membership == null ) setupMembership();
+ return membership.getMembers();
+ }
+
+ public Member getMember(Member mbr) {
+ if ( membership == null ) setupMembership();
+ return membership.getMember(mbr);
+ }
+
+ public Member getLocalMember(boolean incAlive) {
+ return super.getLocalMember(incAlive);
+ }
+
+
+ protected synchronized void setupMembership() {
+ if ( membership == null ) {
+ membership = new Membership((MemberImpl)super.getLocalMember(true));
+ }
+
+ }
+
+ public byte[] getDomain() {
+ return domain;
+ }
+
+ public void setDomain(byte[] domain) {
+ this.domain = domain;
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-import java.util.Set;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-\r
-/**\r
- *\r
- * The fragmentation interceptor splits up large messages into smaller messages and assembles them on the other end.\r
- * This is very useful when you don't want large messages hogging the sending sockets\r
- * and smaller messages can make it through.\r
- * \r
- * <br><b>Configuration Options</b><br>\r
- * OrderInteceptor.expire=<milliseconds> - how long do we keep the fragments in memory and wait for the rest to arrive<b>default=60,000ms -> 60seconds</b>\r
- * This setting is useful to avoid OutOfMemoryErrors<br>\r
- * OrderInteceptor.maxSize=<max message size> - message size in bytes <b>default=1024*100 (around a tenth of a MB)</b><br>\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class FragmentationInterceptor extends ChannelInterceptorBase {\r
- private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( FragmentationInterceptor.class );\r
- \r
- protected HashMap fragpieces = new HashMap();\r
- private int maxSize = 1024*100;\r
- private long expire = 1000 * 60; //one minute expiration\r
- protected boolean deepclone = true;\r
-\r
-\r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- int size = msg.getMessage().getLength();\r
- boolean frag = (size>maxSize) && okToProcess(msg.getOptions());\r
- if ( frag ) {\r
- frag(destination, msg, payload);\r
- } else {\r
- msg.getMessage().append(frag);\r
- super.sendMessage(destination, msg, payload);\r
- }\r
- }\r
- \r
- public void messageReceived(ChannelMessage msg) {\r
- boolean isFrag = XByteBuffer.toBoolean(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-1);\r
- msg.getMessage().trim(1);\r
- if ( isFrag ) {\r
- defrag(msg);\r
- } else {\r
- super.messageReceived(msg);\r
- }\r
- }\r
-\r
- \r
- public FragCollection getFragCollection(FragKey key, ChannelMessage msg) {\r
- FragCollection coll = (FragCollection)fragpieces.get(key);\r
- if ( coll == null ) {\r
- synchronized (fragpieces) {\r
- coll = (FragCollection)fragpieces.get(key);\r
- if ( coll == null ) {\r
- coll = new FragCollection(msg);\r
- fragpieces.put(key, coll);\r
- }\r
- }\r
- } \r
- return coll;\r
- }\r
- \r
- public void removeFragCollection(FragKey key) {\r
- fragpieces.remove(key);\r
- }\r
- \r
- public void defrag(ChannelMessage msg ) { \r
- FragKey key = new FragKey(msg.getUniqueId());\r
- FragCollection coll = getFragCollection(key,msg);\r
- coll.addMessage((ChannelMessage)msg.deepclone());\r
-\r
- if ( coll.complete() ) {\r
- removeFragCollection(key);\r
- ChannelMessage complete = coll.assemble();\r
- super.messageReceived(complete);\r
- \r
- }\r
- }\r
-\r
- public void frag(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- int size = msg.getMessage().getLength();\r
-\r
- int count = ((size / maxSize )+(size%maxSize==0?0:1));\r
- ChannelMessage[] messages = new ChannelMessage[count];\r
- int remaining = size;\r
- for ( int i=0; i<count; i++ ) {\r
- ChannelMessage tmp = (ChannelMessage)msg.clone();\r
- int offset = (i*maxSize);\r
- int length = Math.min(remaining,maxSize);\r
- tmp.getMessage().clear();\r
- tmp.getMessage().append(msg.getMessage().getBytesDirect(),offset,length);\r
- //add the msg nr\r
- //tmp.getMessage().append(XByteBuffer.toBytes(i),0,4);\r
- tmp.getMessage().append(i);\r
- //add the total nr of messages\r
- //tmp.getMessage().append(XByteBuffer.toBytes(count),0,4);\r
- tmp.getMessage().append(count);\r
- //add true as the frag flag\r
- //byte[] flag = XByteBuffer.toBytes(true);\r
- //tmp.getMessage().append(flag,0,flag.length);\r
- tmp.getMessage().append(true);\r
- messages[i] = tmp;\r
- remaining -= length;\r
- \r
- }\r
- for ( int i=0; i<messages.length; i++ ) {\r
- super.sendMessage(destination,messages[i],payload);\r
- }\r
- }\r
- \r
- public void heartbeat() {\r
- try {\r
- Set set = fragpieces.keySet(); \r
- Object[] keys = set.toArray();\r
- for ( int i=0; i<keys.length; i++ ) {\r
- FragKey key = (FragKey)keys[i];\r
- if ( key != null && key.expired(getExpire()) ) \r
- removeFragCollection(key);\r
- }\r
- }catch ( Exception x ) {\r
- if ( log.isErrorEnabled() ) {\r
- log.error("Unable to perform heartbeat clean up in the frag interceptor",x);\r
- }\r
- }\r
- super.heartbeat();\r
- }\r
-\r
- \r
-\r
- public int getMaxSize() {\r
- return maxSize;\r
- }\r
-\r
- public long getExpire() {\r
- return expire;\r
- }\r
-\r
- public void setMaxSize(int maxSize) {\r
- this.maxSize = maxSize;\r
- }\r
-\r
- public void setExpire(long expire) {\r
- this.expire = expire;\r
- }\r
-\r
- public static class FragCollection {\r
- private long received = System.currentTimeMillis();\r
- private ChannelMessage msg;\r
- private XByteBuffer[] frags;\r
- public FragCollection(ChannelMessage msg) {\r
- //get the total messages\r
- int count = XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);\r
- frags = new XByteBuffer[count];\r
- this.msg = msg;\r
- }\r
- \r
- public void addMessage(ChannelMessage msg) {\r
- //remove the total messages\r
- msg.getMessage().trim(4);\r
- //get the msg nr\r
- int nr = XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);\r
- //remove the msg nr\r
- msg.getMessage().trim(4);\r
- frags[nr] = msg.getMessage();\r
- \r
- }\r
- \r
- public boolean complete() {\r
- boolean result = true;\r
- for ( int i=0; (i<frags.length) && (result); i++ ) result = (frags[i] != null);\r
- return result;\r
- }\r
- \r
- public ChannelMessage assemble() {\r
- if ( !complete() ) throw new IllegalStateException("Fragments are missing.");\r
- int buffersize = 0;\r
- for (int i=0; i<frags.length; i++ ) buffersize += frags[i].getLength();\r
- XByteBuffer buf = new XByteBuffer(buffersize,false);\r
- msg.setMessage(buf);\r
- for ( int i=0; i<frags.length; i++ ) {\r
- msg.getMessage().append(frags[i].getBytesDirect(),0,frags[i].getLength());\r
- }\r
- return msg;\r
- }\r
- \r
- public boolean expired(long expire) {\r
- return (System.currentTimeMillis()-received)>expire;\r
- }\r
-\r
- \r
- \r
- }\r
- \r
- public static class FragKey {\r
- private byte[] uniqueId;\r
- private long received = System.currentTimeMillis();\r
- public FragKey(byte[] id ) {\r
- this.uniqueId = id;\r
- }\r
- public int hashCode() {\r
- return XByteBuffer.toInt(uniqueId,0);\r
- }\r
- \r
- public boolean equals(Object o ) {\r
- if ( o instanceof FragKey ) {\r
- return Arrays.equals(uniqueId,((FragKey)o).uniqueId);\r
- } else return false;\r
-\r
- }\r
- \r
- public boolean expired(long expire) {\r
- return (System.currentTimeMillis()-received)>expire;\r
- }\r
-\r
- }\r
- \r
+/*
+ * 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
+ */
+
+package org.apache.catalina.tribes.group.interceptors;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.io.XByteBuffer;
+
+/**
+ *
+ * The fragmentation interceptor splits up large messages into smaller messages and assembles them on the other end.
+ * This is very useful when you don't want large messages hogging the sending sockets
+ * and smaller messages can make it through.
+ *
+ * <br><b>Configuration Options</b><br>
+ * OrderInteceptor.expire=<milliseconds> - how long do we keep the fragments in memory and wait for the rest to arrive<b>default=60,000ms -> 60seconds</b>
+ * This setting is useful to avoid OutOfMemoryErrors<br>
+ * OrderInteceptor.maxSize=<max message size> - message size in bytes <b>default=1024*100 (around a tenth of a MB)</b><br>
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class FragmentationInterceptor extends ChannelInterceptorBase {
+ private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( FragmentationInterceptor.class );
+
+ protected HashMap fragpieces = new HashMap();
+ private int maxSize = 1024*100;
+ private long expire = 1000 * 60; //one minute expiration
+ protected boolean deepclone = true;
+
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ int size = msg.getMessage().getLength();
+ boolean frag = (size>maxSize) && okToProcess(msg.getOptions());
+ if ( frag ) {
+ frag(destination, msg, payload);
+ } else {
+ msg.getMessage().append(frag);
+ super.sendMessage(destination, msg, payload);
+ }
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ boolean isFrag = XByteBuffer.toBoolean(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-1);
+ msg.getMessage().trim(1);
+ if ( isFrag ) {
+ defrag(msg);
+ } else {
+ super.messageReceived(msg);
+ }
+ }
+
+
+ public FragCollection getFragCollection(FragKey key, ChannelMessage msg) {
+ FragCollection coll = (FragCollection)fragpieces.get(key);
+ if ( coll == null ) {
+ synchronized (fragpieces) {
+ coll = (FragCollection)fragpieces.get(key);
+ if ( coll == null ) {
+ coll = new FragCollection(msg);
+ fragpieces.put(key, coll);
+ }
+ }
+ }
+ return coll;
+ }
+
+ public void removeFragCollection(FragKey key) {
+ fragpieces.remove(key);
+ }
+
+ public void defrag(ChannelMessage msg ) {
+ FragKey key = new FragKey(msg.getUniqueId());
+ FragCollection coll = getFragCollection(key,msg);
+ coll.addMessage((ChannelMessage)msg.deepclone());
+
+ if ( coll.complete() ) {
+ removeFragCollection(key);
+ ChannelMessage complete = coll.assemble();
+ super.messageReceived(complete);
+
+ }
+ }
+
+ public void frag(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ int size = msg.getMessage().getLength();
+
+ int count = ((size / maxSize )+(size%maxSize==0?0:1));
+ ChannelMessage[] messages = new ChannelMessage[count];
+ int remaining = size;
+ for ( int i=0; i<count; i++ ) {
+ ChannelMessage tmp = (ChannelMessage)msg.clone();
+ int offset = (i*maxSize);
+ int length = Math.min(remaining,maxSize);
+ tmp.getMessage().clear();
+ tmp.getMessage().append(msg.getMessage().getBytesDirect(),offset,length);
+ //add the msg nr
+ //tmp.getMessage().append(XByteBuffer.toBytes(i),0,4);
+ tmp.getMessage().append(i);
+ //add the total nr of messages
+ //tmp.getMessage().append(XByteBuffer.toBytes(count),0,4);
+ tmp.getMessage().append(count);
+ //add true as the frag flag
+ //byte[] flag = XByteBuffer.toBytes(true);
+ //tmp.getMessage().append(flag,0,flag.length);
+ tmp.getMessage().append(true);
+ messages[i] = tmp;
+ remaining -= length;
+
+ }
+ for ( int i=0; i<messages.length; i++ ) {
+ super.sendMessage(destination,messages[i],payload);
+ }
+ }
+
+ public void heartbeat() {
+ try {
+ Set set = fragpieces.keySet();
+ Object[] keys = set.toArray();
+ for ( int i=0; i<keys.length; i++ ) {
+ FragKey key = (FragKey)keys[i];
+ if ( key != null && key.expired(getExpire()) )
+ removeFragCollection(key);
+ }
+ }catch ( Exception x ) {
+ if ( log.isErrorEnabled() ) {
+ log.error("Unable to perform heartbeat clean up in the frag interceptor",x);
+ }
+ }
+ super.heartbeat();
+ }
+
+
+
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+ public long getExpire() {
+ return expire;
+ }
+
+ public void setMaxSize(int maxSize) {
+ this.maxSize = maxSize;
+ }
+
+ public void setExpire(long expire) {
+ this.expire = expire;
+ }
+
+ public static class FragCollection {
+ private long received = System.currentTimeMillis();
+ private ChannelMessage msg;
+ private XByteBuffer[] frags;
+ public FragCollection(ChannelMessage msg) {
+ //get the total messages
+ int count = XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);
+ frags = new XByteBuffer[count];
+ this.msg = msg;
+ }
+
+ public void addMessage(ChannelMessage msg) {
+ //remove the total messages
+ msg.getMessage().trim(4);
+ //get the msg nr
+ int nr = XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);
+ //remove the msg nr
+ msg.getMessage().trim(4);
+ frags[nr] = msg.getMessage();
+
+ }
+
+ public boolean complete() {
+ boolean result = true;
+ for ( int i=0; (i<frags.length) && (result); i++ ) result = (frags[i] != null);
+ return result;
+ }
+
+ public ChannelMessage assemble() {
+ if ( !complete() ) throw new IllegalStateException("Fragments are missing.");
+ int buffersize = 0;
+ for (int i=0; i<frags.length; i++ ) buffersize += frags[i].getLength();
+ XByteBuffer buf = new XByteBuffer(buffersize,false);
+ msg.setMessage(buf);
+ for ( int i=0; i<frags.length; i++ ) {
+ msg.getMessage().append(frags[i].getBytesDirect(),0,frags[i].getLength());
+ }
+ return msg;
+ }
+
+ public boolean expired(long expire) {
+ return (System.currentTimeMillis()-received)>expire;
+ }
+
+
+
+ }
+
+ public static class FragKey {
+ private byte[] uniqueId;
+ private long received = System.currentTimeMillis();
+ public FragKey(byte[] id ) {
+ this.uniqueId = id;
+ }
+ public int hashCode() {
+ return XByteBuffer.toInt(uniqueId,0);
+ }
+
+ public boolean equals(Object o ) {
+ if ( o instanceof FragKey ) {
+ return Arrays.equals(uniqueId,((FragKey)o).uniqueId);
+ } else return false;
+
+ }
+
+ public boolean expired(long expire) {
+ return (System.currentTimeMillis()-received)>expire;
+ }
+
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import java.io.ByteArrayInputStream;\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.IOException;\r
-import java.util.Arrays;\r
-import java.util.zip.GZIPInputStream;\r
-import java.util.zip.GZIPOutputStream;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-\r
-\r
-\r
-/**\r
- *\r
- *\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class GzipInterceptor extends ChannelInterceptorBase {\r
- public static final int DEFAULT_BUFFER_SIZE = 2048;\r
- \r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- try {\r
- byte[] data = compress(msg.getMessage().getBytes());\r
- msg.getMessage().trim(msg.getMessage().getLength());\r
- msg.getMessage().append(data,0,data.length);\r
- getNext().sendMessage(destination, msg, payload);\r
- } catch ( IOException x ) {\r
- log.error("Unable to compress byte contents");\r
- throw new ChannelException(x);\r
- }\r
- }\r
-\r
- public void messageReceived(ChannelMessage msg) {\r
- try {\r
- byte[] data = decompress(msg.getMessage().getBytes());\r
- msg.getMessage().trim(msg.getMessage().getLength());\r
- msg.getMessage().append(data,0,data.length);\r
- getPrevious().messageReceived(msg);\r
- } catch ( IOException x ) {\r
- log.error("Unable to decompress byte contents",x);\r
- }\r
- }\r
- \r
- public static byte[] compress(byte[] data) throws IOException {\r
- ByteArrayOutputStream bout = new ByteArrayOutputStream();\r
- GZIPOutputStream gout = new GZIPOutputStream(bout);\r
- gout.write(data);\r
- gout.flush();\r
- gout.close();\r
- return bout.toByteArray();\r
- }\r
- \r
- /**\r
- * @todo Fix to create an automatically growing buffer.\r
- * @param data byte[]\r
- * @return byte[]\r
- * @throws IOException\r
- */\r
- public static byte[] decompress(byte[] data) throws IOException {\r
- ByteArrayInputStream bin = new ByteArrayInputStream(data);\r
- GZIPInputStream gin = new GZIPInputStream(bin);\r
- byte[] tmp = new byte[DEFAULT_BUFFER_SIZE];\r
- int length = gin.read(tmp);\r
- byte[] result = new byte[length];\r
- System.arraycopy(tmp,0,result,0,length);\r
- return result;\r
- }\r
- \r
- public static void main(String[] arg) throws Exception {\r
- byte[] data = new byte[1024];\r
- Arrays.fill(data,(byte)1);\r
- byte[] compress = compress(data);\r
- byte[] decompress = decompress(compress);\r
- System.out.println("Debug test");\r
- \r
- }\r
- \r
-}\r
+/*
+ * 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
+ */
+
+package org.apache.catalina.tribes.group.interceptors;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+
+
+
+/**
+ *
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class GzipInterceptor extends ChannelInterceptorBase {
+ public static final int DEFAULT_BUFFER_SIZE = 2048;
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ try {
+ byte[] data = compress(msg.getMessage().getBytes());
+ msg.getMessage().trim(msg.getMessage().getLength());
+ msg.getMessage().append(data,0,data.length);
+ getNext().sendMessage(destination, msg, payload);
+ } catch ( IOException x ) {
+ log.error("Unable to compress byte contents");
+ throw new ChannelException(x);
+ }
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ try {
+ byte[] data = decompress(msg.getMessage().getBytes());
+ msg.getMessage().trim(msg.getMessage().getLength());
+ msg.getMessage().append(data,0,data.length);
+ getPrevious().messageReceived(msg);
+ } catch ( IOException x ) {
+ log.error("Unable to decompress byte contents",x);
+ }
+ }
+
+ public static byte[] compress(byte[] data) throws IOException {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ GZIPOutputStream gout = new GZIPOutputStream(bout);
+ gout.write(data);
+ gout.flush();
+ gout.close();
+ return bout.toByteArray();
+ }
+
+ /**
+ * @todo Fix to create an automatically growing buffer.
+ * @param data byte[]
+ * @return byte[]
+ * @throws IOException
+ */
+ public static byte[] decompress(byte[] data) throws IOException {
+ ByteArrayInputStream bin = new ByteArrayInputStream(data);
+ GZIPInputStream gin = new GZIPInputStream(bin);
+ byte[] tmp = new byte[DEFAULT_BUFFER_SIZE];
+ int length = gin.read(tmp);
+ byte[] result = new byte[length];
+ System.arraycopy(tmp,0,result,0,length);
+ return result;
+ }
+
+ public static void main(String[] arg) throws Exception {
+ byte[] data = new byte[1024];
+ Arrays.fill(data,(byte)1);
+ byte[] compress = compress(data);
+ byte[] decompress = decompress(compress);
+ System.out.println("Debug test");
+
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import java.util.concurrent.LinkedBlockingQueue;\r
-import java.util.concurrent.ThreadPoolExecutor;\r
-import java.util.concurrent.atomic.AtomicLong;\r
-\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-import org.apache.catalina.tribes.transport.bio.util.LinkObject;\r
-import java.util.concurrent.TimeUnit;\r
-\r
-/**\r
- * \r
- * Same implementation as the MessageDispatchInterceptor\r
- * except is ues an atomic long for the currentSize calculation\r
- * and uses a thread pool for message sending.\r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-\r
-public class MessageDispatch15Interceptor extends MessageDispatchInterceptor {\r
-\r
- protected AtomicLong currentSize = new AtomicLong(0);\r
- protected ThreadPoolExecutor executor = null;\r
- protected int maxThreads = 10;\r
- protected int maxSpareThreads = 2;\r
- protected long keepAliveTime = 5000;\r
- protected LinkedBlockingQueue<Runnable> runnablequeue = new LinkedBlockingQueue<Runnable>();\r
-\r
- public long getCurrentSize() {\r
- return currentSize.get();\r
- }\r
-\r
- public long addAndGetCurrentSize(long inc) {\r
- return currentSize.addAndGet(inc);\r
- }\r
-\r
- public long setAndGetCurrentSize(long value) {\r
- currentSize.set(value);\r
- return value;\r
- }\r
- \r
- public boolean addToQueue(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {\r
- final LinkObject obj = new LinkObject(msg,destination,payload);\r
- Runnable r = new Runnable() {\r
- public void run() {\r
- sendAsyncData(obj);\r
- }\r
- };\r
- executor.execute(r);\r
- return true;\r
- }\r
-\r
- public LinkObject removeFromQueue() {\r
- return null; //not used, thread pool contains its own queue.\r
- }\r
-\r
- public void startQueue() {\r
- if ( run ) return;\r
- executor = new ThreadPoolExecutor(maxSpareThreads,maxThreads,keepAliveTime,TimeUnit.MILLISECONDS,runnablequeue);\r
- run = true;\r
- }\r
-\r
- public void stopQueue() {\r
- run = false;\r
- executor.shutdownNow();\r
- setAndGetCurrentSize(0);\r
- runnablequeue.clear();\r
- }\r
-\r
- public long getKeepAliveTime() {\r
- return keepAliveTime;\r
- }\r
-\r
- public int getMaxSpareThreads() {\r
- return maxSpareThreads;\r
- }\r
-\r
- public int getMaxThreads() {\r
- return maxThreads;\r
- }\r
-\r
- public void setKeepAliveTime(long keepAliveTime) {\r
- this.keepAliveTime = keepAliveTime;\r
- }\r
-\r
- public void setMaxSpareThreads(int maxSpareThreads) {\r
- this.maxSpareThreads = maxSpareThreads;\r
- }\r
-\r
- public void setMaxThreads(int maxThreads) {\r
- this.maxThreads = maxThreads;\r
- }\r
-\r
+/*
+ * 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
+ */
+package org.apache.catalina.tribes.group.interceptors;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.transport.bio.util.LinkObject;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *
+ * Same implementation as the MessageDispatchInterceptor
+ * except is ues an atomic long for the currentSize calculation
+ * and uses a thread pool for message sending.
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+
+public class MessageDispatch15Interceptor extends MessageDispatchInterceptor {
+
+ protected AtomicLong currentSize = new AtomicLong(0);
+ protected ThreadPoolExecutor executor = null;
+ protected int maxThreads = 10;
+ protected int maxSpareThreads = 2;
+ protected long keepAliveTime = 5000;
+ protected LinkedBlockingQueue<Runnable> runnablequeue = new LinkedBlockingQueue<Runnable>();
+
+ public long getCurrentSize() {
+ return currentSize.get();
+ }
+
+ public long addAndGetCurrentSize(long inc) {
+ return currentSize.addAndGet(inc);
+ }
+
+ public long setAndGetCurrentSize(long value) {
+ currentSize.set(value);
+ return value;
+ }
+
+ public boolean addToQueue(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {
+ final LinkObject obj = new LinkObject(msg,destination,payload);
+ Runnable r = new Runnable() {
+ public void run() {
+ sendAsyncData(obj);
+ }
+ };
+ executor.execute(r);
+ return true;
+ }
+
+ public LinkObject removeFromQueue() {
+ return null; //not used, thread pool contains its own queue.
+ }
+
+ public void startQueue() {
+ if ( run ) return;
+ executor = new ThreadPoolExecutor(maxSpareThreads,maxThreads,keepAliveTime,TimeUnit.MILLISECONDS,runnablequeue);
+ run = true;
+ }
+
+ public void stopQueue() {
+ run = false;
+ executor.shutdownNow();
+ setAndGetCurrentSize(0);
+ runnablequeue.clear();
+ }
+
+ public long getKeepAliveTime() {
+ return keepAliveTime;
+ }
+
+ public int getMaxSpareThreads() {
+ return maxSpareThreads;
+ }
+
+ public int getMaxThreads() {
+ return maxThreads;
+ }
+
+ public void setKeepAliveTime(long keepAliveTime) {
+ this.keepAliveTime = keepAliveTime;
+ }
+
+ public void setMaxSpareThreads(int maxSpareThreads) {
+ this.maxSpareThreads = maxSpareThreads;
+ }
+
+ public void setMaxThreads(int maxThreads) {
+ this.maxThreads = maxThreads;
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-import org.apache.catalina.tribes.transport.bio.util.FastQueue;\r
-import org.apache.catalina.tribes.transport.bio.util.LinkObject;\r
-import org.apache.catalina.tribes.UniqueId;\r
-\r
-/**\r
- *\r
- * The message dispatcher is a way to enable asynchronous communication\r
- * through a channel. The dispatcher will look for the <code>Channel.SEND_OPTIONS_ASYNCHRONOUS</code>\r
- * flag to be set, if it is, it will queue the message for delivery and immediately return to the sender.\r
- * \r
- * \r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class MessageDispatchInterceptor extends ChannelInterceptorBase implements Runnable {\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(MessageDispatchInterceptor.class);\r
-\r
- protected long maxQueueSize = 1024*1024*64; //64MB\r
- protected FastQueue queue = new FastQueue();\r
- protected boolean run = false;\r
- protected Thread msgDispatchThread = null;\r
- protected long currentSize = 0;\r
- protected boolean useDeepClone = true;\r
- protected boolean alwaysSend = true;\r
-\r
- public MessageDispatchInterceptor() {\r
- setOptionFlag(Channel.SEND_OPTIONS_ASYNCHRONOUS);\r
- }\r
-\r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- boolean async = (msg.getOptions() & Channel.SEND_OPTIONS_ASYNCHRONOUS) == Channel.SEND_OPTIONS_ASYNCHRONOUS;\r
- if ( async && run ) {\r
- if ( (getCurrentSize()+msg.getMessage().getLength()) > maxQueueSize ) {\r
- if ( alwaysSend ) {\r
- super.sendMessage(destination,msg,payload);\r
- return;\r
- } else {\r
- throw new ChannelException("Asynchronous queue is full, reached its limit of " + maxQueueSize +" bytes, current:" + getCurrentSize() + " bytes.");\r
- }//end if\r
- }//end if\r
- //add to queue\r
- if ( useDeepClone ) msg = (ChannelMessage)msg.deepclone();\r
- if (!addToQueue(msg, destination, payload) ) {\r
- throw new ChannelException("Unable to add the message to the async queue, queue bug?");\r
- }\r
- addAndGetCurrentSize(msg.getMessage().getLength());\r
- } else {\r
- super.sendMessage(destination, msg, payload);\r
- }\r
- }\r
- \r
- public boolean addToQueue(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {\r
- return queue.add(msg,destination,payload);\r
- }\r
- \r
- public LinkObject removeFromQueue() {\r
- return queue.remove();\r
- }\r
- \r
- public void startQueue() {\r
- msgDispatchThread = new Thread(this);\r
- msgDispatchThread.setName("MessageDispatchInterceptor.MessageDispatchThread");\r
- msgDispatchThread.setDaemon(true);\r
- msgDispatchThread.setPriority(Thread.MAX_PRIORITY);\r
- queue.setEnabled(true);\r
- run = true;\r
- msgDispatchThread.start();\r
- }\r
- \r
- public void stopQueue() {\r
- run = false;\r
- msgDispatchThread.interrupt();\r
- queue.setEnabled(false);\r
- setAndGetCurrentSize(0);\r
- }\r
- \r
- \r
- public void setOptionFlag(int flag) {\r
- if ( flag != Channel.SEND_OPTIONS_ASYNCHRONOUS ) log.warn("Warning, you are overriding the asynchronous option flag, this will disable the Channel.SEND_OPTIONS_ASYNCHRONOUS that other apps might use.");\r
- super.setOptionFlag(flag);\r
- }\r
-\r
- public void setMaxQueueSize(long maxQueueSize) {\r
- this.maxQueueSize = maxQueueSize;\r
- }\r
-\r
- public void setUseDeepClone(boolean useDeepClone) {\r
- this.useDeepClone = useDeepClone;\r
- }\r
-\r
- public long getMaxQueueSize() {\r
- return maxQueueSize;\r
- }\r
-\r
- public boolean getUseDeepClone() {\r
- return useDeepClone;\r
- }\r
- \r
- public long getCurrentSize() {\r
- return currentSize;\r
- }\r
- \r
- public synchronized long addAndGetCurrentSize(long inc) {\r
- currentSize += inc;\r
- return currentSize;\r
- }\r
- \r
- public synchronized long setAndGetCurrentSize(long value) {\r
- currentSize = value;\r
- return value;\r
- }\r
-\r
- public void start(int svc) throws ChannelException {\r
- //start the thread\r
- if (!run ) {\r
- synchronized (this) {\r
- if ( !run && ((svc & Channel.SND_TX_SEQ)==Channel.SND_TX_SEQ) ) {//only start with the sender\r
- startQueue();\r
- }//end if\r
- }//sync\r
- }//end if\r
- super.start(svc);\r
- }\r
-\r
- \r
- public void stop(int svc) throws ChannelException {\r
- //stop the thread\r
- if ( run ) {\r
- synchronized (this) {\r
- if ( run && ((svc & Channel.SND_TX_SEQ)==Channel.SND_TX_SEQ)) {\r
- stopQueue();\r
- }//end if\r
- }//sync\r
- }//end if\r
-\r
- super.stop(svc);\r
- }\r
- \r
- public void run() {\r
- while ( run ) {\r
- LinkObject link = removeFromQueue();\r
- if ( link == null ) continue; //should not happen unless we exceed wait time\r
- while ( link != null && run ) {\r
- link = sendAsyncData(link);\r
- }//while\r
- }//while\r
- }//run\r
-\r
- protected LinkObject sendAsyncData(LinkObject link) {\r
- ChannelMessage msg = link.data();\r
- Member[] destination = link.getDestination();\r
- try {\r
- super.sendMessage(destination,msg,null);\r
- try {\r
- if ( link.getHandler() != null ) link.getHandler().handleCompletion(new UniqueId(msg.getUniqueId())); \r
- } catch ( Exception ex ) {\r
- log.error("Unable to report back completed message.",ex);\r
- }\r
- } catch ( Exception x ) {\r
- ChannelException cx = null;\r
- if ( x instanceof ChannelException ) cx = (ChannelException)x;\r
- else cx = new ChannelException(x);\r
- if ( log.isDebugEnabled() ) log.debug("Error while processing async message.",x);\r
- try {\r
- if (link.getHandler() != null) link.getHandler().handleError(cx, new UniqueId(msg.getUniqueId()));\r
- } catch ( Exception ex ) {\r
- log.error("Unable to report back error message.",ex);\r
- }\r
- } finally {\r
- addAndGetCurrentSize(-msg.getMessage().getLength());\r
- link = link.next();\r
- }//try\r
- return link;\r
- }\r
-\r
-\r
-}\r
+/*
+ * 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
+ */
+
+package org.apache.catalina.tribes.group.interceptors;
+
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.transport.bio.util.FastQueue;
+import org.apache.catalina.tribes.transport.bio.util.LinkObject;
+import org.apache.catalina.tribes.UniqueId;
+
+/**
+ *
+ * The message dispatcher is a way to enable asynchronous communication
+ * through a channel. The dispatcher will look for the <code>Channel.SEND_OPTIONS_ASYNCHRONOUS</code>
+ * flag to be set, if it is, it will queue the message for delivery and immediately return to the sender.
+ *
+ *
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class MessageDispatchInterceptor extends ChannelInterceptorBase implements Runnable {
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(MessageDispatchInterceptor.class);
+
+ protected long maxQueueSize = 1024*1024*64; //64MB
+ protected FastQueue queue = new FastQueue();
+ protected boolean run = false;
+ protected Thread msgDispatchThread = null;
+ protected long currentSize = 0;
+ protected boolean useDeepClone = true;
+ protected boolean alwaysSend = true;
+
+ public MessageDispatchInterceptor() {
+ setOptionFlag(Channel.SEND_OPTIONS_ASYNCHRONOUS);
+ }
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ boolean async = (msg.getOptions() & Channel.SEND_OPTIONS_ASYNCHRONOUS) == Channel.SEND_OPTIONS_ASYNCHRONOUS;
+ if ( async && run ) {
+ if ( (getCurrentSize()+msg.getMessage().getLength()) > maxQueueSize ) {
+ if ( alwaysSend ) {
+ super.sendMessage(destination,msg,payload);
+ return;
+ } else {
+ throw new ChannelException("Asynchronous queue is full, reached its limit of " + maxQueueSize +" bytes, current:" + getCurrentSize() + " bytes.");
+ }//end if
+ }//end if
+ //add to queue
+ if ( useDeepClone ) msg = (ChannelMessage)msg.deepclone();
+ if (!addToQueue(msg, destination, payload) ) {
+ throw new ChannelException("Unable to add the message to the async queue, queue bug?");
+ }
+ addAndGetCurrentSize(msg.getMessage().getLength());
+ } else {
+ super.sendMessage(destination, msg, payload);
+ }
+ }
+
+ public boolean addToQueue(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {
+ return queue.add(msg,destination,payload);
+ }
+
+ public LinkObject removeFromQueue() {
+ return queue.remove();
+ }
+
+ public void startQueue() {
+ msgDispatchThread = new Thread(this);
+ msgDispatchThread.setName("MessageDispatchInterceptor.MessageDispatchThread");
+ msgDispatchThread.setDaemon(true);
+ msgDispatchThread.setPriority(Thread.MAX_PRIORITY);
+ queue.setEnabled(true);
+ run = true;
+ msgDispatchThread.start();
+ }
+
+ public void stopQueue() {
+ run = false;
+ msgDispatchThread.interrupt();
+ queue.setEnabled(false);
+ setAndGetCurrentSize(0);
+ }
+
+
+ public void setOptionFlag(int flag) {
+ if ( flag != Channel.SEND_OPTIONS_ASYNCHRONOUS ) log.warn("Warning, you are overriding the asynchronous option flag, this will disable the Channel.SEND_OPTIONS_ASYNCHRONOUS that other apps might use.");
+ super.setOptionFlag(flag);
+ }
+
+ public void setMaxQueueSize(long maxQueueSize) {
+ this.maxQueueSize = maxQueueSize;
+ }
+
+ public void setUseDeepClone(boolean useDeepClone) {
+ this.useDeepClone = useDeepClone;
+ }
+
+ public long getMaxQueueSize() {
+ return maxQueueSize;
+ }
+
+ public boolean getUseDeepClone() {
+ return useDeepClone;
+ }
+
+ public long getCurrentSize() {
+ return currentSize;
+ }
+
+ public synchronized long addAndGetCurrentSize(long inc) {
+ currentSize += inc;
+ return currentSize;
+ }
+
+ public synchronized long setAndGetCurrentSize(long value) {
+ currentSize = value;
+ return value;
+ }
+
+ public void start(int svc) throws ChannelException {
+ //start the thread
+ if (!run ) {
+ synchronized (this) {
+ if ( !run && ((svc & Channel.SND_TX_SEQ)==Channel.SND_TX_SEQ) ) {//only start with the sender
+ startQueue();
+ }//end if
+ }//sync
+ }//end if
+ super.start(svc);
+ }
+
+
+ public void stop(int svc) throws ChannelException {
+ //stop the thread
+ if ( run ) {
+ synchronized (this) {
+ if ( run && ((svc & Channel.SND_TX_SEQ)==Channel.SND_TX_SEQ)) {
+ stopQueue();
+ }//end if
+ }//sync
+ }//end if
+
+ super.stop(svc);
+ }
+
+ public void run() {
+ while ( run ) {
+ LinkObject link = removeFromQueue();
+ if ( link == null ) continue; //should not happen unless we exceed wait time
+ while ( link != null && run ) {
+ link = sendAsyncData(link);
+ }//while
+ }//while
+ }//run
+
+ protected LinkObject sendAsyncData(LinkObject link) {
+ ChannelMessage msg = link.data();
+ Member[] destination = link.getDestination();
+ try {
+ super.sendMessage(destination,msg,null);
+ try {
+ if ( link.getHandler() != null ) link.getHandler().handleCompletion(new UniqueId(msg.getUniqueId()));
+ } catch ( Exception ex ) {
+ log.error("Unable to report back completed message.",ex);
+ }
+ } catch ( Exception x ) {
+ ChannelException cx = null;
+ if ( x instanceof ChannelException ) cx = (ChannelException)x;
+ else cx = new ChannelException(x);
+ if ( log.isDebugEnabled() ) log.debug("Error while processing async message.",x);
+ try {
+ if (link.getHandler() != null) link.getHandler().handleError(cx, new UniqueId(msg.getUniqueId()));
+ } catch ( Exception ex ) {
+ log.error("Unable to report back error message.",ex);
+ }
+ } finally {
+ addAndGetCurrentSize(-msg.getMessage().getLength());
+ link = link.next();
+ }//try
+ return link;
+ }
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import java.util.concurrent.atomic.AtomicBoolean;\r
-\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelInterceptor;\r
-import org.apache.catalina.tribes.ChannelInterceptor.InterceptorEvent;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.UniqueId;\r
-import org.apache.catalina.tribes.group.AbsoluteOrder;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-import org.apache.catalina.tribes.io.ChannelData;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-import org.apache.catalina.tribes.membership.MemberImpl;\r
-import org.apache.catalina.tribes.membership.Membership;\r
-import org.apache.catalina.tribes.util.Arrays;\r
-import org.apache.catalina.tribes.util.UUIDGenerator;\r
-\r
-/**\r
- * <p>Title: Auto merging leader election algorithm</p>\r
- *\r
- * <p>Description: Implementation of a simple coordinator algorithm that not only selects a coordinator,\r
- * it also merges groups automatically when members are discovered that werent part of the \r
- * </p>\r
- * <p>This algorithm is non blocking meaning it allows for transactions while the coordination phase is going on\r
- * </p>\r
- * <p>This implementation is based on a home brewed algorithm that uses the AbsoluteOrder of a membership\r
- * to pass a token ring of the current membership.<br>\r
- * This is not the same as just using AbsoluteOrder! Consider the following scenario:<br>\r
- * Nodes, A,B,C,D,E on a network, in that priority. AbsoluteOrder will only work if all\r
- * nodes are receiving pings from all the other nodes. \r
- * meaning, that node{i} receives pings from node{all}-node{i}<br>\r
- * but the following could happen if a multicast problem occurs.\r
- * A has members {B,C,D}<br>\r
- * B has members {A,C}<br>\r
- * C has members {D,E}<br>\r
- * D has members {A,B,C,E}<br>\r
- * E has members {A,C,D}<br>\r
- * Because the default Tribes membership implementation, relies on the multicast packets to \r
- * arrive at all nodes correctly, there is nothing guaranteeing that it will.<br>\r
- * <br>\r
- * To best explain how this algorithm works, lets take the above example:\r
- * For simplicity we assume that a send operation is O(1) for all nodes, although this algorithm will work\r
- * where messages overlap, as they all depend on absolute order<br>\r
- * Scenario 1: A,B,C,D,E all come online at the same time\r
- * Eval phase, A thinks of itself as leader, B thinks of A as leader,\r
- * C thinks of itself as leader, D,E think of A as leader<br>\r
- * Token phase:<br>\r
- * (1) A sends out a message X{A-ldr, A-src, mbrs-A,B,C,D} to B where X is the id for the message(and the view)<br>\r
- * (1) C sends out a message Y{C-ldr, C-src, mbrs-C,D,E} to D where Y is the id for the message(and the view)<br>\r
- * (2) B receives X{A-ldr, A-src, mbrs-A,B,C,D}, sends X{A-ldr, A-src, mbrs-A,B,C,D} to C <br>\r
- * (2) D receives Y{C-ldr, C-src, mbrs-C,D,E} D is aware of A,B, sends Y{A-ldr, C-src, mbrs-A,B,C,D,E} to E<br>\r
- * (3) C receives X{A-ldr, A-src, mbrs-A,B,C,D}, sends X{A-ldr, A-src, mbrs-A,B,C,D,E} to D<br>\r
- * (3) E receives Y{A-ldr, C-src, mbrs-A,B,C,D,E} sends Y{A-ldr, C-src, mbrs-A,B,C,D,E} to A<br>\r
- * (4) D receives X{A-ldr, A-src, mbrs-A,B,C,D,E} sends sends X{A-ldr, A-src, mbrs-A,B,C,D,E} to A<br>\r
- * (4) A receives Y{A-ldr, C-src, mbrs-A,B,C,D,E}, holds the message, add E to its list of members<br>\r
- * (5) A receives X{A-ldr, A-src, mbrs-A,B,C,D,E} <br>\r
- * At this point, the state looks like<br>\r
- * A - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>\r
- * B - {A-ldr, mbrs-A,B,C,D, id=X}<br>\r
- * C - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>\r
- * D - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>\r
- * E - {A-ldr, mbrs-A,B,C,D,E, id=Y}<br>\r
- * <br>\r
- * A message doesn't stop until it reaches its original sender, unless its dropped by a higher leader.\r
- * As you can see, E still thinks the viewId=Y, which is not correct. But at this point we have \r
- * arrived at the same membership and all nodes are informed of each other.<br>\r
- * To synchronize the rest we simply perform the following check at A when A receives X:<br>\r
- * Original X{A-ldr, A-src, mbrs-A,B,C,D} == Arrived X{A-ldr, A-src, mbrs-A,B,C,D,E}<br>\r
- * Since the condition is false, A, will resend the token, and A sends X{A-ldr, A-src, mbrs-A,B,C,D,E} to B\r
- * When A receives X again, the token is complete. <br>\r
- * Optionally, A can send a message X{A-ldr, A-src, mbrs-A,B,C,D,E confirmed} to A,B,C,D,E who then\r
- * install and accept the view.\r
- * </p>\r
- * <p>\r
- * Lets assume that C1 arrives, C1 has lower priority than C, but higher priority than D.<br>\r
- * Lets also assume that C1 sees the following view {B,D,E}<br>\r
- * C1 waits for a token to arrive. When the token arrives, the same scenario as above will happen.<br>\r
- * In the scenario where C1 sees {D,E} and A,B,C can not see C1, no token will ever arrive.<br>\r
- * In this case, C1 sends a Z{C1-ldr, C1-src, mbrs-C1,D,E} to D<br>\r
- * D receives Z{C1-ldr, C1-src, mbrs-C1,D,E} and sends Z{A-ldr, C1-src, mbrs-A,B,C,C1,D,E} to E<br>\r
- * E receives Z{A-ldr, C1-src, mbrs-A,B,C,C1,D,E} and sends it to A<br>\r
- * A sends Z{A-ldr, A-src, mbrs-A,B,C,C1,D,E} to B and the chain continues until A receives the token again.\r
- * At that time A optionally sends out Z{A-ldr, A-src, mbrs-A,B,C,C1,D,E, confirmed} to A,B,C,C1,D,E\r
- * </p>\r
- * <p>To ensure that the view gets implemented at all nodes at the same time, \r
- * A will send out a VIEW_CONF message, this is the 'confirmed' message that is optional above.\r
- * <p>Ideally, the interceptor below this one would be the TcpFailureDetector to ensure correct memberships</p>\r
- *\r
- * <p>The example above, of course can be simplified with a finite statemachine:<br>\r
- * But I suck at writing state machines, my head gets all confused. One day I will document this algorithm though.<br>\r
- * Maybe I'll do a state diagram :)\r
- * </p>\r
- * <h2>State Diagrams</h2>\r
- * <a href="http://people.apache.org/~fhanik/tribes/docs/leader-election-initiate-election.jpg">Initiate an election</a><br><br>\r
- * <a href="http://people.apache.org/~fhanik/tribes/docs/leader-election-message-arrives.jpg">Receive an election message</a><br><br>\r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- * \r
- * \r
- * \r
- */\r
-public class NonBlockingCoordinator extends ChannelInterceptorBase {\r
- \r
- /**\r
- * header for a coordination message\r
- */\r
- protected static final byte[] COORD_HEADER = new byte[] {-86, 38, -34, -29, -98, 90, 65, 63, -81, -122, -6, -110, 99, -54, 13, 63};\r
- /**\r
- * Coordination request\r
- */\r
- protected static final byte[] COORD_REQUEST = new byte[] {104, -95, -92, -42, 114, -36, 71, -19, -79, 20, 122, 101, -1, -48, -49, 30};\r
- /**\r
- * Coordination confirmation, for blocking installations\r
- */\r
- protected static final byte[] COORD_CONF = new byte[] {67, 88, 107, -86, 69, 23, 76, -70, -91, -23, -87, -25, -125, 86, 75, 20};\r
- \r
- /**\r
- * Alive message\r
- */\r
- protected static final byte[] COORD_ALIVE = new byte[] {79, -121, -25, -15, -59, 5, 64, 94, -77, 113, -119, -88, 52, 114, -56, -46,\r
- -18, 102, 10, 34, -127, -9, 71, 115, -70, 72, -101, 88, 72, -124, 127, 111,\r
- 74, 76, -116, 50, 111, 103, 65, 3, -77, 51, -35, 0, 119, 117, 9, -26,\r
- 119, 50, -75, -105, -102, 36, 79, 37, -68, -84, -123, 15, -22, -109, 106, -55};\r
- /**\r
- * Time to wait for coordination timeout\r
- */\r
- protected long waitForCoordMsgTimeout = 15000;\r
- /**\r
- * Our current view\r
- */\r
- protected Membership view = null;\r
- /**\r
- * Out current viewId\r
- */\r
- protected UniqueId viewId;\r
-\r
- /**\r
- * Our nonblocking membership\r
- */\r
- protected Membership membership = null;\r
- \r
- /**\r
- * indicates that we are running an election \r
- * and this is the one we are running\r
- */\r
- protected UniqueId suggestedviewId;\r
- protected Membership suggestedView;\r
- \r
- protected boolean started = false;\r
- protected final int startsvc = 0xFFFF;\r
- \r
- protected Object electionMutex = new Object();\r
- \r
- protected AtomicBoolean coordMsgReceived = new AtomicBoolean(false);\r
- \r
- public NonBlockingCoordinator() {\r
- super();\r
- }\r
- \r
-//============================================================================================================ \r
-// COORDINATION HANDLING\r
-//============================================================================================================\r
- \r
- public void startElection(boolean force) throws ChannelException {\r
- synchronized (electionMutex) {\r
- MemberImpl local = (MemberImpl)getLocalMember(false);\r
- MemberImpl[] others = (MemberImpl[])membership.getMembers();\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START_ELECT,this,"Election initated"));\r
- if ( others.length == 0 ) {\r
- this.viewId = new UniqueId(UUIDGenerator.randomUUID(false));\r
- this.view = new Membership(local,AbsoluteOrder.comp, true);\r
- this.handleViewConf(this.createElectionMsg(local,others,local),local,view);\r
- return; //the only member, no need for an election\r
- }\r
- if ( suggestedviewId != null ) {\r
- \r
- if ( view != null && Arrays.diff(view,suggestedView,local).length == 0 && Arrays.diff(suggestedView,view,local).length == 0) {\r
- suggestedviewId = null;\r
- suggestedView = null;\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, running election matches view"));\r
- } else {\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, election running"));\r
- }\r
- return; //election already running, I'm not allowed to have two of them\r
- }\r
- if ( view != null && Arrays.diff(view,membership,local).length == 0 && Arrays.diff(membership,view,local).length == 0) {\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, view matches membership"));\r
- return; //already have this view installed\r
- } \r
- int prio = AbsoluteOrder.comp.compare(local,others[0]);\r
- MemberImpl leader = ( prio < 0 )?local:others[0];//am I the leader in my view?\r
- if ( local.equals(leader) || force ) {\r
- CoordinationMessage msg = createElectionMsg(local, others, leader);\r
- suggestedviewId = msg.getId();\r
- suggestedView = new Membership(local,AbsoluteOrder.comp,true);\r
- Arrays.fill(suggestedView,msg.getMembers());\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_PROCESS_ELECT,this,"Election, sending request"));\r
- sendElectionMsg(local,others[0],msg);\r
- } else {\r
- try {\r
- coordMsgReceived.set(false);\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_WAIT_FOR_MSG,this,"Election, waiting for request"));\r
- electionMutex.wait(waitForCoordMsgTimeout);\r
- }catch ( InterruptedException x ) {\r
- Thread.currentThread().interrupted();\r
- }\r
- if ( suggestedviewId == null && (!coordMsgReceived.get())) {\r
- //no message arrived, send the coord msg\r
-// fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_WAIT_FOR_MSG,this,"Election, waiting timed out."));\r
-// startElection(true);\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, waiting timed out."));\r
- } else {\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, received a message"));\r
- }\r
- }//end if\r
- \r
- }\r
- }\r
-\r
- private CoordinationMessage createElectionMsg(MemberImpl local, MemberImpl[] others, MemberImpl leader) {\r
- Membership m = new Membership(local,AbsoluteOrder.comp,true);\r
- Arrays.fill(m,others);\r
- MemberImpl[] mbrs = m.getMembers();\r
- m.reset(); \r
- CoordinationMessage msg = new CoordinationMessage(leader, local, mbrs,new UniqueId(UUIDGenerator.randomUUID(true)), this.COORD_REQUEST);\r
- return msg;\r
- }\r
-\r
- protected void sendElectionMsg(MemberImpl local, MemberImpl next, CoordinationMessage msg) throws ChannelException {\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_SEND_MSG,this,"Sending election message to("+next.getName()+")"));\r
- super.sendMessage(new Member[] {next}, createData(msg, local), null);\r
- }\r
- \r
- protected void sendElectionMsgToNextInline(MemberImpl local, CoordinationMessage msg) throws ChannelException { \r
- int next = Arrays.nextIndex(local,msg.getMembers());\r
- int current = next;\r
- msg.leader = msg.getMembers()[0];\r
- boolean sent = false;\r
- while ( !sent && current >= 0 ) {\r
- try {\r
- sendElectionMsg(local, (MemberImpl) msg.getMembers()[current], msg);\r
- sent = true;\r
- }catch ( ChannelException x ) {\r
- log.warn("Unable to send election message to:"+msg.getMembers()[current]);\r
- current = Arrays.nextIndex(msg.getMembers()[current],msg.getMembers());\r
- if ( current == next ) throw x;\r
- }\r
- }\r
- }\r
- \r
- public Member getNextInLine(MemberImpl local, MemberImpl[] others) {\r
- MemberImpl result = null;\r
- for ( int i=0; i<others.length; i++ ) {\r
- \r
- }\r
- return result;\r
- }\r
- \r
- public ChannelData createData(CoordinationMessage msg, MemberImpl local) {\r
- msg.write();\r
- ChannelData data = new ChannelData(true);\r
- data.setAddress(local);\r
- data.setMessage(msg.getBuffer());\r
- data.setOptions(Channel.SEND_OPTIONS_USE_ACK);\r
- data.setTimestamp(System.currentTimeMillis());\r
- return data;\r
- }\r
- \r
- protected void viewChange(UniqueId viewId, Member[] view) {\r
- //invoke any listeners\r
- }\r
- \r
- protected boolean alive(Member mbr) {\r
- return TcpFailureDetector.memberAlive(mbr,\r
- COORD_ALIVE,\r
- false,\r
- false,\r
- waitForCoordMsgTimeout,\r
- waitForCoordMsgTimeout,\r
- getOptionFlag());\r
- }\r
- \r
- protected Membership mergeOnArrive(CoordinationMessage msg, Member sender) {\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_PRE_MERGE,this,"Pre merge"));\r
- MemberImpl local = (MemberImpl)getLocalMember(false);\r
- Membership merged = new Membership(local,AbsoluteOrder.comp,true);\r
- Arrays.fill(merged,msg.getMembers());\r
- Arrays.fill(merged,getMembers());\r
- Member[] diff = Arrays.diff(merged,membership,local);\r
- for ( int i=0; i<diff.length; i++ ) {\r
- if (!alive(diff[i])) merged.removeMember((MemberImpl)diff[i]);\r
- else memberAdded(diff[i],false);\r
- }\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_POST_MERGE,this,"Post merge"));\r
- return merged;\r
- }\r
- \r
- protected void processCoordMessage(CoordinationMessage msg, Member sender) throws ChannelException {\r
- if ( !coordMsgReceived.get() ) {\r
- coordMsgReceived.set(true);\r
- synchronized (electionMutex) { electionMutex.notifyAll();}\r
- } \r
- msg.timestamp = System.currentTimeMillis();\r
- Membership merged = mergeOnArrive(msg, sender);\r
- if (isViewConf(msg)) handleViewConf(msg, sender, merged);\r
- else handleToken(msg, sender, merged);\r
- ClassLoader loader;\r
-\r
- }\r
- \r
- protected void handleToken(CoordinationMessage msg, Member sender,Membership merged) throws ChannelException {\r
- MemberImpl local = (MemberImpl)getLocalMember(false);\r
- if ( local.equals(msg.getSource()) ) {\r
- //my message msg.src=local\r
- handleMyToken(local, msg, sender,merged);\r
- } else {\r
- handleOtherToken(local, msg, sender,merged);\r
- }\r
- }\r
- \r
- protected void handleMyToken(MemberImpl local, CoordinationMessage msg, Member sender,Membership merged) throws ChannelException {\r
- if ( local.equals(msg.getLeader()) ) {\r
- //no leadership change\r
- if ( Arrays.sameMembers(msg.getMembers(),merged.getMembers()) ) {\r
- msg.type = COORD_CONF;\r
- super.sendMessage(Arrays.remove(msg.getMembers(),local),createData(msg,local),null);\r
- handleViewConf(msg,local,merged);\r
- } else {\r
- //membership change\r
- suggestedView = new Membership(local,AbsoluteOrder.comp,true);\r
- suggestedviewId = msg.getId();\r
- Arrays.fill(suggestedView,merged.getMembers());\r
- msg.view = (MemberImpl[])merged.getMembers();\r
- sendElectionMsgToNextInline(local,msg);\r
- }\r
- } else {\r
- //leadership change\r
- suggestedView = null;\r
- suggestedviewId = null;\r
- msg.view = (MemberImpl[])merged.getMembers();\r
- sendElectionMsgToNextInline(local,msg);\r
- }\r
- }\r
- \r
- protected void handleOtherToken(MemberImpl local, CoordinationMessage msg, Member sender,Membership merged) throws ChannelException {\r
- if ( local.equals(msg.getLeader()) ) {\r
- //I am the new leader\r
- //startElection(false);\r
- } else {\r
- msg.view = (MemberImpl[])merged.getMembers();\r
- sendElectionMsgToNextInline(local,msg);\r
- }\r
- }\r
- \r
- protected void handleViewConf(CoordinationMessage msg, Member sender,Membership merged) throws ChannelException {\r
- if ( viewId != null && msg.getId().equals(viewId) ) return;//we already have this view\r
- view = new Membership((MemberImpl)getLocalMember(false),AbsoluteOrder.comp,true);\r
- Arrays.fill(view,msg.getMembers());\r
- viewId = msg.getId();\r
- \r
- if ( viewId.equals(suggestedviewId) ) {\r
- suggestedView = null;\r
- suggestedviewId = null;\r
- }\r
- \r
- if (suggestedView != null && AbsoluteOrder.comp.compare(suggestedView.getMembers()[0],merged.getMembers()[0])<0 ) {\r
- suggestedView = null;\r
- suggestedviewId = null;\r
- }\r
- \r
- viewChange(viewId,view.getMembers());\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_CONF_RX,this,"Accepted View"));\r
- \r
- if ( suggestedviewId == null && hasHigherPriority(merged.getMembers(),membership.getMembers()) ) {\r
- startElection(false);\r
- }\r
- }\r
- \r
- protected boolean isViewConf(CoordinationMessage msg) {\r
- return Arrays.contains(msg.getType(),0,COORD_CONF,0,COORD_CONF.length);\r
- }\r
- \r
- protected boolean hasHigherPriority(Member[] complete, Member[] local) {\r
- if ( local == null || local.length == 0 ) return false;\r
- if ( complete == null || complete.length == 0 ) return true;\r
- AbsoluteOrder.absoluteOrder(complete);\r
- AbsoluteOrder.absoluteOrder(local);\r
- return (AbsoluteOrder.comp.compare(complete[0],local[0]) > 0);\r
- \r
- }\r
-\r
- \r
- /**\r
- * Returns coordinator if one is available\r
- * @return Member\r
- */\r
- public Member getCoordinator() {\r
- return (view != null && view.hasMembers()) ? view.getMembers()[0] : null;\r
- }\r
- \r
- public Member[] getView() {\r
- return (view != null && view.hasMembers()) ? view.getMembers() : new Member[0];\r
- }\r
- \r
- public UniqueId getViewId() {\r
- return viewId;\r
- }\r
- \r
- /**\r
- * Block in/out messages while a election is going on\r
- */\r
- protected void halt() {\r
-\r
- }\r
-\r
- /**\r
- * Release lock for in/out messages election is completed\r
- */\r
- protected void release() {\r
-\r
- }\r
-\r
- /**\r
- * Wait for an election to end\r
- */\r
- protected void waitForRelease() {\r
-\r
- }\r
-\r
- \r
-//============================================================================================================ \r
-// OVERRIDDEN METHODS FROM CHANNEL INTERCEPTOR BASE \r
-//============================================================================================================\r
- public void start(int svc) throws ChannelException {\r
- if (membership == null) setupMembership();\r
- if (started)return;\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START, this, "Before start"));\r
- super.start(startsvc);\r
- started = true;\r
- if (view == null) view = new Membership( (MemberImpl)super.getLocalMember(true), AbsoluteOrder.comp, true);\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START, this, "After start"));\r
- startElection(false);\r
- }\r
-\r
- public void stop(int svc) throws ChannelException {\r
- try {\r
- halt();\r
- synchronized (electionMutex) {\r
- if (!started)return;\r
- started = false;\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_STOP, this, "Before stop"));\r
- super.stop(startsvc);\r
- this.view = null;\r
- this.viewId = null;\r
- this.suggestedView = null;\r
- this.suggestedviewId = null;\r
- this.membership.reset();\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_STOP, this, "After stop"));\r
- }\r
- }finally {\r
- release();\r
- }\r
- }\r
- \r
- \r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- waitForRelease();\r
- super.sendMessage(destination, msg, payload);\r
- }\r
-\r
- public void messageReceived(ChannelMessage msg) {\r
- if ( Arrays.contains(msg.getMessage().getBytesDirect(),0,COORD_ALIVE,0,COORD_ALIVE.length) ) {\r
- //ignore message, its an alive message\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MSG_ARRIVE,this,"Alive Message"));\r
-\r
- } else if ( Arrays.contains(msg.getMessage().getBytesDirect(),0,COORD_HEADER,0,COORD_HEADER.length) ) {\r
- try {\r
- CoordinationMessage cmsg = new CoordinationMessage(msg.getMessage());\r
- Member[] cmbr = cmsg.getMembers();\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MSG_ARRIVE,this,"Coord Msg Arrived("+Arrays.toNameString(cmbr)+")"));\r
- processCoordMessage(cmsg, msg.getAddress());\r
- }catch ( ChannelException x ) {\r
- log.error("Error processing coordination message. Could be fatal.",x);\r
- }\r
- } else {\r
- super.messageReceived(msg);\r
- }\r
- }\r
-\r
- public boolean accept(ChannelMessage msg) {\r
- return super.accept(msg);\r
- }\r
-\r
- public void memberAdded(Member member) {\r
- memberAdded(member,true);\r
- }\r
-\r
- public void memberAdded(Member member,boolean elect) {\r
- try {\r
- if ( membership == null ) setupMembership();\r
- if ( membership.memberAlive((MemberImpl)member) ) super.memberAdded(member);\r
- try {\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MBR_ADD,this,"Member add("+member.getName()+")"));\r
- if (started && elect) startElection(false);\r
- }catch ( ChannelException x ) {\r
- log.error("Unable to start election when member was added.",x);\r
- }\r
- }finally {\r
- }\r
- \r
- }\r
-\r
- public void memberDisappeared(Member member) {\r
- try {\r
- \r
- membership.removeMember((MemberImpl)member);\r
- super.memberDisappeared(member);\r
- try {\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MBR_DEL,this,"Member remove("+member.getName()+")"));\r
- if ( started && (isCoordinator() || isHighest()) ) \r
- startElection(true); //to do, if a member disappears, only the coordinator can start\r
- }catch ( ChannelException x ) {\r
- log.error("Unable to start election when member was removed.",x);\r
- }\r
- }finally {\r
- }\r
- }\r
- \r
- public boolean isHighest() {\r
- Member local = getLocalMember(false);\r
- if ( membership.getMembers().length == 0 ) return true;\r
- else return AbsoluteOrder.comp.compare(local,membership.getMembers()[0])<=0;\r
- }\r
- \r
- public boolean isCoordinator() {\r
- Member coord = getCoordinator();\r
- return coord != null && getLocalMember(false).equals(coord);\r
- }\r
-\r
- public void heartbeat() {\r
- try {\r
- MemberImpl local = (MemberImpl)getLocalMember(false);\r
- if ( view != null && (Arrays.diff(view,membership,local).length != 0 || Arrays.diff(membership,view,local).length != 0) ) {\r
- if ( isHighest() ) {\r
- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START_ELECT, this,\r
- "Heartbeat found inconsistency, restart election"));\r
- startElection(true);\r
- } \r
- }\r
- } catch ( Exception x ){\r
- log.error("Unable to perform heartbeat.",x);\r
- } finally {\r
- super.heartbeat();\r
- }\r
- }\r
-\r
- /**\r
- * has members\r
- */\r
- public boolean hasMembers() {\r
- \r
- return membership.hasMembers();\r
- }\r
-\r
- /**\r
- * Get all current cluster members\r
- * @return all members or empty array\r
- */\r
- public Member[] getMembers() {\r
- \r
- return membership.getMembers();\r
- }\r
-\r
- /**\r
- *\r
- * @param mbr Member\r
- * @return Member\r
- */\r
- public Member getMember(Member mbr) {\r
- \r
- return membership.getMember(mbr);\r
- }\r
-\r
- /**\r
- * Return the member that represents this node.\r
- *\r
- * @return Member\r
- */\r
- public Member getLocalMember(boolean incAlive) {\r
- Member local = super.getLocalMember(incAlive);\r
- if ( view == null && (local != null)) setupMembership();\r
- return local;\r
- }\r
- \r
- protected synchronized void setupMembership() {\r
- if ( membership == null ) {\r
- membership = new Membership((MemberImpl)super.getLocalMember(true),AbsoluteOrder.comp,false);\r
- }\r
- }\r
- \r
- \r
-//============================================================================================================ \r
-// HELPER CLASSES FOR COORDINATION\r
-//============================================================================================================\r
- \r
- \r
- \r
- \r
- public static class CoordinationMessage {\r
- //X{A-ldr, A-src, mbrs-A,B,C,D}\r
- protected XByteBuffer buf;\r
- protected MemberImpl leader;\r
- protected MemberImpl source;\r
- protected MemberImpl[] view;\r
- protected UniqueId id;\r
- protected byte[] type;\r
- protected long timestamp = System.currentTimeMillis();\r
- \r
- public CoordinationMessage(XByteBuffer buf) {\r
- this.buf = buf;\r
- parse();\r
- }\r
-\r
- public CoordinationMessage(MemberImpl leader,\r
- MemberImpl source, \r
- MemberImpl[] view,\r
- UniqueId id,\r
- byte[] type) {\r
- this.buf = new XByteBuffer(4096,false);\r
- this.leader = leader;\r
- this.source = source;\r
- this.view = view;\r
- this.id = id;\r
- this.type = type;\r
- this.write();\r
- }\r
- \r
-\r
- public byte[] getHeader() {\r
- return NonBlockingCoordinator.COORD_HEADER;\r
- }\r
- \r
- public MemberImpl getLeader() {\r
- if ( leader == null ) parse();\r
- return leader;\r
- }\r
- \r
- public MemberImpl getSource() {\r
- if ( source == null ) parse();\r
- return source;\r
- }\r
- \r
- public UniqueId getId() {\r
- if ( id == null ) parse();\r
- return id;\r
- }\r
- \r
- public MemberImpl[] getMembers() {\r
- if ( view == null ) parse();\r
- return view;\r
- }\r
- \r
- public byte[] getType() {\r
- if (type == null ) parse();\r
- return type;\r
- }\r
- \r
- public XByteBuffer getBuffer() {\r
- return this.buf;\r
- }\r
- \r
- public void parse() {\r
- //header\r
- int offset = 16;\r
- //leader\r
- int ldrLen = buf.toInt(buf.getBytesDirect(),offset);\r
- offset += 4;\r
- byte[] ldr = new byte[ldrLen];\r
- System.arraycopy(buf.getBytesDirect(),offset,ldr,0,ldrLen);\r
- leader = MemberImpl.getMember(ldr);\r
- offset += ldrLen;\r
- //source\r
- int srcLen = buf.toInt(buf.getBytesDirect(),offset);\r
- offset += 4;\r
- byte[] src = new byte[srcLen];\r
- System.arraycopy(buf.getBytesDirect(),offset,src,0,srcLen);\r
- source = MemberImpl.getMember(src);\r
- offset += srcLen;\r
- //view\r
- int mbrCount = buf.toInt(buf.getBytesDirect(),offset);\r
- offset += 4;\r
- view = new MemberImpl[mbrCount];\r
- for (int i=0; i<view.length; i++ ) {\r
- int mbrLen = buf.toInt(buf.getBytesDirect(),offset);\r
- offset += 4;\r
- byte[] mbr = new byte[mbrLen];\r
- System.arraycopy(buf.getBytesDirect(), offset, mbr, 0, mbrLen);\r
- view[i] = MemberImpl.getMember(mbr);\r
- offset += mbrLen;\r
- }\r
- //id\r
- this.id = new UniqueId(buf.getBytesDirect(),offset,16);\r
- offset += 16;\r
- type = new byte[16];\r
- System.arraycopy(buf.getBytesDirect(), offset, type, 0, type.length);\r
- offset += 16;\r
- \r
- }\r
- \r
- public void write() {\r
- buf.reset();\r
- //header\r
- buf.append(COORD_HEADER,0,COORD_HEADER.length);\r
- //leader\r
- byte[] ldr = leader.getData(false,false);\r
- buf.append(ldr.length);\r
- buf.append(ldr,0,ldr.length);\r
- ldr = null;\r
- //source\r
- byte[] src = source.getData(false,false);\r
- buf.append(src.length);\r
- buf.append(src,0,src.length);\r
- src = null;\r
- //view\r
- buf.append(view.length);\r
- for (int i=0; i<view.length; i++ ) {\r
- byte[] mbr = view[i].getData(false,false);\r
- buf.append(mbr.length);\r
- buf.append(mbr,0,mbr.length);\r
- }\r
- //id\r
- buf.append(id.getBytes(),0,id.getBytes().length);\r
- buf.append(type,0,type.length);\r
- }\r
- }\r
- \r
- public void fireInterceptorEvent(InterceptorEvent event) {\r
- if (event instanceof CoordinationEvent &&\r
- ((CoordinationEvent)event).type == CoordinationEvent.EVT_CONF_RX) \r
- log.info(event);\r
- }\r
- \r
- public static class CoordinationEvent implements InterceptorEvent {\r
- public static final int EVT_START = 1;\r
- public static final int EVT_MBR_ADD = 2;\r
- public static final int EVT_MBR_DEL = 3;\r
- public static final int EVT_START_ELECT = 4;\r
- public static final int EVT_PROCESS_ELECT = 5;\r
- public static final int EVT_MSG_ARRIVE = 6;\r
- public static final int EVT_PRE_MERGE = 7;\r
- public static final int EVT_POST_MERGE = 8;\r
- public static final int EVT_WAIT_FOR_MSG = 9;\r
- public static final int EVT_SEND_MSG = 10;\r
- public static final int EVT_STOP = 11;\r
- public static final int EVT_CONF_RX = 12;\r
- public static final int EVT_ELECT_ABANDONED = 13;\r
- \r
- int type;\r
- ChannelInterceptor interceptor;\r
- Member coord; \r
- Member[] mbrs;\r
- String info;\r
- Membership view;\r
- Membership suggestedView;\r
- public CoordinationEvent(int type,ChannelInterceptor interceptor, String info) {\r
- this.type = type;\r
- this.interceptor = interceptor;\r
- this.coord = ((NonBlockingCoordinator)interceptor).getCoordinator();\r
- this.mbrs = ((NonBlockingCoordinator)interceptor).membership.getMembers();\r
- this.info = info;\r
- this.view = ((NonBlockingCoordinator)interceptor).view;\r
- this.suggestedView = ((NonBlockingCoordinator)interceptor).suggestedView;\r
- }\r
- \r
- public int getEventType() {\r
- return type;\r
- }\r
- \r
- public String getEventTypeDesc() {\r
- switch (type) {\r
- case EVT_START: return "EVT_START:"+info;\r
- case EVT_MBR_ADD: return "EVT_MBR_ADD:"+info;\r
- case EVT_MBR_DEL: return "EVT_MBR_DEL:"+info;\r
- case EVT_START_ELECT: return "EVT_START_ELECT:"+info;\r
- case EVT_PROCESS_ELECT: return "EVT_PROCESS_ELECT:"+info;\r
- case EVT_MSG_ARRIVE: return "EVT_MSG_ARRIVE:"+info;\r
- case EVT_PRE_MERGE: return "EVT_PRE_MERGE:"+info;\r
- case EVT_POST_MERGE: return "EVT_POST_MERGE:"+info;\r
- case EVT_WAIT_FOR_MSG: return "EVT_WAIT_FOR_MSG:"+info;\r
- case EVT_SEND_MSG: return "EVT_SEND_MSG:"+info;\r
- case EVT_STOP: return "EVT_STOP:"+info;\r
- case EVT_CONF_RX: return "EVT_CONF_RX:"+info;\r
- case EVT_ELECT_ABANDONED: return "EVT_ELECT_ABANDONED:"+info;\r
- default: return "Unknown";\r
- }\r
- }\r
- \r
- public ChannelInterceptor getInterceptor() {\r
- return interceptor;\r
- }\r
- \r
- public String toString() {\r
- StringBuffer buf = new StringBuffer("CoordinationEvent[type=");\r
- buf.append(type).append("\n\tLocal:");\r
- Member local = interceptor.getLocalMember(false);\r
- buf.append(local!=null?local.getName():"").append("\n\tCoord:");\r
- buf.append(coord!=null?coord.getName():"").append("\n\tView:");\r
- buf.append(Arrays.toNameString(view!=null?view.getMembers():null)).append("\n\tSuggested View:");\r
- buf.append(Arrays.toNameString(suggestedView!=null?suggestedView.getMembers():null)).append("\n\tMembers:");\r
- buf.append(Arrays.toNameString(mbrs)).append("\n\tInfo:");\r
- buf.append(info).append("]");\r
- return buf.toString();\r
- }\r
- }\r
-\r
- \r
-\r
-\r
-\r
+/*
+ * 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
+ */
+package org.apache.catalina.tribes.group.interceptors;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelInterceptor;
+import org.apache.catalina.tribes.ChannelInterceptor.InterceptorEvent;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.UniqueId;
+import org.apache.catalina.tribes.group.AbsoluteOrder;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.io.ChannelData;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.catalina.tribes.membership.MemberImpl;
+import org.apache.catalina.tribes.membership.Membership;
+import org.apache.catalina.tribes.util.Arrays;
+import org.apache.catalina.tribes.util.UUIDGenerator;
+
+/**
+ * <p>Title: Auto merging leader election algorithm</p>
+ *
+ * <p>Description: Implementation of a simple coordinator algorithm that not only selects a coordinator,
+ * it also merges groups automatically when members are discovered that werent part of the
+ * </p>
+ * <p>This algorithm is non blocking meaning it allows for transactions while the coordination phase is going on
+ * </p>
+ * <p>This implementation is based on a home brewed algorithm that uses the AbsoluteOrder of a membership
+ * to pass a token ring of the current membership.<br>
+ * This is not the same as just using AbsoluteOrder! Consider the following scenario:<br>
+ * Nodes, A,B,C,D,E on a network, in that priority. AbsoluteOrder will only work if all
+ * nodes are receiving pings from all the other nodes.
+ * meaning, that node{i} receives pings from node{all}-node{i}<br>
+ * but the following could happen if a multicast problem occurs.
+ * A has members {B,C,D}<br>
+ * B has members {A,C}<br>
+ * C has members {D,E}<br>
+ * D has members {A,B,C,E}<br>
+ * E has members {A,C,D}<br>
+ * Because the default Tribes membership implementation, relies on the multicast packets to
+ * arrive at all nodes correctly, there is nothing guaranteeing that it will.<br>
+ * <br>
+ * To best explain how this algorithm works, lets take the above example:
+ * For simplicity we assume that a send operation is O(1) for all nodes, although this algorithm will work
+ * where messages overlap, as they all depend on absolute order<br>
+ * Scenario 1: A,B,C,D,E all come online at the same time
+ * Eval phase, A thinks of itself as leader, B thinks of A as leader,
+ * C thinks of itself as leader, D,E think of A as leader<br>
+ * Token phase:<br>
+ * (1) A sends out a message X{A-ldr, A-src, mbrs-A,B,C,D} to B where X is the id for the message(and the view)<br>
+ * (1) C sends out a message Y{C-ldr, C-src, mbrs-C,D,E} to D where Y is the id for the message(and the view)<br>
+ * (2) B receives X{A-ldr, A-src, mbrs-A,B,C,D}, sends X{A-ldr, A-src, mbrs-A,B,C,D} to C <br>
+ * (2) D receives Y{C-ldr, C-src, mbrs-C,D,E} D is aware of A,B, sends Y{A-ldr, C-src, mbrs-A,B,C,D,E} to E<br>
+ * (3) C receives X{A-ldr, A-src, mbrs-A,B,C,D}, sends X{A-ldr, A-src, mbrs-A,B,C,D,E} to D<br>
+ * (3) E receives Y{A-ldr, C-src, mbrs-A,B,C,D,E} sends Y{A-ldr, C-src, mbrs-A,B,C,D,E} to A<br>
+ * (4) D receives X{A-ldr, A-src, mbrs-A,B,C,D,E} sends sends X{A-ldr, A-src, mbrs-A,B,C,D,E} to A<br>
+ * (4) A receives Y{A-ldr, C-src, mbrs-A,B,C,D,E}, holds the message, add E to its list of members<br>
+ * (5) A receives X{A-ldr, A-src, mbrs-A,B,C,D,E} <br>
+ * At this point, the state looks like<br>
+ * A - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>
+ * B - {A-ldr, mbrs-A,B,C,D, id=X}<br>
+ * C - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>
+ * D - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>
+ * E - {A-ldr, mbrs-A,B,C,D,E, id=Y}<br>
+ * <br>
+ * A message doesn't stop until it reaches its original sender, unless its dropped by a higher leader.
+ * As you can see, E still thinks the viewId=Y, which is not correct. But at this point we have
+ * arrived at the same membership and all nodes are informed of each other.<br>
+ * To synchronize the rest we simply perform the following check at A when A receives X:<br>
+ * Original X{A-ldr, A-src, mbrs-A,B,C,D} == Arrived X{A-ldr, A-src, mbrs-A,B,C,D,E}<br>
+ * Since the condition is false, A, will resend the token, and A sends X{A-ldr, A-src, mbrs-A,B,C,D,E} to B
+ * When A receives X again, the token is complete. <br>
+ * Optionally, A can send a message X{A-ldr, A-src, mbrs-A,B,C,D,E confirmed} to A,B,C,D,E who then
+ * install and accept the view.
+ * </p>
+ * <p>
+ * Lets assume that C1 arrives, C1 has lower priority than C, but higher priority than D.<br>
+ * Lets also assume that C1 sees the following view {B,D,E}<br>
+ * C1 waits for a token to arrive. When the token arrives, the same scenario as above will happen.<br>
+ * In the scenario where C1 sees {D,E} and A,B,C can not see C1, no token will ever arrive.<br>
+ * In this case, C1 sends a Z{C1-ldr, C1-src, mbrs-C1,D,E} to D<br>
+ * D receives Z{C1-ldr, C1-src, mbrs-C1,D,E} and sends Z{A-ldr, C1-src, mbrs-A,B,C,C1,D,E} to E<br>
+ * E receives Z{A-ldr, C1-src, mbrs-A,B,C,C1,D,E} and sends it to A<br>
+ * A sends Z{A-ldr, A-src, mbrs-A,B,C,C1,D,E} to B and the chain continues until A receives the token again.
+ * At that time A optionally sends out Z{A-ldr, A-src, mbrs-A,B,C,C1,D,E, confirmed} to A,B,C,C1,D,E
+ * </p>
+ * <p>To ensure that the view gets implemented at all nodes at the same time,
+ * A will send out a VIEW_CONF message, this is the 'confirmed' message that is optional above.
+ * <p>Ideally, the interceptor below this one would be the TcpFailureDetector to ensure correct memberships</p>
+ *
+ * <p>The example above, of course can be simplified with a finite statemachine:<br>
+ * But I suck at writing state machines, my head gets all confused. One day I will document this algorithm though.<br>
+ * Maybe I'll do a state diagram :)
+ * </p>
+ * <h2>State Diagrams</h2>
+ * <a href="http://people.apache.org/~fhanik/tribes/docs/leader-election-initiate-election.jpg">Initiate an election</a><br><br>
+ * <a href="http://people.apache.org/~fhanik/tribes/docs/leader-election-message-arrives.jpg">Receive an election message</a><br><br>
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ *
+ *
+ *
+ */
+public class NonBlockingCoordinator extends ChannelInterceptorBase {
+
+ /**
+ * header for a coordination message
+ */
+ protected static final byte[] COORD_HEADER = new byte[] {-86, 38, -34, -29, -98, 90, 65, 63, -81, -122, -6, -110, 99, -54, 13, 63};
+ /**
+ * Coordination request
+ */
+ protected static final byte[] COORD_REQUEST = new byte[] {104, -95, -92, -42, 114, -36, 71, -19, -79, 20, 122, 101, -1, -48, -49, 30};
+ /**
+ * Coordination confirmation, for blocking installations
+ */
+ protected static final byte[] COORD_CONF = new byte[] {67, 88, 107, -86, 69, 23, 76, -70, -91, -23, -87, -25, -125, 86, 75, 20};
+
+ /**
+ * Alive message
+ */
+ protected static final byte[] COORD_ALIVE = new byte[] {79, -121, -25, -15, -59, 5, 64, 94, -77, 113, -119, -88, 52, 114, -56, -46,
+ -18, 102, 10, 34, -127, -9, 71, 115, -70, 72, -101, 88, 72, -124, 127, 111,
+ 74, 76, -116, 50, 111, 103, 65, 3, -77, 51, -35, 0, 119, 117, 9, -26,
+ 119, 50, -75, -105, -102, 36, 79, 37, -68, -84, -123, 15, -22, -109, 106, -55};
+ /**
+ * Time to wait for coordination timeout
+ */
+ protected long waitForCoordMsgTimeout = 15000;
+ /**
+ * Our current view
+ */
+ protected Membership view = null;
+ /**
+ * Out current viewId
+ */
+ protected UniqueId viewId;
+
+ /**
+ * Our nonblocking membership
+ */
+ protected Membership membership = null;
+
+ /**
+ * indicates that we are running an election
+ * and this is the one we are running
+ */
+ protected UniqueId suggestedviewId;
+ protected Membership suggestedView;
+
+ protected boolean started = false;
+ protected final int startsvc = 0xFFFF;
+
+ protected Object electionMutex = new Object();
+
+ protected AtomicBoolean coordMsgReceived = new AtomicBoolean(false);
+
+ public NonBlockingCoordinator() {
+ super();
+ }
+
+//============================================================================================================
+// COORDINATION HANDLING
+//============================================================================================================
+
+ public void startElection(boolean force) throws ChannelException {
+ synchronized (electionMutex) {
+ MemberImpl local = (MemberImpl)getLocalMember(false);
+ MemberImpl[] others = (MemberImpl[])membership.getMembers();
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START_ELECT,this,"Election initated"));
+ if ( others.length == 0 ) {
+ this.viewId = new UniqueId(UUIDGenerator.randomUUID(false));
+ this.view = new Membership(local,AbsoluteOrder.comp, true);
+ this.handleViewConf(this.createElectionMsg(local,others,local),local,view);
+ return; //the only member, no need for an election
+ }
+ if ( suggestedviewId != null ) {
+
+ if ( view != null && Arrays.diff(view,suggestedView,local).length == 0 && Arrays.diff(suggestedView,view,local).length == 0) {
+ suggestedviewId = null;
+ suggestedView = null;
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, running election matches view"));
+ } else {
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, election running"));
+ }
+ return; //election already running, I'm not allowed to have two of them
+ }
+ if ( view != null && Arrays.diff(view,membership,local).length == 0 && Arrays.diff(membership,view,local).length == 0) {
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, view matches membership"));
+ return; //already have this view installed
+ }
+ int prio = AbsoluteOrder.comp.compare(local,others[0]);
+ MemberImpl leader = ( prio < 0 )?local:others[0];//am I the leader in my view?
+ if ( local.equals(leader) || force ) {
+ CoordinationMessage msg = createElectionMsg(local, others, leader);
+ suggestedviewId = msg.getId();
+ suggestedView = new Membership(local,AbsoluteOrder.comp,true);
+ Arrays.fill(suggestedView,msg.getMembers());
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_PROCESS_ELECT,this,"Election, sending request"));
+ sendElectionMsg(local,others[0],msg);
+ } else {
+ try {
+ coordMsgReceived.set(false);
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_WAIT_FOR_MSG,this,"Election, waiting for request"));
+ electionMutex.wait(waitForCoordMsgTimeout);
+ }catch ( InterruptedException x ) {
+ Thread.currentThread().interrupted();
+ }
+ if ( suggestedviewId == null && (!coordMsgReceived.get())) {
+ //no message arrived, send the coord msg
+// fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_WAIT_FOR_MSG,this,"Election, waiting timed out."));
+// startElection(true);
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, waiting timed out."));
+ } else {
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned, received a message"));
+ }
+ }//end if
+
+ }
+ }
+
+ private CoordinationMessage createElectionMsg(MemberImpl local, MemberImpl[] others, MemberImpl leader) {
+ Membership m = new Membership(local,AbsoluteOrder.comp,true);
+ Arrays.fill(m,others);
+ MemberImpl[] mbrs = m.getMembers();
+ m.reset();
+ CoordinationMessage msg = new CoordinationMessage(leader, local, mbrs,new UniqueId(UUIDGenerator.randomUUID(true)), this.COORD_REQUEST);
+ return msg;
+ }
+
+ protected void sendElectionMsg(MemberImpl local, MemberImpl next, CoordinationMessage msg) throws ChannelException {
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_SEND_MSG,this,"Sending election message to("+next.getName()+")"));
+ super.sendMessage(new Member[] {next}, createData(msg, local), null);
+ }
+
+ protected void sendElectionMsgToNextInline(MemberImpl local, CoordinationMessage msg) throws ChannelException {
+ int next = Arrays.nextIndex(local,msg.getMembers());
+ int current = next;
+ msg.leader = msg.getMembers()[0];
+ boolean sent = false;
+ while ( !sent && current >= 0 ) {
+ try {
+ sendElectionMsg(local, (MemberImpl) msg.getMembers()[current], msg);
+ sent = true;
+ }catch ( ChannelException x ) {
+ log.warn("Unable to send election message to:"+msg.getMembers()[current]);
+ current = Arrays.nextIndex(msg.getMembers()[current],msg.getMembers());
+ if ( current == next ) throw x;
+ }
+ }
+ }
+
+ public Member getNextInLine(MemberImpl local, MemberImpl[] others) {
+ MemberImpl result = null;
+ for ( int i=0; i<others.length; i++ ) {
+
+ }
+ return result;
+ }
+
+ public ChannelData createData(CoordinationMessage msg, MemberImpl local) {
+ msg.write();
+ ChannelData data = new ChannelData(true);
+ data.setAddress(local);
+ data.setMessage(msg.getBuffer());
+ data.setOptions(Channel.SEND_OPTIONS_USE_ACK);
+ data.setTimestamp(System.currentTimeMillis());
+ return data;
+ }
+
+ protected void viewChange(UniqueId viewId, Member[] view) {
+ //invoke any listeners
+ }
+
+ protected boolean alive(Member mbr) {
+ return TcpFailureDetector.memberAlive(mbr,
+ COORD_ALIVE,
+ false,
+ false,
+ waitForCoordMsgTimeout,
+ waitForCoordMsgTimeout,
+ getOptionFlag());
+ }
+
+ protected Membership mergeOnArrive(CoordinationMessage msg, Member sender) {
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_PRE_MERGE,this,"Pre merge"));
+ MemberImpl local = (MemberImpl)getLocalMember(false);
+ Membership merged = new Membership(local,AbsoluteOrder.comp,true);
+ Arrays.fill(merged,msg.getMembers());
+ Arrays.fill(merged,getMembers());
+ Member[] diff = Arrays.diff(merged,membership,local);
+ for ( int i=0; i<diff.length; i++ ) {
+ if (!alive(diff[i])) merged.removeMember((MemberImpl)diff[i]);
+ else memberAdded(diff[i],false);
+ }
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_POST_MERGE,this,"Post merge"));
+ return merged;
+ }
+
+ protected void processCoordMessage(CoordinationMessage msg, Member sender) throws ChannelException {
+ if ( !coordMsgReceived.get() ) {
+ coordMsgReceived.set(true);
+ synchronized (electionMutex) { electionMutex.notifyAll();}
+ }
+ msg.timestamp = System.currentTimeMillis();
+ Membership merged = mergeOnArrive(msg, sender);
+ if (isViewConf(msg)) handleViewConf(msg, sender, merged);
+ else handleToken(msg, sender, merged);
+ ClassLoader loader;
+
+ }
+
+ protected void handleToken(CoordinationMessage msg, Member sender,Membership merged) throws ChannelException {
+ MemberImpl local = (MemberImpl)getLocalMember(false);
+ if ( local.equals(msg.getSource()) ) {
+ //my message msg.src=local
+ handleMyToken(local, msg, sender,merged);
+ } else {
+ handleOtherToken(local, msg, sender,merged);
+ }
+ }
+
+ protected void handleMyToken(MemberImpl local, CoordinationMessage msg, Member sender,Membership merged) throws ChannelException {
+ if ( local.equals(msg.getLeader()) ) {
+ //no leadership change
+ if ( Arrays.sameMembers(msg.getMembers(),merged.getMembers()) ) {
+ msg.type = COORD_CONF;
+ super.sendMessage(Arrays.remove(msg.getMembers(),local),createData(msg,local),null);
+ handleViewConf(msg,local,merged);
+ } else {
+ //membership change
+ suggestedView = new Membership(local,AbsoluteOrder.comp,true);
+ suggestedviewId = msg.getId();
+ Arrays.fill(suggestedView,merged.getMembers());
+ msg.view = (MemberImpl[])merged.getMembers();
+ sendElectionMsgToNextInline(local,msg);
+ }
+ } else {
+ //leadership change
+ suggestedView = null;
+ suggestedviewId = null;
+ msg.view = (MemberImpl[])merged.getMembers();
+ sendElectionMsgToNextInline(local,msg);
+ }
+ }
+
+ protected void handleOtherToken(MemberImpl local, CoordinationMessage msg, Member sender,Membership merged) throws ChannelException {
+ if ( local.equals(msg.getLeader()) ) {
+ //I am the new leader
+ //startElection(false);
+ } else {
+ msg.view = (MemberImpl[])merged.getMembers();
+ sendElectionMsgToNextInline(local,msg);
+ }
+ }
+
+ protected void handleViewConf(CoordinationMessage msg, Member sender,Membership merged) throws ChannelException {
+ if ( viewId != null && msg.getId().equals(viewId) ) return;//we already have this view
+ view = new Membership((MemberImpl)getLocalMember(false),AbsoluteOrder.comp,true);
+ Arrays.fill(view,msg.getMembers());
+ viewId = msg.getId();
+
+ if ( viewId.equals(suggestedviewId) ) {
+ suggestedView = null;
+ suggestedviewId = null;
+ }
+
+ if (suggestedView != null && AbsoluteOrder.comp.compare(suggestedView.getMembers()[0],merged.getMembers()[0])<0 ) {
+ suggestedView = null;
+ suggestedviewId = null;
+ }
+
+ viewChange(viewId,view.getMembers());
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_CONF_RX,this,"Accepted View"));
+
+ if ( suggestedviewId == null && hasHigherPriority(merged.getMembers(),membership.getMembers()) ) {
+ startElection(false);
+ }
+ }
+
+ protected boolean isViewConf(CoordinationMessage msg) {
+ return Arrays.contains(msg.getType(),0,COORD_CONF,0,COORD_CONF.length);
+ }
+
+ protected boolean hasHigherPriority(Member[] complete, Member[] local) {
+ if ( local == null || local.length == 0 ) return false;
+ if ( complete == null || complete.length == 0 ) return true;
+ AbsoluteOrder.absoluteOrder(complete);
+ AbsoluteOrder.absoluteOrder(local);
+ return (AbsoluteOrder.comp.compare(complete[0],local[0]) > 0);
+
+ }
+
+
+ /**
+ * Returns coordinator if one is available
+ * @return Member
+ */
+ public Member getCoordinator() {
+ return (view != null && view.hasMembers()) ? view.getMembers()[0] : null;
+ }
+
+ public Member[] getView() {
+ return (view != null && view.hasMembers()) ? view.getMembers() : new Member[0];
+ }
+
+ public UniqueId getViewId() {
+ return viewId;
+ }
+
+ /**
+ * Block in/out messages while a election is going on
+ */
+ protected void halt() {
+
+ }
+
+ /**
+ * Release lock for in/out messages election is completed
+ */
+ protected void release() {
+
+ }
+
+ /**
+ * Wait for an election to end
+ */
+ protected void waitForRelease() {
+
+ }
+
+
+//============================================================================================================
+// OVERRIDDEN METHODS FROM CHANNEL INTERCEPTOR BASE
+//============================================================================================================
+ public void start(int svc) throws ChannelException {
+ if (membership == null) setupMembership();
+ if (started)return;
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START, this, "Before start"));
+ super.start(startsvc);
+ started = true;
+ if (view == null) view = new Membership( (MemberImpl)super.getLocalMember(true), AbsoluteOrder.comp, true);
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START, this, "After start"));
+ startElection(false);
+ }
+
+ public void stop(int svc) throws ChannelException {
+ try {
+ halt();
+ synchronized (electionMutex) {
+ if (!started)return;
+ started = false;
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_STOP, this, "Before stop"));
+ super.stop(startsvc);
+ this.view = null;
+ this.viewId = null;
+ this.suggestedView = null;
+ this.suggestedviewId = null;
+ this.membership.reset();
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_STOP, this, "After stop"));
+ }
+ }finally {
+ release();
+ }
+ }
+
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ waitForRelease();
+ super.sendMessage(destination, msg, payload);
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ if ( Arrays.contains(msg.getMessage().getBytesDirect(),0,COORD_ALIVE,0,COORD_ALIVE.length) ) {
+ //ignore message, its an alive message
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MSG_ARRIVE,this,"Alive Message"));
+
+ } else if ( Arrays.contains(msg.getMessage().getBytesDirect(),0,COORD_HEADER,0,COORD_HEADER.length) ) {
+ try {
+ CoordinationMessage cmsg = new CoordinationMessage(msg.getMessage());
+ Member[] cmbr = cmsg.getMembers();
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MSG_ARRIVE,this,"Coord Msg Arrived("+Arrays.toNameString(cmbr)+")"));
+ processCoordMessage(cmsg, msg.getAddress());
+ }catch ( ChannelException x ) {
+ log.error("Error processing coordination message. Could be fatal.",x);
+ }
+ } else {
+ super.messageReceived(msg);
+ }
+ }
+
+ public boolean accept(ChannelMessage msg) {
+ return super.accept(msg);
+ }
+
+ public void memberAdded(Member member) {
+ memberAdded(member,true);
+ }
+
+ public void memberAdded(Member member,boolean elect) {
+ try {
+ if ( membership == null ) setupMembership();
+ if ( membership.memberAlive((MemberImpl)member) ) super.memberAdded(member);
+ try {
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MBR_ADD,this,"Member add("+member.getName()+")"));
+ if (started && elect) startElection(false);
+ }catch ( ChannelException x ) {
+ log.error("Unable to start election when member was added.",x);
+ }
+ }finally {
+ }
+
+ }
+
+ public void memberDisappeared(Member member) {
+ try {
+
+ membership.removeMember((MemberImpl)member);
+ super.memberDisappeared(member);
+ try {
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MBR_DEL,this,"Member remove("+member.getName()+")"));
+ if ( started && (isCoordinator() || isHighest()) )
+ startElection(true); //to do, if a member disappears, only the coordinator can start
+ }catch ( ChannelException x ) {
+ log.error("Unable to start election when member was removed.",x);
+ }
+ }finally {
+ }
+ }
+
+ public boolean isHighest() {
+ Member local = getLocalMember(false);
+ if ( membership.getMembers().length == 0 ) return true;
+ else return AbsoluteOrder.comp.compare(local,membership.getMembers()[0])<=0;
+ }
+
+ public boolean isCoordinator() {
+ Member coord = getCoordinator();
+ return coord != null && getLocalMember(false).equals(coord);
+ }
+
+ public void heartbeat() {
+ try {
+ MemberImpl local = (MemberImpl)getLocalMember(false);
+ if ( view != null && (Arrays.diff(view,membership,local).length != 0 || Arrays.diff(membership,view,local).length != 0) ) {
+ if ( isHighest() ) {
+ fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START_ELECT, this,
+ "Heartbeat found inconsistency, restart election"));
+ startElection(true);
+ }
+ }
+ } catch ( Exception x ){
+ log.error("Unable to perform heartbeat.",x);
+ } finally {
+ super.heartbeat();
+ }
+ }
+
+ /**
+ * has members
+ */
+ public boolean hasMembers() {
+
+ return membership.hasMembers();
+ }
+
+ /**
+ * Get all current cluster members
+ * @return all members or empty array
+ */
+ public Member[] getMembers() {
+
+ return membership.getMembers();
+ }
+
+ /**
+ *
+ * @param mbr Member
+ * @return Member
+ */
+ public Member getMember(Member mbr) {
+
+ return membership.getMember(mbr);
+ }
+
+ /**
+ * Return the member that represents this node.
+ *
+ * @return Member
+ */
+ public Member getLocalMember(boolean incAlive) {
+ Member local = super.getLocalMember(incAlive);
+ if ( view == null && (local != null)) setupMembership();
+ return local;
+ }
+
+ protected synchronized void setupMembership() {
+ if ( membership == null ) {
+ membership = new Membership((MemberImpl)super.getLocalMember(true),AbsoluteOrder.comp,false);
+ }
+ }
+
+
+//============================================================================================================
+// HELPER CLASSES FOR COORDINATION
+//============================================================================================================
+
+
+
+
+ public static class CoordinationMessage {
+ //X{A-ldr, A-src, mbrs-A,B,C,D}
+ protected XByteBuffer buf;
+ protected MemberImpl leader;
+ protected MemberImpl source;
+ protected MemberImpl[] view;
+ protected UniqueId id;
+ protected byte[] type;
+ protected long timestamp = System.currentTimeMillis();
+
+ public CoordinationMessage(XByteBuffer buf) {
+ this.buf = buf;
+ parse();
+ }
+
+ public CoordinationMessage(MemberImpl leader,
+ MemberImpl source,
+ MemberImpl[] view,
+ UniqueId id,
+ byte[] type) {
+ this.buf = new XByteBuffer(4096,false);
+ this.leader = leader;
+ this.source = source;
+ this.view = view;
+ this.id = id;
+ this.type = type;
+ this.write();
+ }
+
+
+ public byte[] getHeader() {
+ return NonBlockingCoordinator.COORD_HEADER;
+ }
+
+ public MemberImpl getLeader() {
+ if ( leader == null ) parse();
+ return leader;
+ }
+
+ public MemberImpl getSource() {
+ if ( source == null ) parse();
+ return source;
+ }
+
+ public UniqueId getId() {
+ if ( id == null ) parse();
+ return id;
+ }
+
+ public MemberImpl[] getMembers() {
+ if ( view == null ) parse();
+ return view;
+ }
+
+ public byte[] getType() {
+ if (type == null ) parse();
+ return type;
+ }
+
+ public XByteBuffer getBuffer() {
+ return this.buf;
+ }
+
+ public void parse() {
+ //header
+ int offset = 16;
+ //leader
+ int ldrLen = buf.toInt(buf.getBytesDirect(),offset);
+ offset += 4;
+ byte[] ldr = new byte[ldrLen];
+ System.arraycopy(buf.getBytesDirect(),offset,ldr,0,ldrLen);
+ leader = MemberImpl.getMember(ldr);
+ offset += ldrLen;
+ //source
+ int srcLen = buf.toInt(buf.getBytesDirect(),offset);
+ offset += 4;
+ byte[] src = new byte[srcLen];
+ System.arraycopy(buf.getBytesDirect(),offset,src,0,srcLen);
+ source = MemberImpl.getMember(src);
+ offset += srcLen;
+ //view
+ int mbrCount = buf.toInt(buf.getBytesDirect(),offset);
+ offset += 4;
+ view = new MemberImpl[mbrCount];
+ for (int i=0; i<view.length; i++ ) {
+ int mbrLen = buf.toInt(buf.getBytesDirect(),offset);
+ offset += 4;
+ byte[] mbr = new byte[mbrLen];
+ System.arraycopy(buf.getBytesDirect(), offset, mbr, 0, mbrLen);
+ view[i] = MemberImpl.getMember(mbr);
+ offset += mbrLen;
+ }
+ //id
+ this.id = new UniqueId(buf.getBytesDirect(),offset,16);
+ offset += 16;
+ type = new byte[16];
+ System.arraycopy(buf.getBytesDirect(), offset, type, 0, type.length);
+ offset += 16;
+
+ }
+
+ public void write() {
+ buf.reset();
+ //header
+ buf.append(COORD_HEADER,0,COORD_HEADER.length);
+ //leader
+ byte[] ldr = leader.getData(false,false);
+ buf.append(ldr.length);
+ buf.append(ldr,0,ldr.length);
+ ldr = null;
+ //source
+ byte[] src = source.getData(false,false);
+ buf.append(src.length);
+ buf.append(src,0,src.length);
+ src = null;
+ //view
+ buf.append(view.length);
+ for (int i=0; i<view.length; i++ ) {
+ byte[] mbr = view[i].getData(false,false);
+ buf.append(mbr.length);
+ buf.append(mbr,0,mbr.length);
+ }
+ //id
+ buf.append(id.getBytes(),0,id.getBytes().length);
+ buf.append(type,0,type.length);
+ }
+ }
+
+ public void fireInterceptorEvent(InterceptorEvent event) {
+ if (event instanceof CoordinationEvent &&
+ ((CoordinationEvent)event).type == CoordinationEvent.EVT_CONF_RX)
+ log.info(event);
+ }
+
+ public static class CoordinationEvent implements InterceptorEvent {
+ public static final int EVT_START = 1;
+ public static final int EVT_MBR_ADD = 2;
+ public static final int EVT_MBR_DEL = 3;
+ public static final int EVT_START_ELECT = 4;
+ public static final int EVT_PROCESS_ELECT = 5;
+ public static final int EVT_MSG_ARRIVE = 6;
+ public static final int EVT_PRE_MERGE = 7;
+ public static final int EVT_POST_MERGE = 8;
+ public static final int EVT_WAIT_FOR_MSG = 9;
+ public static final int EVT_SEND_MSG = 10;
+ public static final int EVT_STOP = 11;
+ public static final int EVT_CONF_RX = 12;
+ public static final int EVT_ELECT_ABANDONED = 13;
+
+ int type;
+ ChannelInterceptor interceptor;
+ Member coord;
+ Member[] mbrs;
+ String info;
+ Membership view;
+ Membership suggestedView;
+ public CoordinationEvent(int type,ChannelInterceptor interceptor, String info) {
+ this.type = type;
+ this.interceptor = interceptor;
+ this.coord = ((NonBlockingCoordinator)interceptor).getCoordinator();
+ this.mbrs = ((NonBlockingCoordinator)interceptor).membership.getMembers();
+ this.info = info;
+ this.view = ((NonBlockingCoordinator)interceptor).view;
+ this.suggestedView = ((NonBlockingCoordinator)interceptor).suggestedView;
+ }
+
+ public int getEventType() {
+ return type;
+ }
+
+ public String getEventTypeDesc() {
+ switch (type) {
+ case EVT_START: return "EVT_START:"+info;
+ case EVT_MBR_ADD: return "EVT_MBR_ADD:"+info;
+ case EVT_MBR_DEL: return "EVT_MBR_DEL:"+info;
+ case EVT_START_ELECT: return "EVT_START_ELECT:"+info;
+ case EVT_PROCESS_ELECT: return "EVT_PROCESS_ELECT:"+info;
+ case EVT_MSG_ARRIVE: return "EVT_MSG_ARRIVE:"+info;
+ case EVT_PRE_MERGE: return "EVT_PRE_MERGE:"+info;
+ case EVT_POST_MERGE: return "EVT_POST_MERGE:"+info;
+ case EVT_WAIT_FOR_MSG: return "EVT_WAIT_FOR_MSG:"+info;
+ case EVT_SEND_MSG: return "EVT_SEND_MSG:"+info;
+ case EVT_STOP: return "EVT_STOP:"+info;
+ case EVT_CONF_RX: return "EVT_CONF_RX:"+info;
+ case EVT_ELECT_ABANDONED: return "EVT_ELECT_ABANDONED:"+info;
+ default: return "Unknown";
+ }
+ }
+
+ public ChannelInterceptor getInterceptor() {
+ return interceptor;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer("CoordinationEvent[type=");
+ buf.append(type).append("\n\tLocal:");
+ Member local = interceptor.getLocalMember(false);
+ buf.append(local!=null?local.getName():"").append("\n\tCoord:");
+ buf.append(coord!=null?coord.getName():"").append("\n\tView:");
+ buf.append(Arrays.toNameString(view!=null?view.getMembers():null)).append("\n\tSuggested View:");
+ buf.append(Arrays.toNameString(suggestedView!=null?suggestedView.getMembers():null)).append("\n\tMembers:");
+ buf.append(Arrays.toNameString(mbrs)).append("\n\tInfo:");
+ buf.append(info).append("]");
+ return buf.toString();
+ }
+ }
+
+
+
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import java.util.HashMap;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-\r
-\r
-\r
-/**\r
- *\r
- * The order interceptor guarantees that messages are received in the same order they were \r
- * sent.\r
- * This interceptor works best with the ack=true setting. <br>\r
- * There is no point in \r
- * using this with the replicationMode="fastasynchqueue" as this mode guarantees ordering.<BR>\r
- * If you are using the mode ack=false replicationMode=pooled, and have a lot of concurrent threads,\r
- * this interceptor can really slow you down, as many messages will be completely out of order\r
- * and the queue might become rather large. If this is the case, then you might want to set \r
- * the value OrderInterceptor.maxQueue = 25 (meaning that we will never keep more than 25 messages in our queue)\r
- * <br><b>Configuration Options</b><br>\r
- * OrderInteceptor.expire=<milliseconds> - if a message arrives out of order, how long before we act on it <b>default=3000ms</b><br>\r
- * OrderInteceptor.maxQueue=<max queue size> - how much can the queue grow to ensure ordering. \r
- * This setting is useful to avoid OutOfMemoryErrors<b>default=Integer.MAX_VALUE</b><br>\r
- * OrderInterceptor.forwardExpired=<boolean> - this flag tells the interceptor what to \r
- * do when a message has expired or the queue has grown larger than the maxQueue value.\r
- * true means that the message is sent up the stack to the receiver that will receive and out of order message\r
- * false means, forget the message and reset the message counter. <b>default=true</b>\r
- * \r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class OrderInterceptor extends ChannelInterceptorBase {\r
- private HashMap outcounter = new HashMap();\r
- private HashMap incounter = new HashMap();\r
- private HashMap incoming = new HashMap();\r
- private long expire = 3000;\r
- private boolean forwardExpired = true;\r
- private int maxQueue = Integer.MAX_VALUE;\r
-\r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- for ( int i=0; i<destination.length; i++ ) {\r
- int nr = incCounter(destination[i]);\r
- //reduce byte copy\r
- msg.getMessage().append(nr);\r
- try {\r
- getNext().sendMessage(new Member[] {destination[i]}, msg, payload);\r
- }finally {\r
- msg.getMessage().trim(4);\r
- }\r
- }\r
- }\r
-\r
- public void messageReceived(ChannelMessage msg) {\r
- int msgnr = XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);\r
- msg.getMessage().trim(4);\r
- MessageOrder order = new MessageOrder(msgnr,(ChannelMessage)msg.deepclone());\r
- if ( processIncoming(order) ) processLeftOvers(msg.getAddress(),false);\r
- }\r
- \r
- public synchronized void processLeftOvers(Member member, boolean force) {\r
- MessageOrder tmp = (MessageOrder)incoming.get(member);\r
- if ( force ) {\r
- Counter cnt = getInCounter(member);\r
- cnt.setCounter(Integer.MAX_VALUE);\r
- }\r
- if ( tmp!= null ) processIncoming(tmp);\r
- }\r
- /**\r
- * \r
- * @param order MessageOrder\r
- * @return boolean - true if a message expired and was processed\r
- */\r
- public synchronized boolean processIncoming(MessageOrder order) {\r
- boolean result = false;\r
- Member member = order.getMessage().getAddress();\r
- Counter cnt = getInCounter(member);\r
- \r
- MessageOrder tmp = (MessageOrder)incoming.get(member);\r
- if ( tmp != null ) {\r
- order = MessageOrder.add(tmp,order);\r
- }\r
- \r
- \r
- while ( (order!=null) && (order.getMsgNr() <= cnt.getCounter()) ) {\r
- //we are right on target. process orders\r
- if ( order.getMsgNr() == cnt.getCounter() ) cnt.inc();\r
- else if ( order.getMsgNr() > cnt.getCounter() ) cnt.setCounter(order.getMsgNr());\r
- super.messageReceived(order.getMessage());\r
- order.setMessage(null);\r
- order = order.next;\r
- }\r
- MessageOrder head = order;\r
- MessageOrder prev = null;\r
- tmp = order;\r
- //flag to empty out the queue when it larger than maxQueue\r
- boolean empty = order!=null?order.getCount()>=maxQueue:false;\r
- while ( tmp != null ) {\r
- //process expired messages or empty out the queue\r
- if ( tmp.isExpired(expire) || empty ) {\r
- //reset the head\r
- if ( tmp == head ) head = tmp.next;\r
- cnt.setCounter(tmp.getMsgNr()+1);\r
- if ( getForwardExpired() ) super.messageReceived(tmp.getMessage());\r
- tmp.setMessage(null);\r
- tmp = tmp.next;\r
- if ( prev != null ) prev.next = tmp; \r
- result = true;\r
- } else {\r
- prev = tmp;\r
- tmp = tmp.next;\r
- }\r
- }\r
- if ( head == null ) incoming.remove(member);\r
- else incoming.put(member, head);\r
- return result;\r
- }\r
- \r
- public void memberAdded(Member member) {\r
- //notify upwards\r
- getInCounter(member);\r
- getOutCounter(member);\r
- super.memberAdded(member);\r
- }\r
-\r
- public void memberDisappeared(Member member) {\r
- //notify upwards\r
- outcounter.remove(member);\r
- incounter.remove(member);\r
- //clear the remaining queue\r
- processLeftOvers(member,true);\r
- super.memberDisappeared(member);\r
- }\r
- \r
- public int incCounter(Member mbr) { \r
- Counter cnt = getOutCounter(mbr);\r
- return cnt.inc();\r
- }\r
- \r
- public synchronized Counter getInCounter(Member mbr) {\r
- Counter cnt = (Counter)incounter.get(mbr);\r
- if ( cnt == null ) {\r
- cnt = new Counter();\r
- cnt.inc(); //always start at 1 for incoming\r
- incounter.put(mbr,cnt);\r
- }\r
- return cnt;\r
- }\r
-\r
- public synchronized Counter getOutCounter(Member mbr) {\r
- Counter cnt = (Counter)outcounter.get(mbr);\r
- if ( cnt == null ) {\r
- cnt = new Counter();\r
- outcounter.put(mbr,cnt);\r
- }\r
- return cnt;\r
- }\r
-\r
- public static class Counter {\r
- private int value = 0;\r
- \r
- public int getCounter() {\r
- return value;\r
- }\r
- \r
- public synchronized void setCounter(int counter) {\r
- this.value = counter;\r
- }\r
- \r
- public synchronized int inc() {\r
- return ++value;\r
- }\r
- }\r
- \r
- public static class MessageOrder {\r
- private long received = System.currentTimeMillis();\r
- private MessageOrder next;\r
- private int msgNr;\r
- private ChannelMessage msg = null;\r
- public MessageOrder(int msgNr,ChannelMessage msg) {\r
- this.msgNr = msgNr;\r
- this.msg = msg;\r
- }\r
- \r
- public boolean isExpired(long expireTime) {\r
- return (System.currentTimeMillis()-received) > expireTime;\r
- }\r
- \r
- public ChannelMessage getMessage() {\r
- return msg;\r
- }\r
- \r
- public void setMessage(ChannelMessage msg) {\r
- this.msg = msg;\r
- }\r
- \r
- public void setNext(MessageOrder order) {\r
- this.next = order;\r
- }\r
- public MessageOrder getNext() {\r
- return next;\r
- }\r
- \r
- public int getCount() {\r
- int counter = 1;\r
- MessageOrder tmp = next;\r
- while ( tmp != null ) {\r
- counter++;\r
- tmp = tmp.next;\r
- }\r
- return counter;\r
- }\r
- \r
- public static MessageOrder add(MessageOrder head, MessageOrder add) {\r
- if ( head == null ) return add;\r
- if ( add == null ) return head;\r
- if ( head == add ) return add;\r
-\r
- if ( head.getMsgNr() > add.getMsgNr() ) {\r
- add.next = head;\r
- return add;\r
- }\r
- \r
- MessageOrder iter = head;\r
- MessageOrder prev = null;\r
- while ( iter.getMsgNr() < add.getMsgNr() && (iter.next !=null ) ) {\r
- prev = iter;\r
- iter = iter.next;\r
- }\r
- if ( iter.getMsgNr() < add.getMsgNr() ) {\r
- //add after\r
- add.next = iter.next;\r
- iter.next = add;\r
- } else if (iter.getMsgNr() > add.getMsgNr()) {\r
- //add before\r
- prev.next = add;\r
- add.next = iter;\r
- \r
- } else {\r
- throw new ArithmeticException("Message added has the same counter, synchronization bug. Disable the order interceptor");\r
- }\r
- \r
- return head;\r
- }\r
- \r
- public int getMsgNr() {\r
- return msgNr;\r
- }\r
- \r
- \r
- \r
- }\r
-\r
- public void setExpire(long expire) {\r
- this.expire = expire;\r
- }\r
-\r
- public void setForwardExpired(boolean forwardExpired) {\r
- this.forwardExpired = forwardExpired;\r
- }\r
-\r
- public void setMaxQueue(int maxQueue) {\r
- this.maxQueue = maxQueue;\r
- }\r
-\r
- public long getExpire() {\r
- return expire;\r
- }\r
-\r
- public boolean getForwardExpired() {\r
- return forwardExpired;\r
- }\r
-\r
- public int getMaxQueue() {\r
- return maxQueue;\r
- }\r
-\r
-}\r
+/*
+ * 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
+ */
+
+package org.apache.catalina.tribes.group.interceptors;
+
+import java.util.HashMap;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.io.XByteBuffer;
+
+
+
+/**
+ *
+ * The order interceptor guarantees that messages are received in the same order they were
+ * sent.
+ * This interceptor works best with the ack=true setting. <br>
+ * There is no point in
+ * using this with the replicationMode="fastasynchqueue" as this mode guarantees ordering.<BR>
+ * If you are using the mode ack=false replicationMode=pooled, and have a lot of concurrent threads,
+ * this interceptor can really slow you down, as many messages will be completely out of order
+ * and the queue might become rather large. If this is the case, then you might want to set
+ * the value OrderInterceptor.maxQueue = 25 (meaning that we will never keep more than 25 messages in our queue)
+ * <br><b>Configuration Options</b><br>
+ * OrderInteceptor.expire=<milliseconds> - if a message arrives out of order, how long before we act on it <b>default=3000ms</b><br>
+ * OrderInteceptor.maxQueue=<max queue size> - how much can the queue grow to ensure ordering.
+ * This setting is useful to avoid OutOfMemoryErrors<b>default=Integer.MAX_VALUE</b><br>
+ * OrderInterceptor.forwardExpired=<boolean> - this flag tells the interceptor what to
+ * do when a message has expired or the queue has grown larger than the maxQueue value.
+ * true means that the message is sent up the stack to the receiver that will receive and out of order message
+ * false means, forget the message and reset the message counter. <b>default=true</b>
+ *
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class OrderInterceptor extends ChannelInterceptorBase {
+ private HashMap outcounter = new HashMap();
+ private HashMap incounter = new HashMap();
+ private HashMap incoming = new HashMap();
+ private long expire = 3000;
+ private boolean forwardExpired = true;
+ private int maxQueue = Integer.MAX_VALUE;
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ for ( int i=0; i<destination.length; i++ ) {
+ int nr = incCounter(destination[i]);
+ //reduce byte copy
+ msg.getMessage().append(nr);
+ try {
+ getNext().sendMessage(new Member[] {destination[i]}, msg, payload);
+ }finally {
+ msg.getMessage().trim(4);
+ }
+ }
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ int msgnr = XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);
+ msg.getMessage().trim(4);
+ MessageOrder order = new MessageOrder(msgnr,(ChannelMessage)msg.deepclone());
+ if ( processIncoming(order) ) processLeftOvers(msg.getAddress(),false);
+ }
+
+ public synchronized void processLeftOvers(Member member, boolean force) {
+ MessageOrder tmp = (MessageOrder)incoming.get(member);
+ if ( force ) {
+ Counter cnt = getInCounter(member);
+ cnt.setCounter(Integer.MAX_VALUE);
+ }
+ if ( tmp!= null ) processIncoming(tmp);
+ }
+ /**
+ *
+ * @param order MessageOrder
+ * @return boolean - true if a message expired and was processed
+ */
+ public synchronized boolean processIncoming(MessageOrder order) {
+ boolean result = false;
+ Member member = order.getMessage().getAddress();
+ Counter cnt = getInCounter(member);
+
+ MessageOrder tmp = (MessageOrder)incoming.get(member);
+ if ( tmp != null ) {
+ order = MessageOrder.add(tmp,order);
+ }
+
+
+ while ( (order!=null) && (order.getMsgNr() <= cnt.getCounter()) ) {
+ //we are right on target. process orders
+ if ( order.getMsgNr() == cnt.getCounter() ) cnt.inc();
+ else if ( order.getMsgNr() > cnt.getCounter() ) cnt.setCounter(order.getMsgNr());
+ super.messageReceived(order.getMessage());
+ order.setMessage(null);
+ order = order.next;
+ }
+ MessageOrder head = order;
+ MessageOrder prev = null;
+ tmp = order;
+ //flag to empty out the queue when it larger than maxQueue
+ boolean empty = order!=null?order.getCount()>=maxQueue:false;
+ while ( tmp != null ) {
+ //process expired messages or empty out the queue
+ if ( tmp.isExpired(expire) || empty ) {
+ //reset the head
+ if ( tmp == head ) head = tmp.next;
+ cnt.setCounter(tmp.getMsgNr()+1);
+ if ( getForwardExpired() ) super.messageReceived(tmp.getMessage());
+ tmp.setMessage(null);
+ tmp = tmp.next;
+ if ( prev != null ) prev.next = tmp;
+ result = true;
+ } else {
+ prev = tmp;
+ tmp = tmp.next;
+ }
+ }
+ if ( head == null ) incoming.remove(member);
+ else incoming.put(member, head);
+ return result;
+ }
+
+ public void memberAdded(Member member) {
+ //notify upwards
+ getInCounter(member);
+ getOutCounter(member);
+ super.memberAdded(member);
+ }
+
+ public void memberDisappeared(Member member) {
+ //notify upwards
+ outcounter.remove(member);
+ incounter.remove(member);
+ //clear the remaining queue
+ processLeftOvers(member,true);
+ super.memberDisappeared(member);
+ }
+
+ public int incCounter(Member mbr) {
+ Counter cnt = getOutCounter(mbr);
+ return cnt.inc();
+ }
+
+ public synchronized Counter getInCounter(Member mbr) {
+ Counter cnt = (Counter)incounter.get(mbr);
+ if ( cnt == null ) {
+ cnt = new Counter();
+ cnt.inc(); //always start at 1 for incoming
+ incounter.put(mbr,cnt);
+ }
+ return cnt;
+ }
+
+ public synchronized Counter getOutCounter(Member mbr) {
+ Counter cnt = (Counter)outcounter.get(mbr);
+ if ( cnt == null ) {
+ cnt = new Counter();
+ outcounter.put(mbr,cnt);
+ }
+ return cnt;
+ }
+
+ public static class Counter {
+ private int value = 0;
+
+ public int getCounter() {
+ return value;
+ }
+
+ public synchronized void setCounter(int counter) {
+ this.value = counter;
+ }
+
+ public synchronized int inc() {
+ return ++value;
+ }
+ }
+
+ public static class MessageOrder {
+ private long received = System.currentTimeMillis();
+ private MessageOrder next;
+ private int msgNr;
+ private ChannelMessage msg = null;
+ public MessageOrder(int msgNr,ChannelMessage msg) {
+ this.msgNr = msgNr;
+ this.msg = msg;
+ }
+
+ public boolean isExpired(long expireTime) {
+ return (System.currentTimeMillis()-received) > expireTime;
+ }
+
+ public ChannelMessage getMessage() {
+ return msg;
+ }
+
+ public void setMessage(ChannelMessage msg) {
+ this.msg = msg;
+ }
+
+ public void setNext(MessageOrder order) {
+ this.next = order;
+ }
+ public MessageOrder getNext() {
+ return next;
+ }
+
+ public int getCount() {
+ int counter = 1;
+ MessageOrder tmp = next;
+ while ( tmp != null ) {
+ counter++;
+ tmp = tmp.next;
+ }
+ return counter;
+ }
+
+ public static MessageOrder add(MessageOrder head, MessageOrder add) {
+ if ( head == null ) return add;
+ if ( add == null ) return head;
+ if ( head == add ) return add;
+
+ if ( head.getMsgNr() > add.getMsgNr() ) {
+ add.next = head;
+ return add;
+ }
+
+ MessageOrder iter = head;
+ MessageOrder prev = null;
+ while ( iter.getMsgNr() < add.getMsgNr() && (iter.next !=null ) ) {
+ prev = iter;
+ iter = iter.next;
+ }
+ if ( iter.getMsgNr() < add.getMsgNr() ) {
+ //add after
+ add.next = iter.next;
+ iter.next = add;
+ } else if (iter.getMsgNr() > add.getMsgNr()) {
+ //add before
+ prev.next = add;
+ add.next = iter;
+
+ } else {
+ throw new ArithmeticException("Message added has the same counter, synchronization bug. Disable the order interceptor");
+ }
+
+ return head;
+ }
+
+ public int getMsgNr() {
+ return msgNr;
+ }
+
+
+
+ }
+
+ public void setExpire(long expire) {
+ this.expire = expire;
+ }
+
+ public void setForwardExpired(boolean forwardExpired) {
+ this.forwardExpired = forwardExpired;
+ }
+
+ public void setMaxQueue(int maxQueue) {
+ this.maxQueue = maxQueue;
+ }
+
+ public long getExpire() {
+ return expire;
+ }
+
+ public boolean getForwardExpired() {
+ return forwardExpired;
+ }
+
+ public int getMaxQueue() {
+ return maxQueue;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import java.net.InetAddress;\r
-import java.net.InetSocketAddress;\r
-import java.net.Socket;\r
-import java.net.SocketTimeoutException;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelException.FaultyMember;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.RemoteProcessException;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-import org.apache.catalina.tribes.io.ChannelData;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-import org.apache.catalina.tribes.membership.MemberImpl;\r
-import org.apache.catalina.tribes.membership.Membership;\r
-import java.net.ConnectException;\r
-\r
-/**\r
- * <p>Title: A perfect failure detector </p>\r
- *\r
- * <p>Description: The TcpFailureDetector is a useful interceptor\r
- * that adds reliability to the membership layer.</p>\r
- * <p>\r
- * If the network is busy, or the system is busy so that the membership receiver thread\r
- * is not getting enough time to update its table, members can be "timed out"\r
- * This failure detector will intercept the memberDisappeared message(unless its a true shutdown message)\r
- * and connect to the member using TCP.\r
- * </p>\r
- * <p>\r
- * The TcpFailureDetector works in two ways. <br>\r
- * 1. It intercepts memberDisappeared events\r
- * 2. It catches send errors \r
- * </p>\r
- *\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class TcpFailureDetector extends ChannelInterceptorBase {\r
- \r
- private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( TcpFailureDetector.class );\r
- \r
- protected static byte[] TCP_FAIL_DETECT = new byte[] {\r
- 79, -89, 115, 72, 121, -126, 67, -55, -97, 111, -119, -128, -95, 91, 7, 20,\r
- 125, -39, 82, 91, -21, -15, 67, -102, -73, 126, -66, -113, -127, 103, 30, -74,\r
- 55, 21, -66, -121, 69, 126, 76, -88, -65, 10, 77, 19, 83, 56, 21, 50,\r
- 85, -10, -108, -73, 58, -6, 64, 120, -111, 4, 125, -41, 114, -124, -64, -43}; \r
- \r
- protected boolean performConnectTest = true;\r
-\r
- protected long connectTimeout = 1000;//1 second default\r
- \r
- protected boolean performSendTest = true;\r
-\r
- protected boolean performReadTest = false;\r
- \r
- protected long readTestTimeout = 5000;//5 seconds\r
- \r
- protected Membership membership = null;\r
- \r
- protected HashMap removeSuspects = new HashMap();\r
- \r
- protected HashMap addSuspects = new HashMap();\r
- \r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- try {\r
- super.sendMessage(destination, msg, payload);\r
- }catch ( ChannelException cx ) {\r
- FaultyMember[] mbrs = cx.getFaultyMembers();\r
- for ( int i=0; i<mbrs.length; i++ ) {\r
- if ( mbrs[i].getCause()!=null && \r
- (!(mbrs[i].getCause() instanceof RemoteProcessException)) ) {//RemoteProcessException's are ok\r
- this.memberDisappeared(mbrs[i].getMember());\r
- }//end if\r
- }//for\r
- throw cx;\r
- }\r
- }\r
-\r
- public void messageReceived(ChannelMessage msg) {\r
- //catch incoming \r
- boolean process = true;\r
- if ( okToProcess(msg.getOptions()) ) {\r
- //check to see if it is a testMessage, if so, process = false\r
- process = ( (msg.getMessage().getLength() != TCP_FAIL_DETECT.length) ||\r
- (!Arrays.equals(TCP_FAIL_DETECT,msg.getMessage().getBytes()) ) );\r
- }//end if\r
- \r
- //ignore the message, it doesnt have the flag set\r
- if ( process ) super.messageReceived(msg);\r
- else if ( log.isDebugEnabled() ) log.debug("Received a failure detector packet:"+msg);\r
- }//messageReceived\r
- \r
- \r
- public void memberAdded(Member member) {\r
- if ( membership == null ) setupMembership();\r
- boolean notify = false;\r
- synchronized (membership) {\r
- if (removeSuspects.containsKey(member)) {\r
- //previously marked suspect, system below picked up the member again\r
- removeSuspects.remove(member);\r
- } else if (membership.getMember( (MemberImpl) member) == null){\r
- //if we add it here, then add it upwards too\r
- //check to see if it is alive\r
- if (memberAlive(member)) {\r
- membership.memberAlive( (MemberImpl) member);\r
- notify = true;\r
- } else {\r
- addSuspects.put(member, new Long(System.currentTimeMillis()));\r
- }\r
- }\r
- }\r
- if ( notify ) super.memberAdded(member);\r
- }\r
-\r
- public void memberDisappeared(Member member) {\r
- if ( membership == null ) setupMembership();\r
- boolean notify = false;\r
- boolean shutdown = Arrays.equals(member.getCommand(),Member.SHUTDOWN_PAYLOAD);\r
- if ( !shutdown ) log.info("Received memberDisappeared["+member+"] message. Will verify.");\r
- synchronized (membership) {\r
- //check to see if the member really is gone\r
- //if the payload is not a shutdown message\r
- if (shutdown || !memberAlive(member)) {\r
- //not correct, we need to maintain the map\r
- membership.removeMember( (MemberImpl) member);\r
- removeSuspects.remove(member);\r
- notify = true;\r
- } else {\r
- //add the member as suspect\r
- removeSuspects.put(member, new Long(System.currentTimeMillis()));\r
- }\r
- }\r
- if ( notify ) {\r
- log.info("Verification complete. Member disappeared["+member+"]");\r
- super.memberDisappeared(member);\r
- } else {\r
- log.info("Verification complete. Member still alive["+member+"]");\r
-\r
- }\r
- }\r
- \r
- public boolean hasMembers() {\r
- if ( membership == null ) setupMembership();\r
- return membership.hasMembers();\r
- }\r
-\r
- public Member[] getMembers() {\r
- if ( membership == null ) setupMembership();\r
- return membership.getMembers();\r
- }\r
-\r
- public Member getMember(Member mbr) {\r
- if ( membership == null ) setupMembership();\r
- return membership.getMember(mbr);\r
- }\r
-\r
- public Member getLocalMember(boolean incAlive) {\r
- return super.getLocalMember(incAlive);\r
- }\r
- \r
- public void heartbeat() {\r
- try {\r
- if (membership == null) setupMembership();\r
- synchronized (membership) {\r
- //update all alive times\r
- Member[] members = super.getMembers();\r
- for (int i = 0; members != null && i < members.length; i++) {\r
- if (membership.memberAlive( (MemberImpl) members[i])) {\r
- //we don't have this one in our membership, check to see if he/she is alive\r
- if (memberAlive(members[i])) {\r
- log.warn("Member added, even though we werent notified:" + members[i]);\r
- super.memberAdded(members[i]);\r
- } else {\r
- membership.removeMember( (MemberImpl) members[i]);\r
- } //end if\r
- } //end if\r
- } //for\r
-\r
- //check suspect members if they are still alive,\r
- //if not, simply issue the memberDisappeared message\r
- MemberImpl[] keys = (MemberImpl[]) removeSuspects.keySet().toArray(new MemberImpl[removeSuspects.size()]);\r
- for (int i = 0; i < keys.length; i++) {\r
- MemberImpl m = (MemberImpl) keys[i];\r
- if (membership.getMember(m) != null && (!memberAlive(m))) {\r
- membership.removeMember(m);\r
- super.memberDisappeared(m);\r
- removeSuspects.remove(m);\r
- log.info("Suspect member, confirmed dead.["+m+"]");\r
- } //end if\r
- }\r
-\r
- //check add suspects members if they are alive now,\r
- //if they are, simply issue the memberAdded message\r
- keys = (MemberImpl[]) addSuspects.keySet().toArray(new MemberImpl[addSuspects.size()]);\r
- for (int i = 0; i < keys.length; i++) {\r
- MemberImpl m = (MemberImpl) keys[i];\r
- if ( membership.getMember(m) == null && (memberAlive(m))) {\r
- membership.memberAlive(m);\r
- super.memberAdded(m);\r
- addSuspects.remove(m);\r
- log.info("Suspect member, confirmed alive.["+m+"]");\r
- } //end if\r
- }\r
- }\r
- }catch ( Exception x ) {\r
- log.warn("Unable to perform heartbeat on the TcpFailureDetector.",x);\r
- } finally {\r
- super.heartbeat();\r
- }\r
- }\r
- \r
- protected synchronized void setupMembership() {\r
- if ( membership == null ) {\r
- membership = new Membership((MemberImpl)super.getLocalMember(true));\r
- }\r
- \r
- }\r
- \r
- protected boolean memberAlive(Member mbr) {\r
- return memberAlive(mbr,TCP_FAIL_DETECT,performSendTest,performReadTest,readTestTimeout,connectTimeout,getOptionFlag());\r
- }\r
- \r
- protected static boolean memberAlive(Member mbr, byte[] msgData, \r
- boolean sendTest, boolean readTest,\r
- long readTimeout, long conTimeout,\r
- int optionFlag) {\r
- //could be a shutdown notification\r
- if ( Arrays.equals(mbr.getCommand(),Member.SHUTDOWN_PAYLOAD) ) return false;\r
- \r
- Socket socket = new Socket(); \r
- try {\r
- InetAddress ia = InetAddress.getByAddress(mbr.getHost());\r
- InetSocketAddress addr = new InetSocketAddress(ia, mbr.getPort());\r
- socket.setSoTimeout((int)readTimeout);\r
- socket.connect(addr, (int) conTimeout);\r
- if ( sendTest ) {\r
- ChannelData data = new ChannelData(true);\r
- data.setAddress(mbr);\r
- data.setMessage(new XByteBuffer(msgData,false));\r
- data.setTimestamp(System.currentTimeMillis());\r
- int options = optionFlag | Channel.SEND_OPTIONS_BYTE_MESSAGE;\r
- if ( readTest ) options = (options | Channel.SEND_OPTIONS_USE_ACK);\r
- else options = (options & (~Channel.SEND_OPTIONS_USE_ACK));\r
- data.setOptions(options);\r
- byte[] message = XByteBuffer.createDataPackage(data);\r
- socket.getOutputStream().write(message);\r
- if ( readTest ) {\r
- int length = socket.getInputStream().read(message);\r
- return length > 0;\r
- }\r
- }//end if\r
- return true;\r
- } catch ( SocketTimeoutException sx) {\r
- //do nothing, we couldn't connect\r
- } catch ( ConnectException cx) {\r
- //do nothing, we couldn't connect\r
- }catch (Exception x ) {\r
- log.error("Unable to perform failure detection check, assuming member down.",x);\r
- } finally {\r
- try {socket.close(); } catch ( Exception ignore ){}\r
- }\r
- return false;\r
- }\r
-\r
-\r
-\r
- \r
+/*
+ * 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
+ */
+package org.apache.catalina.tribes.group.interceptors;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelException.FaultyMember;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.RemoteProcessException;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.io.ChannelData;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.catalina.tribes.membership.MemberImpl;
+import org.apache.catalina.tribes.membership.Membership;
+import java.net.ConnectException;
+
+/**
+ * <p>Title: A perfect failure detector </p>
+ *
+ * <p>Description: The TcpFailureDetector is a useful interceptor
+ * that adds reliability to the membership layer.</p>
+ * <p>
+ * If the network is busy, or the system is busy so that the membership receiver thread
+ * is not getting enough time to update its table, members can be "timed out"
+ * This failure detector will intercept the memberDisappeared message(unless its a true shutdown message)
+ * and connect to the member using TCP.
+ * </p>
+ * <p>
+ * The TcpFailureDetector works in two ways. <br>
+ * 1. It intercepts memberDisappeared events
+ * 2. It catches send errors
+ * </p>
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class TcpFailureDetector extends ChannelInterceptorBase {
+
+ private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( TcpFailureDetector.class );
+
+ protected static byte[] TCP_FAIL_DETECT = new byte[] {
+ 79, -89, 115, 72, 121, -126, 67, -55, -97, 111, -119, -128, -95, 91, 7, 20,
+ 125, -39, 82, 91, -21, -15, 67, -102, -73, 126, -66, -113, -127, 103, 30, -74,
+ 55, 21, -66, -121, 69, 126, 76, -88, -65, 10, 77, 19, 83, 56, 21, 50,
+ 85, -10, -108, -73, 58, -6, 64, 120, -111, 4, 125, -41, 114, -124, -64, -43};
+
+ protected boolean performConnectTest = true;
+
+ protected long connectTimeout = 1000;//1 second default
+
+ protected boolean performSendTest = true;
+
+ protected boolean performReadTest = false;
+
+ protected long readTestTimeout = 5000;//5 seconds
+
+ protected Membership membership = null;
+
+ protected HashMap removeSuspects = new HashMap();
+
+ protected HashMap addSuspects = new HashMap();
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ try {
+ super.sendMessage(destination, msg, payload);
+ }catch ( ChannelException cx ) {
+ FaultyMember[] mbrs = cx.getFaultyMembers();
+ for ( int i=0; i<mbrs.length; i++ ) {
+ if ( mbrs[i].getCause()!=null &&
+ (!(mbrs[i].getCause() instanceof RemoteProcessException)) ) {//RemoteProcessException's are ok
+ this.memberDisappeared(mbrs[i].getMember());
+ }//end if
+ }//for
+ throw cx;
+ }
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ //catch incoming
+ boolean process = true;
+ if ( okToProcess(msg.getOptions()) ) {
+ //check to see if it is a testMessage, if so, process = false
+ process = ( (msg.getMessage().getLength() != TCP_FAIL_DETECT.length) ||
+ (!Arrays.equals(TCP_FAIL_DETECT,msg.getMessage().getBytes()) ) );
+ }//end if
+
+ //ignore the message, it doesnt have the flag set
+ if ( process ) super.messageReceived(msg);
+ else if ( log.isDebugEnabled() ) log.debug("Received a failure detector packet:"+msg);
+ }//messageReceived
+
+
+ public void memberAdded(Member member) {
+ if ( membership == null ) setupMembership();
+ boolean notify = false;
+ synchronized (membership) {
+ if (removeSuspects.containsKey(member)) {
+ //previously marked suspect, system below picked up the member again
+ removeSuspects.remove(member);
+ } else if (membership.getMember( (MemberImpl) member) == null){
+ //if we add it here, then add it upwards too
+ //check to see if it is alive
+ if (memberAlive(member)) {
+ membership.memberAlive( (MemberImpl) member);
+ notify = true;
+ } else {
+ addSuspects.put(member, new Long(System.currentTimeMillis()));
+ }
+ }
+ }
+ if ( notify ) super.memberAdded(member);
+ }
+
+ public void memberDisappeared(Member member) {
+ if ( membership == null ) setupMembership();
+ boolean notify = false;
+ boolean shutdown = Arrays.equals(member.getCommand(),Member.SHUTDOWN_PAYLOAD);
+ if ( !shutdown ) log.info("Received memberDisappeared["+member+"] message. Will verify.");
+ synchronized (membership) {
+ //check to see if the member really is gone
+ //if the payload is not a shutdown message
+ if (shutdown || !memberAlive(member)) {
+ //not correct, we need to maintain the map
+ membership.removeMember( (MemberImpl) member);
+ removeSuspects.remove(member);
+ notify = true;
+ } else {
+ //add the member as suspect
+ removeSuspects.put(member, new Long(System.currentTimeMillis()));
+ }
+ }
+ if ( notify ) {
+ log.info("Verification complete. Member disappeared["+member+"]");
+ super.memberDisappeared(member);
+ } else {
+ log.info("Verification complete. Member still alive["+member+"]");
+
+ }
+ }
+
+ public boolean hasMembers() {
+ if ( membership == null ) setupMembership();
+ return membership.hasMembers();
+ }
+
+ public Member[] getMembers() {
+ if ( membership == null ) setupMembership();
+ return membership.getMembers();
+ }
+
+ public Member getMember(Member mbr) {
+ if ( membership == null ) setupMembership();
+ return membership.getMember(mbr);
+ }
+
+ public Member getLocalMember(boolean incAlive) {
+ return super.getLocalMember(incAlive);
+ }
+
+ public void heartbeat() {
+ try {
+ if (membership == null) setupMembership();
+ synchronized (membership) {
+ //update all alive times
+ Member[] members = super.getMembers();
+ for (int i = 0; members != null && i < members.length; i++) {
+ if (membership.memberAlive( (MemberImpl) members[i])) {
+ //we don't have this one in our membership, check to see if he/she is alive
+ if (memberAlive(members[i])) {
+ log.warn("Member added, even though we werent notified:" + members[i]);
+ super.memberAdded(members[i]);
+ } else {
+ membership.removeMember( (MemberImpl) members[i]);
+ } //end if
+ } //end if
+ } //for
+
+ //check suspect members if they are still alive,
+ //if not, simply issue the memberDisappeared message
+ MemberImpl[] keys = (MemberImpl[]) removeSuspects.keySet().toArray(new MemberImpl[removeSuspects.size()]);
+ for (int i = 0; i < keys.length; i++) {
+ MemberImpl m = (MemberImpl) keys[i];
+ if (membership.getMember(m) != null && (!memberAlive(m))) {
+ membership.removeMember(m);
+ super.memberDisappeared(m);
+ removeSuspects.remove(m);
+ log.info("Suspect member, confirmed dead.["+m+"]");
+ } //end if
+ }
+
+ //check add suspects members if they are alive now,
+ //if they are, simply issue the memberAdded message
+ keys = (MemberImpl[]) addSuspects.keySet().toArray(new MemberImpl[addSuspects.size()]);
+ for (int i = 0; i < keys.length; i++) {
+ MemberImpl m = (MemberImpl) keys[i];
+ if ( membership.getMember(m) == null && (memberAlive(m))) {
+ membership.memberAlive(m);
+ super.memberAdded(m);
+ addSuspects.remove(m);
+ log.info("Suspect member, confirmed alive.["+m+"]");
+ } //end if
+ }
+ }
+ }catch ( Exception x ) {
+ log.warn("Unable to perform heartbeat on the TcpFailureDetector.",x);
+ } finally {
+ super.heartbeat();
+ }
+ }
+
+ protected synchronized void setupMembership() {
+ if ( membership == null ) {
+ membership = new Membership((MemberImpl)super.getLocalMember(true));
+ }
+
+ }
+
+ protected boolean memberAlive(Member mbr) {
+ return memberAlive(mbr,TCP_FAIL_DETECT,performSendTest,performReadTest,readTestTimeout,connectTimeout,getOptionFlag());
+ }
+
+ protected static boolean memberAlive(Member mbr, byte[] msgData,
+ boolean sendTest, boolean readTest,
+ long readTimeout, long conTimeout,
+ int optionFlag) {
+ //could be a shutdown notification
+ if ( Arrays.equals(mbr.getCommand(),Member.SHUTDOWN_PAYLOAD) ) return false;
+
+ Socket socket = new Socket();
+ try {
+ InetAddress ia = InetAddress.getByAddress(mbr.getHost());
+ InetSocketAddress addr = new InetSocketAddress(ia, mbr.getPort());
+ socket.setSoTimeout((int)readTimeout);
+ socket.connect(addr, (int) conTimeout);
+ if ( sendTest ) {
+ ChannelData data = new ChannelData(true);
+ data.setAddress(mbr);
+ data.setMessage(new XByteBuffer(msgData,false));
+ data.setTimestamp(System.currentTimeMillis());
+ int options = optionFlag | Channel.SEND_OPTIONS_BYTE_MESSAGE;
+ if ( readTest ) options = (options | Channel.SEND_OPTIONS_USE_ACK);
+ else options = (options & (~Channel.SEND_OPTIONS_USE_ACK));
+ data.setOptions(options);
+ byte[] message = XByteBuffer.createDataPackage(data);
+ socket.getOutputStream().write(message);
+ if ( readTest ) {
+ int length = socket.getInputStream().read(message);
+ return length > 0;
+ }
+ }//end if
+ return true;
+ } catch ( SocketTimeoutException sx) {
+ //do nothing, we couldn't connect
+ } catch ( ConnectException cx) {
+ //do nothing, we couldn't connect
+ }catch (Exception x ) {
+ log.error("Unable to perform failure detection check, assuming member down.",x);
+ } finally {
+ try {socket.close(); } catch ( Exception ignore ){}
+ }
+ return false;
+ }
+
+
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- */\r
-\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-import org.apache.catalina.tribes.io.ChannelData;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-import java.text.DecimalFormat;\r
-import org.apache.catalina.tribes.membership.MemberImpl;\r
-import java.util.concurrent.atomic.AtomicInteger;\r
-import java.util.concurrent.atomic.AtomicLong;\r
-\r
-\r
-\r
-/**\r
- *\r
- *\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class ThroughputInterceptor extends ChannelInterceptorBase {\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ThroughputInterceptor.class);\r
-\r
- double mbTx = 0;\r
- double mbAppTx = 0;\r
- double mbRx = 0;\r
- double timeTx = 0;\r
- double lastCnt = 0;\r
- AtomicLong msgTxCnt = new AtomicLong(1);\r
- AtomicLong msgRxCnt = new AtomicLong(0);\r
- AtomicLong msgTxErr = new AtomicLong(0);\r
- int interval = 10000;\r
- AtomicInteger access = new AtomicInteger(0);\r
- long txStart = 0;\r
- long rxStart = 0;\r
- DecimalFormat df = new DecimalFormat("#0.00");\r
-\r
-\r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {\r
- if ( access.addAndGet(1) == 1 ) txStart = System.currentTimeMillis();\r
- long bytes = XByteBuffer.getDataPackageLength(((ChannelData)msg).getDataPackageLength());\r
- try {\r
- super.sendMessage(destination, msg, payload);\r
- }catch ( ChannelException x ) {\r
- msgTxErr.addAndGet(1);\r
- access.addAndGet(-1);\r
- throw x;\r
- } \r
- mbTx += ((double)(bytes*destination.length))/(1024d*1024d);\r
- mbAppTx += ((double)(bytes))/(1024d*1024d);\r
- if ( access.addAndGet(-1) == 0 ) {\r
- long stop = System.currentTimeMillis();\r
- timeTx += ( (double) (stop - txStart)) / 1000d;\r
- if ((msgTxCnt.get() / interval) >= lastCnt) {\r
- lastCnt++;\r
- report(timeTx);\r
- }\r
- }\r
- msgTxCnt.addAndGet(1);\r
- }\r
-\r
- public void messageReceived(ChannelMessage msg) {\r
- if ( rxStart == 0 ) rxStart = System.currentTimeMillis();\r
- long bytes = XByteBuffer.getDataPackageLength(((ChannelData)msg).getDataPackageLength());\r
- mbRx += ((double)bytes)/(1024d*1024d);\r
- msgRxCnt.addAndGet(1);\r
- if ( msgRxCnt.get() % interval == 0 ) report(timeTx);\r
- super.messageReceived(msg);\r
- \r
- }\r
- \r
- public void report(double timeTx) {\r
- StringBuffer buf = new StringBuffer("ThroughputInterceptor Report[\n\tTx Msg:");\r
- buf.append(msgTxCnt).append(" messages\n\tSent:");\r
- buf.append(df.format(mbTx));\r
- buf.append(" MB (total)\n\tSent:");\r
- buf.append(df.format(mbAppTx));\r
- buf.append(" MB (application)\n\tTime:");\r
- buf.append(df.format(timeTx));\r
- buf.append(" seconds\n\tTx Speed:");\r
- buf.append(df.format(mbTx/timeTx));\r
- buf.append(" MB/sec (total)\n\tTxSpeed:");\r
- buf.append(df.format(mbAppTx/timeTx));\r
- buf.append(" MB/sec (application)\n\tError Msg:");\r
- buf.append(msgTxErr).append("\n\tRx Msg:");\r
- buf.append(msgRxCnt);\r
- buf.append(" messages\n\tRx Speed:");\r
- buf.append(df.format(mbRx/((double)((System.currentTimeMillis()-rxStart)/1000))));\r
- buf.append(" MB/sec (since 1st msg)\n\tReceived:");\r
- buf.append(df.format(mbRx)).append(" MB]\n");\r
- if ( log.isInfoEnabled() ) log.info(buf);\r
- }\r
- \r
- public void setInterval(int interval) {\r
- this.interval = interval;\r
- }\r
-\r
- public int getInterval() {\r
- return interval;\r
- }\r
-\r
-}\r
+/*
+ * 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
+ */
+
+package org.apache.catalina.tribes.group.interceptors;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.io.ChannelData;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import java.text.DecimalFormat;
+import org.apache.catalina.tribes.membership.MemberImpl;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+
+
+/**
+ *
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class ThroughputInterceptor extends ChannelInterceptorBase {
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ThroughputInterceptor.class);
+
+ double mbTx = 0;
+ double mbAppTx = 0;
+ double mbRx = 0;
+ double timeTx = 0;
+ double lastCnt = 0;
+ AtomicLong msgTxCnt = new AtomicLong(1);
+ AtomicLong msgRxCnt = new AtomicLong(0);
+ AtomicLong msgTxErr = new AtomicLong(0);
+ int interval = 10000;
+ AtomicInteger access = new AtomicInteger(0);
+ long txStart = 0;
+ long rxStart = 0;
+ DecimalFormat df = new DecimalFormat("#0.00");
+
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws ChannelException {
+ if ( access.addAndGet(1) == 1 ) txStart = System.currentTimeMillis();
+ long bytes = XByteBuffer.getDataPackageLength(((ChannelData)msg).getDataPackageLength());
+ try {
+ super.sendMessage(destination, msg, payload);
+ }catch ( ChannelException x ) {
+ msgTxErr.addAndGet(1);
+ access.addAndGet(-1);
+ throw x;
+ }
+ mbTx += ((double)(bytes*destination.length))/(1024d*1024d);
+ mbAppTx += ((double)(bytes))/(1024d*1024d);
+ if ( access.addAndGet(-1) == 0 ) {
+ long stop = System.currentTimeMillis();
+ timeTx += ( (double) (stop - txStart)) / 1000d;
+ if ((msgTxCnt.get() / interval) >= lastCnt) {
+ lastCnt++;
+ report(timeTx);
+ }
+ }
+ msgTxCnt.addAndGet(1);
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ if ( rxStart == 0 ) rxStart = System.currentTimeMillis();
+ long bytes = XByteBuffer.getDataPackageLength(((ChannelData)msg).getDataPackageLength());
+ mbRx += ((double)bytes)/(1024d*1024d);
+ msgRxCnt.addAndGet(1);
+ if ( msgRxCnt.get() % interval == 0 ) report(timeTx);
+ super.messageReceived(msg);
+
+ }
+
+ public void report(double timeTx) {
+ StringBuffer buf = new StringBuffer("ThroughputInterceptor Report[\n\tTx Msg:");
+ buf.append(msgTxCnt).append(" messages\n\tSent:");
+ buf.append(df.format(mbTx));
+ buf.append(" MB (total)\n\tSent:");
+ buf.append(df.format(mbAppTx));
+ buf.append(" MB (application)\n\tTime:");
+ buf.append(df.format(timeTx));
+ buf.append(" seconds\n\tTx Speed:");
+ buf.append(df.format(mbTx/timeTx));
+ buf.append(" MB/sec (total)\n\tTxSpeed:");
+ buf.append(df.format(mbAppTx/timeTx));
+ buf.append(" MB/sec (application)\n\tError Msg:");
+ buf.append(msgTxErr).append("\n\tRx Msg:");
+ buf.append(msgRxCnt);
+ buf.append(" messages\n\tRx Speed:");
+ buf.append(df.format(mbRx/((double)((System.currentTimeMillis()-rxStart)/1000))));
+ buf.append(" MB/sec (since 1st msg)\n\tReceived:");
+ buf.append(df.format(mbRx)).append(" MB]\n");
+ if ( log.isInfoEnabled() ) log.info(buf);
+ }
+
+ public void setInterval(int interval) {
+ this.interval = interval;
+ }
+
+ public int getInterval() {
+ return interval;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.group.interceptors;\r
-\r
-import java.util.HashMap;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.ChannelInterceptorBase;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-import org.apache.catalina.tribes.util.UUIDGenerator;\r
-import org.apache.catalina.tribes.util.Arrays;\r
-import org.apache.catalina.tribes.UniqueId;\r
-import java.util.Map;\r
-\r
-/**\r
- * <p>Title: </p>\r
- *\r
- * <p>Description: </p>\r
- *\r
- * <p>Copyright: Copyright (c) 2005</p>\r
- *\r
- * <p>Company: </p>\r
- *\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-public class TwoPhaseCommitInterceptor extends ChannelInterceptorBase {\r
-\r
- public static final byte[] START_DATA = new byte[] {113, 1, -58, 2, -34, -60, 75, -78, -101, -12, 32, -29, 32, 111, -40, 4};\r
- public static final byte[] END_DATA = new byte[] {54, -13, 90, 110, 47, -31, 75, -24, -81, -29, 36, 52, -58, 77, -110, 56};\r
- private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(TwoPhaseCommitInterceptor.class);\r
-\r
- protected HashMap messages = new HashMap();\r
- protected long expire = 1000 * 60; //one minute expiration\r
- protected boolean deepclone = true;\r
-\r
- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws\r
- ChannelException {\r
- //todo, optimize, if destination.length==1, then we can do\r
- //msg.setOptions(msg.getOptions() & (~getOptionFlag())\r
- //and just send one message\r
- if (okToProcess(msg.getOptions()) ) {\r
- super.sendMessage(destination, msg, null);\r
- ChannelMessage confirmation = null;\r
- if ( deepclone ) confirmation = (ChannelMessage)msg.deepclone();\r
- else confirmation = (ChannelMessage)msg.clone();\r
- confirmation.getMessage().reset();\r
- UUIDGenerator.randomUUID(false,confirmation.getUniqueId(),0);\r
- confirmation.getMessage().append(START_DATA,0,START_DATA.length);\r
- confirmation.getMessage().append(msg.getUniqueId(),0,msg.getUniqueId().length);\r
- confirmation.getMessage().append(END_DATA,0,END_DATA.length);\r
- super.sendMessage(destination,confirmation,payload);\r
- } else {\r
- //turn off two phase commit\r
- //this wont work if the interceptor has 0 as a flag\r
- //since there is no flag to turn off\r
- //msg.setOptions(msg.getOptions() & (~getOptionFlag()));\r
- super.sendMessage(destination, msg, payload);\r
- }\r
- }\r
-\r
- public void messageReceived(ChannelMessage msg) {\r
- if (okToProcess(msg.getOptions())) {\r
- if ( msg.getMessage().getLength() == (START_DATA.length+msg.getUniqueId().length+END_DATA.length) &&\r
- Arrays.contains(msg.getMessage().getBytesDirect(),0,START_DATA,0,START_DATA.length) &&\r
- Arrays.contains(msg.getMessage().getBytesDirect(),START_DATA.length+msg.getUniqueId().length,END_DATA,0,END_DATA.length) ) {\r
- UniqueId id = new UniqueId(msg.getMessage().getBytesDirect(),START_DATA.length,msg.getUniqueId().length);\r
- MapEntry original = (MapEntry)messages.get(id);\r
- if ( original != null ) {\r
- super.messageReceived(original.msg);\r
- messages.remove(id);\r
- } else log.warn("Received a confirmation, but original message is missing. Id:"+Arrays.toString(id.getBytes()));\r
- } else {\r
- UniqueId id = new UniqueId(msg.getUniqueId());\r
- MapEntry entry = new MapEntry((ChannelMessage)msg.deepclone(),id,System.currentTimeMillis());\r
- messages.put(id,entry);\r
- }\r
- } else {\r
- super.messageReceived(msg);\r
- }\r
- }\r
-\r
- public boolean getDeepclone() {\r
- return deepclone;\r
- }\r
-\r
- public long getExpire() {\r
- return expire;\r
- }\r
-\r
- public void setDeepclone(boolean deepclone) {\r
- this.deepclone = deepclone;\r
- }\r
-\r
- public void setExpire(long expire) {\r
- this.expire = expire;\r
- }\r
- \r
- public void heartbeat() {\r
- try {\r
- long now = System.currentTimeMillis();\r
- Map.Entry[] entries = (Map.Entry[])messages.entrySet().toArray(new Map.Entry[messages.size()]);\r
- for (int i=0; i<entries.length; i++ ) {\r
- MapEntry entry = (MapEntry)entries[i].getValue();\r
- if ( entry.expired(now,expire) ) {\r
- log.info("Message ["+entry.id+"] has expired. Removing.");\r
- messages.remove(entry.id);\r
- }//end if\r
- }\r
- } catch ( Exception x ) {\r
- log.warn("Unable to perform heartbeat on the TwoPhaseCommit interceptor.",x);\r
- } finally {\r
- super.heartbeat();\r
- }\r
- }\r
- \r
- public static class MapEntry {\r
- public ChannelMessage msg;\r
- public UniqueId id;\r
- public long timestamp;\r
- \r
- public MapEntry(ChannelMessage msg, UniqueId id, long timestamp) {\r
- this.msg = msg;\r
- this.id = id;\r
- this.timestamp = timestamp;\r
- }\r
- public boolean expired(long now, long expiration) {\r
- return (now - timestamp ) > expiration;\r
- }\r
-\r
- }\r
-\r
+/*
+ * 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.apache.catalina.tribes.group.interceptors;
+
+import java.util.HashMap;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+import org.apache.catalina.tribes.util.UUIDGenerator;
+import org.apache.catalina.tribes.util.Arrays;
+import org.apache.catalina.tribes.UniqueId;
+import java.util.Map;
+
+/**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2005</p>
+ *
+ * <p>Company: </p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public class TwoPhaseCommitInterceptor extends ChannelInterceptorBase {
+
+ public static final byte[] START_DATA = new byte[] {113, 1, -58, 2, -34, -60, 75, -78, -101, -12, 32, -29, 32, 111, -40, 4};
+ public static final byte[] END_DATA = new byte[] {54, -13, 90, 110, 47, -31, 75, -24, -81, -29, 36, 52, -58, 77, -110, 56};
+ private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(TwoPhaseCommitInterceptor.class);
+
+ protected HashMap messages = new HashMap();
+ protected long expire = 1000 * 60; //one minute expiration
+ protected boolean deepclone = true;
+
+ public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload payload) throws
+ ChannelException {
+ //todo, optimize, if destination.length==1, then we can do
+ //msg.setOptions(msg.getOptions() & (~getOptionFlag())
+ //and just send one message
+ if (okToProcess(msg.getOptions()) ) {
+ super.sendMessage(destination, msg, null);
+ ChannelMessage confirmation = null;
+ if ( deepclone ) confirmation = (ChannelMessage)msg.deepclone();
+ else confirmation = (ChannelMessage)msg.clone();
+ confirmation.getMessage().reset();
+ UUIDGenerator.randomUUID(false,confirmation.getUniqueId(),0);
+ confirmation.getMessage().append(START_DATA,0,START_DATA.length);
+ confirmation.getMessage().append(msg.getUniqueId(),0,msg.getUniqueId().length);
+ confirmation.getMessage().append(END_DATA,0,END_DATA.length);
+ super.sendMessage(destination,confirmation,payload);
+ } else {
+ //turn off two phase commit
+ //this wont work if the interceptor has 0 as a flag
+ //since there is no flag to turn off
+ //msg.setOptions(msg.getOptions() & (~getOptionFlag()));
+ super.sendMessage(destination, msg, payload);
+ }
+ }
+
+ public void messageReceived(ChannelMessage msg) {
+ if (okToProcess(msg.getOptions())) {
+ if ( msg.getMessage().getLength() == (START_DATA.length+msg.getUniqueId().length+END_DATA.length) &&
+ Arrays.contains(msg.getMessage().getBytesDirect(),0,START_DATA,0,START_DATA.length) &&
+ Arrays.contains(msg.getMessage().getBytesDirect(),START_DATA.length+msg.getUniqueId().length,END_DATA,0,END_DATA.length) ) {
+ UniqueId id = new UniqueId(msg.getMessage().getBytesDirect(),START_DATA.length,msg.getUniqueId().length);
+ MapEntry original = (MapEntry)messages.get(id);
+ if ( original != null ) {
+ super.messageReceived(original.msg);
+ messages.remove(id);
+ } else log.warn("Received a confirmation, but original message is missing. Id:"+Arrays.toString(id.getBytes()));
+ } else {
+ UniqueId id = new UniqueId(msg.getUniqueId());
+ MapEntry entry = new MapEntry((ChannelMessage)msg.deepclone(),id,System.currentTimeMillis());
+ messages.put(id,entry);
+ }
+ } else {
+ super.messageReceived(msg);
+ }
+ }
+
+ public boolean getDeepclone() {
+ return deepclone;
+ }
+
+ public long getExpire() {
+ return expire;
+ }
+
+ public void setDeepclone(boolean deepclone) {
+ this.deepclone = deepclone;
+ }
+
+ public void setExpire(long expire) {
+ this.expire = expire;
+ }
+
+ public void heartbeat() {
+ try {
+ long now = System.currentTimeMillis();
+ Map.Entry[] entries = (Map.Entry[])messages.entrySet().toArray(new Map.Entry[messages.size()]);
+ for (int i=0; i<entries.length; i++ ) {
+ MapEntry entry = (MapEntry)entries[i].getValue();
+ if ( entry.expired(now,expire) ) {
+ log.info("Message ["+entry.id+"] has expired. Removing.");
+ messages.remove(entry.id);
+ }//end if
+ }
+ } catch ( Exception x ) {
+ log.warn("Unable to perform heartbeat on the TwoPhaseCommit interceptor.",x);
+ } finally {
+ super.heartbeat();
+ }
+ }
+
+ public static class MapEntry {
+ public ChannelMessage msg;
+ public UniqueId id;
+ public long timestamp;
+
+ public MapEntry(ChannelMessage msg, UniqueId id, long timestamp) {
+ this.msg = msg;
+ this.id = id;
+ this.timestamp = timestamp;
+ }
+ public boolean expired(long now, long expiration) {
+ return (now - timestamp ) > expiration;
+ }
+
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.io;\r
-\r
-\r
-import org.apache.juli.logging.Log;\r
-import org.apache.juli.logging.LogFactory;\r
-\r
-/**\r
- *\r
- * @author Filip Hanik\r
- * \r
- * @version 1.0\r
- */\r
-public class BufferPool {\r
- protected static Log log = LogFactory.getLog(BufferPool.class);\r
-\r
- public static int DEFAULT_POOL_SIZE = 100*1024*1024; //100MB\r
-\r
-\r
-\r
- protected static BufferPool instance = null;\r
- protected BufferPoolAPI pool = null;\r
-\r
- private BufferPool(BufferPoolAPI pool) {\r
- this.pool = pool;\r
- }\r
-\r
- public XByteBuffer getBuffer(int minSize, boolean discard) {\r
- if ( pool != null ) return pool.getBuffer(minSize, discard);\r
- else return new XByteBuffer(minSize,discard);\r
- }\r
-\r
- public void returnBuffer(XByteBuffer buffer) {\r
- if ( pool != null ) pool.returnBuffer(buffer);\r
- }\r
-\r
- public void clear() {\r
- if ( pool != null ) pool.clear();\r
- }\r
-\r
-\r
- public static BufferPool getBufferPool() {\r
- if ( (instance == null) ) {\r
- synchronized (BufferPool.class) {\r
- if ( instance == null ) {\r
- BufferPoolAPI pool = null;\r
- Class clazz = null;\r
- try {\r
- clazz = Class.forName("org.apache.catalina.tribes.io.BufferPool15Impl");\r
- pool = (BufferPoolAPI)clazz.newInstance();\r
- } catch ( Throwable x ) {\r
- try {\r
- clazz = Class.forName("org.apache.catalina.tribes.io.BufferPool14Impl");\r
- pool = (BufferPoolAPI)clazz.newInstance();\r
- } catch ( Throwable e ) {\r
- log.warn("Unable to initilize BufferPool, not pooling XByteBuffer objects:"+x.getMessage());\r
- if ( log.isDebugEnabled() ) log.debug("Unable to initilize BufferPool, not pooling XByteBuffer objects:",x);\r
- }\r
- }\r
- pool.setMaxSize(DEFAULT_POOL_SIZE);\r
- log.info("Created a buffer pool with max size:"+DEFAULT_POOL_SIZE+" bytes of type:"+(clazz!=null?clazz.getName():"null"));\r
- instance = new BufferPool(pool);\r
- }//end if\r
- }//sync\r
- }//end if\r
- return instance;\r
- }\r
-\r
-\r
- public static interface BufferPoolAPI {\r
- public void setMaxSize(int bytes);\r
-\r
- public XByteBuffer getBuffer(int minSize, boolean discard);\r
-\r
- public void returnBuffer(XByteBuffer buffer);\r
-\r
- public void clear();\r
- } \r
-}\r
+/*
+ * 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.apache.catalina.tribes.io;
+
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ *
+ * @author Filip Hanik
+ *
+ * @version 1.0
+ */
+public class BufferPool {
+ protected static Log log = LogFactory.getLog(BufferPool.class);
+
+ public static int DEFAULT_POOL_SIZE = 100*1024*1024; //100MB
+
+
+
+ protected static BufferPool instance = null;
+ protected BufferPoolAPI pool = null;
+
+ private BufferPool(BufferPoolAPI pool) {
+ this.pool = pool;
+ }
+
+ public XByteBuffer getBuffer(int minSize, boolean discard) {
+ if ( pool != null ) return pool.getBuffer(minSize, discard);
+ else return new XByteBuffer(minSize,discard);
+ }
+
+ public void returnBuffer(XByteBuffer buffer) {
+ if ( pool != null ) pool.returnBuffer(buffer);
+ }
+
+ public void clear() {
+ if ( pool != null ) pool.clear();
+ }
+
+
+ public static BufferPool getBufferPool() {
+ if ( (instance == null) ) {
+ synchronized (BufferPool.class) {
+ if ( instance == null ) {
+ BufferPoolAPI pool = null;
+ Class clazz = null;
+ try {
+ clazz = Class.forName("org.apache.catalina.tribes.io.BufferPool15Impl");
+ pool = (BufferPoolAPI)clazz.newInstance();
+ } catch ( Throwable x ) {
+ try {
+ clazz = Class.forName("org.apache.catalina.tribes.io.BufferPool14Impl");
+ pool = (BufferPoolAPI)clazz.newInstance();
+ } catch ( Throwable e ) {
+ log.warn("Unable to initilize BufferPool, not pooling XByteBuffer objects:"+x.getMessage());
+ if ( log.isDebugEnabled() ) log.debug("Unable to initilize BufferPool, not pooling XByteBuffer objects:",x);
+ }
+ }
+ pool.setMaxSize(DEFAULT_POOL_SIZE);
+ log.info("Created a buffer pool with max size:"+DEFAULT_POOL_SIZE+" bytes of type:"+(clazz!=null?clazz.getName():"null"));
+ instance = new BufferPool(pool);
+ }//end if
+ }//sync
+ }//end if
+ return instance;
+ }
+
+
+ public static interface BufferPoolAPI {
+ public void setMaxSize(int bytes);
+
+ public XByteBuffer getBuffer(int minSize, boolean discard);
+
+ public void returnBuffer(XByteBuffer buffer);
+
+ public void clear();
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.io;\r
-\r
-import java.util.Queue;\r
-import java.util.LinkedList;\r
-\r
-\r
-/**\r
- *\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-class BufferPool14Impl implements BufferPool.BufferPoolAPI {\r
- protected int maxSize;\r
- protected int size = 0;\r
- protected LinkedList queue = new LinkedList();\r
-\r
- public void setMaxSize(int bytes) {\r
- this.maxSize = bytes;\r
- }\r
- \r
- public synchronized int addAndGet(int val) {\r
- size = size + (val);\r
- return size;\r
- }\r
- \r
- \r
-\r
- public synchronized XByteBuffer getBuffer(int minSize, boolean discard) {\r
- XByteBuffer buffer = (XByteBuffer)(queue.size()>0?queue.remove(0):null);\r
- if ( buffer != null ) addAndGet(-buffer.getCapacity());\r
- if ( buffer == null ) buffer = new XByteBuffer(minSize,discard);\r
- else if ( buffer.getCapacity() <= minSize ) buffer.expand(minSize);\r
- buffer.setDiscard(discard);\r
- buffer.reset();\r
- return buffer;\r
- }\r
-\r
- public synchronized void returnBuffer(XByteBuffer buffer) {\r
- if ( (size + buffer.getCapacity()) <= maxSize ) {\r
- addAndGet(buffer.getCapacity());\r
- queue.add(buffer);\r
- }\r
- }\r
-\r
- public synchronized void clear() {\r
- queue.clear();\r
- size = 0;\r
- }\r
-\r
- public int getMaxSize() {\r
- return maxSize;\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.io;
+
+import java.util.Queue;
+import java.util.LinkedList;
+
+
+/**
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+class BufferPool14Impl implements BufferPool.BufferPoolAPI {
+ protected int maxSize;
+ protected int size = 0;
+ protected LinkedList queue = new LinkedList();
+
+ public void setMaxSize(int bytes) {
+ this.maxSize = bytes;
+ }
+
+ public synchronized int addAndGet(int val) {
+ size = size + (val);
+ return size;
+ }
+
+
+
+ public synchronized XByteBuffer getBuffer(int minSize, boolean discard) {
+ XByteBuffer buffer = (XByteBuffer)(queue.size()>0?queue.remove(0):null);
+ if ( buffer != null ) addAndGet(-buffer.getCapacity());
+ if ( buffer == null ) buffer = new XByteBuffer(minSize,discard);
+ else if ( buffer.getCapacity() <= minSize ) buffer.expand(minSize);
+ buffer.setDiscard(discard);
+ buffer.reset();
+ return buffer;
+ }
+
+ public synchronized void returnBuffer(XByteBuffer buffer) {
+ if ( (size + buffer.getCapacity()) <= maxSize ) {
+ addAndGet(buffer.getCapacity());
+ queue.add(buffer);
+ }
+ }
+
+ public synchronized void clear() {
+ queue.clear();
+ size = 0;
+ }
+
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.io;\r
-\r
-import java.util.concurrent.ConcurrentLinkedQueue;\r
-import java.util.concurrent.atomic.AtomicInteger;\r
-\r
-/**\r
- *\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-class BufferPool15Impl implements BufferPool.BufferPoolAPI {\r
- protected int maxSize;\r
- protected AtomicInteger size = new AtomicInteger(0);\r
- protected ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();\r
-\r
- public void setMaxSize(int bytes) {\r
- this.maxSize = bytes;\r
- }\r
-\r
-\r
- public XByteBuffer getBuffer(int minSize, boolean discard) {\r
- XByteBuffer buffer = (XByteBuffer)queue.poll();\r
- if ( buffer != null ) size.addAndGet(-buffer.getCapacity());\r
- if ( buffer == null ) buffer = new XByteBuffer(minSize,discard);\r
- else if ( buffer.getCapacity() <= minSize ) buffer.expand(minSize);\r
- buffer.setDiscard(discard);\r
- buffer.reset();\r
- return buffer;\r
- }\r
-\r
- public void returnBuffer(XByteBuffer buffer) {\r
- if ( (size.get() + buffer.getCapacity()) <= maxSize ) {\r
- size.addAndGet(buffer.getCapacity());\r
- queue.offer(buffer);\r
- }\r
- }\r
-\r
- public void clear() {\r
- queue.clear();\r
- size.set(0);\r
- }\r
-\r
- public int getMaxSize() {\r
- return maxSize;\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.io;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+class BufferPool15Impl implements BufferPool.BufferPoolAPI {
+ protected int maxSize;
+ protected AtomicInteger size = new AtomicInteger(0);
+ protected ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
+
+ public void setMaxSize(int bytes) {
+ this.maxSize = bytes;
+ }
+
+
+ public XByteBuffer getBuffer(int minSize, boolean discard) {
+ XByteBuffer buffer = (XByteBuffer)queue.poll();
+ if ( buffer != null ) size.addAndGet(-buffer.getCapacity());
+ if ( buffer == null ) buffer = new XByteBuffer(minSize,discard);
+ else if ( buffer.getCapacity() <= minSize ) buffer.expand(minSize);
+ buffer.setDiscard(discard);
+ buffer.reset();
+ return buffer;
+ }
+
+ public void returnBuffer(XByteBuffer buffer) {
+ if ( (size.get() + buffer.getCapacity()) <= maxSize ) {
+ size.addAndGet(buffer.getCapacity());
+ queue.offer(buffer);
+ }
+ }
+
+ public void clear() {
+ queue.clear();
+ size.set(0);
+ }
+
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.tribes.membership;\r
-\r
-import org.apache.catalina.tribes.util.Arrays;\r
-\r
-\r
-/**\r
- * Manifest constants for the <code>org.apache.catalina.tribes.membership</code>\r
- * package.\r
- *\r
- * @author Peter Rossbach\r
- * @version $Revision: 303950 $ $Date: 2005-06-09 15:38:30 -0500 (Thu, 09 Jun 2005) $\r
- * @author Filip Hanik\r
- */\r
-\r
-public class Constants {\r
-\r
- public static final String Package = "org.apache.catalina.tribes.membership";\r
- public static void main(String[] args) throws Exception {\r
- System.out.println(Arrays.toString("TRIBES-B".getBytes()));\r
- System.out.println(Arrays.toString("TRIBES-E".getBytes()));\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.tribes.membership;
+
+import org.apache.catalina.tribes.util.Arrays;
+
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.tribes.membership</code>
+ * package.
+ *
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @author Filip Hanik
+ */
+
+public class Constants {
+
+ public static final String Package = "org.apache.catalina.tribes.membership";
+ public static void main(String[] args) throws Exception {
+ System.out.println(Arrays.toString("TRIBES-B".getBytes()));
+ System.out.println(Arrays.toString("TRIBES-E".getBytes()));
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.membership;\r
-\r
-import java.util.Properties;\r
-\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.MembershipListener;\r
-import org.apache.catalina.tribes.MembershipService;\r
-import org.apache.catalina.tribes.util.StringManager;\r
-import org.apache.catalina.tribes.util.UUIDGenerator;\r
-import java.io.IOException;\r
-\r
-/**\r
- * A <b>membership</b> implementation using simple multicast.\r
- * This is the representation of a multicast membership service.\r
- * This class is responsible for maintaining a list of active cluster nodes in the cluster.\r
- * If a node fails to send out a heartbeat, the node will be dismissed.\r
- *\r
- * @author Filip Hanik\r
- * @version $Revision: 378093 $, $Date: 2006-02-15 15:13:45 -0600 (Wed, 15 Feb 2006) $\r
- */\r
-\r
-\r
-public class McastService implements MembershipService,MembershipListener {\r
-\r
- private static org.apache.juli.logging.Log log =\r
- org.apache.juli.logging.LogFactory.getLog( McastService.class );\r
-\r
- /**\r
- * The string manager for this package.\r
- */\r
- protected StringManager sm = StringManager.getManager(Constants.Package);\r
-\r
- /**\r
- * The descriptive information about this implementation.\r
- */\r
- private static final String info = "McastService/2.1";\r
-\r
- /**\r
- * The implementation specific properties\r
- */\r
- protected Properties properties = new Properties();\r
- /**\r
- * A handle to the actual low level implementation\r
- */\r
- protected McastServiceImpl impl;\r
- /**\r
- * A membership listener delegate (should be the cluster :)\r
- */\r
- protected MembershipListener listener;\r
- /**\r
- * The local member\r
- */\r
- protected MemberImpl localMember ;\r
- private int mcastSoTimeout;\r
- private int mcastTTL;\r
- \r
- protected byte[] payload;\r
- \r
- protected byte[] domain;\r
-\r
- /**\r
- * Create a membership service.\r
- */\r
- public McastService() {\r
- //default values\r
- properties.setProperty("mcastPort","45564");\r
- properties.setProperty("mcastAddress","228.0.0.4");\r
- properties.setProperty("memberDropTime","3000");\r
- properties.setProperty("mcastFrequency","500");\r
-\r
- }\r
-\r
- /**\r
- * Return descriptive information about this implementation and the\r
- * corresponding version number, in the format\r
- * <code><description>/<version></code>.\r
- */\r
- public String getInfo() {\r
- return (info);\r
- }\r
- \r
- /**\r
- *\r
- * @param properties\r
- * <BR/>All are required<BR />\r
- * 1. mcastPort - the port to listen to<BR>\r
- * 2. mcastAddress - the mcast group address<BR>\r
- * 4. bindAddress - the bind address if any - only one that can be null<BR>\r
- * 5. memberDropTime - the time a member is gone before it is considered gone.<BR>\r
- * 6. mcastFrequency - the frequency of sending messages<BR>\r
- * 7. tcpListenPort - the port this member listens to<BR>\r
- * 8. tcpListenHost - the bind address of this member<BR>\r
- * @exception java.lang.IllegalArgumentException if a property is missing.\r
- */\r
- public void setProperties(Properties properties) {\r
- hasProperty(properties,"mcastPort");\r
- hasProperty(properties,"mcastAddress");\r
- hasProperty(properties,"memberDropTime");\r
- hasProperty(properties,"mcastFrequency");\r
- hasProperty(properties,"tcpListenPort");\r
- hasProperty(properties,"tcpListenHost");\r
- this.properties = properties;\r
- }\r
-\r
- /**\r
- * Return the properties, see setProperties\r
- */\r
- public Properties getProperties() {\r
- return properties;\r
- }\r
-\r
- /**\r
- * Return the local member name\r
- */\r
- public String getLocalMemberName() {\r
- return localMember.toString() ;\r
- }\r
- \r
- /**\r
- * Return the local member\r
- */\r
- public Member getLocalMember(boolean alive) {\r
- if ( alive && localMember != null && impl != null) localMember.setMemberAliveTime(System.currentTimeMillis()-impl.getServiceStartTime());\r
- return localMember;\r
- }\r
- \r
- /**\r
- * Sets the local member properties for broadcasting\r
- */\r
- public void setLocalMemberProperties(String listenHost, int listenPort) {\r
- properties.setProperty("tcpListenHost",listenHost);\r
- properties.setProperty("tcpListenPort",String.valueOf(listenPort));\r
- try {\r
- if (localMember != null) {\r
- localMember.setHostname(listenHost);\r
- localMember.setPort(listenPort);\r
- } else {\r
- localMember = new MemberImpl(listenHost, listenPort, 0);\r
- localMember.setUniqueId(UUIDGenerator.randomUUID(true));\r
- localMember.setPayload(getPayload());\r
- localMember.setDomain(getDomain());\r
- }\r
- localMember.getData(true, true);\r
- }catch ( IOException x ) {\r
- throw new IllegalArgumentException(x);\r
- }\r
- }\r
- \r
- public void setAddress(String addr) {\r
- properties.setProperty("mcastAddress", addr);\r
- }\r
- \r
- /**\r
- * @deprecated use setAddress\r
- * @param addr String\r
- */\r
- public void setMcastAddr(String addr) {\r
- setAddress(addr);\r
- }\r
- \r
- public String getAddress() {\r
- return properties.getProperty("mcastAddress");\r
- }\r
- \r
- /**\r
- * @deprecated use getAddress\r
- * @return String\r
- */\r
- public String getMcastAddr() {\r
- return getAddress();\r
- }\r
-\r
- public void setMcastBindAddress(String bindaddr) {\r
- setBind(bindaddr);\r
- }\r
- \r
- public void setBind(String bindaddr) {\r
- properties.setProperty("mcastBindAddress", bindaddr);\r
- }\r
- /**\r
- * @deprecated use getBind\r
- * @return String\r
- */\r
- public String getMcastBindAddress() {\r
- return getBind();\r
- }\r
-\r
- public String getBind() {\r
- return properties.getProperty("mcastBindAddress");\r
- }\r
-\r
- /**\r
- * @deprecated use setPort\r
- * @param port int\r
- */\r
- public void setMcastPort(int port) {\r
- setPort(port);\r
- }\r
-\r
- public void setPort(int port) {\r
- properties.setProperty("mcastPort", String.valueOf(port));\r
- }\r
-\r
- /**\r
- * @deprecated use getPort()\r
- * @return int\r
- */\r
- public int getMcastPort() {\r
- return getPort();\r
- }\r
- public int getPort() {\r
- String p = properties.getProperty("mcastPort");\r
- return new Integer(p).intValue();\r
- }\r
- \r
- /**\r
- * @deprecated use setFrequency\r
- * @param time long\r
- */\r
- public void setMcastFrequency(long time) {\r
- setFrequency(time);\r
- }\r
- \r
- public void setFrequency(long time) {\r
- properties.setProperty("mcastFrequency", String.valueOf(time));\r
- }\r
-\r
- /**\r
- * @deprecated use getFrequency\r
- * @return long\r
- */\r
- public long getMcastFrequency() {\r
- return getFrequency();\r
- }\r
-\r
- public long getFrequency() {\r
- String p = properties.getProperty("mcastFrequency");\r
- return new Long(p).longValue();\r
- }\r
-\r
- public void setMcastDropTime(long time) {\r
- setDropTime(time);\r
- }\r
- public void setDropTime(long time) {\r
- properties.setProperty("memberDropTime", String.valueOf(time));\r
- }\r
- \r
- /**\r
- * @deprecated use getDropTime\r
- * @return long\r
- */\r
- public long getMcastDropTime() {\r
- return getDropTime();\r
- }\r
-\r
- public long getDropTime() {\r
- String p = properties.getProperty("memberDropTime");\r
- return new Long(p).longValue();\r
- }\r
-\r
- /**\r
- * Check if a required property is available.\r
- * @param properties The set of properties\r
- * @param name The property to check for\r
- */\r
- protected void hasProperty(Properties properties, String name){\r
- if ( properties.getProperty(name)==null) throw new IllegalArgumentException("McastService:Required property \""+name+"\" is missing.");\r
- }\r
-\r
- /**\r
- * Start broadcasting and listening to membership pings\r
- * @throws java.lang.Exception if a IO error occurs\r
- */\r
- public void start() throws java.lang.Exception {\r
- start(MembershipService.MBR_RX);\r
- start(MembershipService.MBR_TX);\r
- }\r
- \r
- public void start(int level) throws java.lang.Exception {\r
- hasProperty(properties,"mcastPort");\r
- hasProperty(properties,"mcastAddress");\r
- hasProperty(properties,"memberDropTime");\r
- hasProperty(properties,"mcastFrequency");\r
- hasProperty(properties,"tcpListenPort");\r
- hasProperty(properties,"tcpListenHost");\r
-\r
- if ( impl != null ) {\r
- impl.start(level);\r
- return;\r
- }\r
- String host = getProperties().getProperty("tcpListenHost");\r
- int port = Integer.parseInt(getProperties().getProperty("tcpListenPort"));\r
- \r
- if ( localMember == null ) {\r
- localMember = new MemberImpl(host, port, 100);\r
- localMember.setUniqueId(UUIDGenerator.randomUUID(true));\r
- } else {\r
- localMember.setHostname(host);\r
- localMember.setPort(port);\r
- localMember.setMemberAliveTime(100);\r
- }\r
- if ( this.payload != null ) localMember.setPayload(payload);\r
- if ( this.domain != null ) localMember.setDomain(domain);\r
- localMember.setServiceStartTime(System.currentTimeMillis());\r
- java.net.InetAddress bind = null;\r
- if ( properties.getProperty("mcastBindAddress")!= null ) {\r
- bind = java.net.InetAddress.getByName(properties.getProperty("mcastBindAddress"));\r
- }\r
- int ttl = -1;\r
- int soTimeout = -1;\r
- if ( properties.getProperty("mcastTTL") != null ) {\r
- try {\r
- ttl = Integer.parseInt(properties.getProperty("mcastTTL"));\r
- } catch ( Exception x ) {\r
- log.error("Unable to parse mcastTTL="+properties.getProperty("mcastTTL"),x);\r
- }\r
- }\r
- if ( properties.getProperty("mcastSoTimeout") != null ) {\r
- try {\r
- soTimeout = Integer.parseInt(properties.getProperty("mcastSoTimeout"));\r
- } catch ( Exception x ) {\r
- log.error("Unable to parse mcastSoTimeout="+properties.getProperty("mcastSoTimeout"),x);\r
- }\r
- }\r
-\r
- impl = new McastServiceImpl((MemberImpl)localMember,Long.parseLong(properties.getProperty("mcastFrequency")),\r
- Long.parseLong(properties.getProperty("memberDropTime")),\r
- Integer.parseInt(properties.getProperty("mcastPort")),\r
- bind,\r
- java.net.InetAddress.getByName(properties.getProperty("mcastAddress")),\r
- ttl,\r
- soTimeout,\r
- this);\r
- \r
- impl.start(level);\r
- \r
-\r
- }\r
-\r
- \r
- /**\r
- * Stop broadcasting and listening to membership pings\r
- */\r
- public void stop(int svc) {\r
- try {\r
- if ( impl != null && impl.stop(svc) ) impl = null;\r
- } catch ( Exception x) {\r
- log.error("Unable to stop the mcast service, level:"+svc+".",x);\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Return all the members by name\r
- */\r
- public String[] getMembersByName() {\r
- Member[] currentMembers = getMembers();\r
- String [] membernames ;\r
- if(currentMembers != null) {\r
- membernames = new String[currentMembers.length];\r
- for (int i = 0; i < currentMembers.length; i++) {\r
- membernames[i] = currentMembers[i].toString() ;\r
- }\r
- } else\r
- membernames = new String[0] ;\r
- return membernames ;\r
- }\r
- \r
- /**\r
- * Return the member by name\r
- */\r
- public Member findMemberByName(String name) {\r
- Member[] currentMembers = getMembers();\r
- for (int i = 0; i < currentMembers.length; i++) {\r
- if (name.equals(currentMembers[i].toString()))\r
- return currentMembers[i];\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * has members?\r
- */\r
- public boolean hasMembers() {\r
- if ( impl == null || impl.membership == null ) return false;\r
- return impl.membership.hasMembers();\r
- }\r
- \r
- public Member getMember(Member mbr) {\r
- if ( impl == null || impl.membership == null ) return null;\r
- return impl.membership.getMember(mbr);\r
- }\r
-\r
- /**\r
- * Return all the members\r
- */\r
- public Member[] getMembers() {\r
- if ( impl == null || impl.membership == null ) return null;\r
- return impl.membership.getMembers();\r
- }\r
- /**\r
- * Add a membership listener, this version only supports one listener per service,\r
- * so calling this method twice will result in only the second listener being active.\r
- * @param listener The listener\r
- */\r
- public void setMembershipListener(MembershipListener listener) {\r
- this.listener = listener;\r
- }\r
- /**\r
- * Remove the membership listener\r
- */\r
- public void removeMembershipListener(){\r
- listener = null;\r
- }\r
-\r
- public void memberAdded(Member member) {\r
- if ( listener!=null ) listener.memberAdded(member);\r
- }\r
-\r
- /**\r
- * Callback from the impl when a new member has been received\r
- * @param member The member\r
- */\r
- public void memberDisappeared(Member member)\r
- {\r
- if ( listener!=null ) listener.memberDisappeared(member);\r
- }\r
-\r
- /**\r
- * @deprecated use getSoTimeout\r
- * @return int\r
- */\r
- public int getMcastSoTimeout() {\r
- return getSoTimeout();\r
- }\r
- \r
- public int getSoTimeout() {\r
- return mcastSoTimeout;\r
- }\r
- \r
- /**\r
- * @deprecated use setSoTimeout\r
- * @param mcastSoTimeout int\r
- */\r
- public void setMcastSoTimeout(int mcastSoTimeout) {\r
- setSoTimeout(mcastSoTimeout);\r
- }\r
- \r
- public void setSoTimeout(int mcastSoTimeout) {\r
- this.mcastSoTimeout = mcastSoTimeout;\r
- properties.setProperty("mcastSoTimeout", String.valueOf(mcastSoTimeout));\r
- }\r
- \r
- /**\r
- * @deprecated use getTtl\r
- * @return int\r
- */\r
- public int getMcastTTL() {\r
- return getTtl();\r
- }\r
- \r
- public int getTtl() {\r
- return mcastTTL;\r
- }\r
-\r
- public byte[] getPayload() {\r
- return payload;\r
- }\r
- \r
- public byte[] getDomain() {\r
- return domain;\r
- }\r
- \r
- /**\r
- * @deprecated use setTtl\r
- * @param mcastTTL int\r
- */\r
- public void setMcastTTL(int mcastTTL) {\r
- setTtl(mcastTTL);\r
- }\r
-\r
- public void setTtl(int mcastTTL) {\r
- this.mcastTTL = mcastTTL;\r
- properties.setProperty("mcastTTL", String.valueOf(mcastTTL));\r
- }\r
-\r
- public void setPayload(byte[] payload) {\r
- this.payload = payload;\r
- if ( localMember != null ) {\r
- localMember.setPayload(payload);\r
- localMember.getData(true,true);\r
- try {\r
- if (impl != null) impl.send(false);\r
- }catch ( Exception x ) {\r
- log.error("Unable to send payload update.",x);\r
- }\r
- }\r
- }\r
- \r
- public void setDomain(byte[] domain) {\r
- this.domain = domain;\r
- if ( localMember != null ) {\r
- localMember.setDomain(domain);\r
- localMember.getData(true,true);\r
- try {\r
- if (impl != null) impl.send(false);\r
- }catch ( Exception x ) {\r
- log.error("Unable to send domain update.",x);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Simple test program\r
- * @param args Command-line arguments\r
- * @throws Exception If an error occurs\r
- */\r
- public static void main(String args[]) throws Exception {\r
- if(log.isInfoEnabled())\r
- log.info("Usage McastService hostname tcpport");\r
- McastService service = new McastService();\r
- java.util.Properties p = new java.util.Properties();\r
- p.setProperty("mcastPort","5555");\r
- p.setProperty("mcastAddress","224.10.10.10");\r
- p.setProperty("mcastClusterDomain","catalina");\r
- p.setProperty("bindAddress","localhost");\r
- p.setProperty("memberDropTime","3000");\r
- p.setProperty("mcastFrequency","500");\r
- p.setProperty("tcpListenPort","4000");\r
- p.setProperty("tcpListenHost","127.0.0.1");\r
- service.setProperties(p);\r
- service.start();\r
- Thread.sleep(60*1000*60);\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.tribes.membership;
+
+import java.util.Properties;
+
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipListener;
+import org.apache.catalina.tribes.MembershipService;
+import org.apache.catalina.tribes.util.StringManager;
+import org.apache.catalina.tribes.util.UUIDGenerator;
+import java.io.IOException;
+
+/**
+ * A <b>membership</b> implementation using simple multicast.
+ * This is the representation of a multicast membership service.
+ * This class is responsible for maintaining a list of active cluster nodes in the cluster.
+ * If a node fails to send out a heartbeat, the node will be dismissed.
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+
+
+public class McastService implements MembershipService,MembershipListener {
+
+ private static org.apache.juli.logging.Log log =
+ org.apache.juli.logging.LogFactory.getLog( McastService.class );
+
+ /**
+ * The string manager for this package.
+ */
+ protected StringManager sm = StringManager.getManager(Constants.Package);
+
+ /**
+ * The descriptive information about this implementation.
+ */
+ private static final String info = "McastService/2.1";
+
+ /**
+ * The implementation specific properties
+ */
+ protected Properties properties = new Properties();
+ /**
+ * A handle to the actual low level implementation
+ */
+ protected McastServiceImpl impl;
+ /**
+ * A membership listener delegate (should be the cluster :)
+ */
+ protected MembershipListener listener;
+ /**
+ * The local member
+ */
+ protected MemberImpl localMember ;
+ private int mcastSoTimeout;
+ private int mcastTTL;
+
+ protected byte[] payload;
+
+ protected byte[] domain;
+
+ /**
+ * Create a membership service.
+ */
+ public McastService() {
+ //default values
+ properties.setProperty("mcastPort","45564");
+ properties.setProperty("mcastAddress","228.0.0.4");
+ properties.setProperty("memberDropTime","3000");
+ properties.setProperty("mcastFrequency","500");
+
+ }
+
+ /**
+ * Return descriptive information about this implementation and the
+ * corresponding version number, in the format
+ * <code><description>/<version></code>.
+ */
+ public String getInfo() {
+ return (info);
+ }
+
+ /**
+ *
+ * @param properties
+ * <BR/>All are required<BR />
+ * 1. mcastPort - the port to listen to<BR>
+ * 2. mcastAddress - the mcast group address<BR>
+ * 4. bindAddress - the bind address if any - only one that can be null<BR>
+ * 5. memberDropTime - the time a member is gone before it is considered gone.<BR>
+ * 6. mcastFrequency - the frequency of sending messages<BR>
+ * 7. tcpListenPort - the port this member listens to<BR>
+ * 8. tcpListenHost - the bind address of this member<BR>
+ * @exception java.lang.IllegalArgumentException if a property is missing.
+ */
+ public void setProperties(Properties properties) {
+ hasProperty(properties,"mcastPort");
+ hasProperty(properties,"mcastAddress");
+ hasProperty(properties,"memberDropTime");
+ hasProperty(properties,"mcastFrequency");
+ hasProperty(properties,"tcpListenPort");
+ hasProperty(properties,"tcpListenHost");
+ this.properties = properties;
+ }
+
+ /**
+ * Return the properties, see setProperties
+ */
+ public Properties getProperties() {
+ return properties;
+ }
+
+ /**
+ * Return the local member name
+ */
+ public String getLocalMemberName() {
+ return localMember.toString() ;
+ }
+
+ /**
+ * Return the local member
+ */
+ public Member getLocalMember(boolean alive) {
+ if ( alive && localMember != null && impl != null) localMember.setMemberAliveTime(System.currentTimeMillis()-impl.getServiceStartTime());
+ return localMember;
+ }
+
+ /**
+ * Sets the local member properties for broadcasting
+ */
+ public void setLocalMemberProperties(String listenHost, int listenPort) {
+ properties.setProperty("tcpListenHost",listenHost);
+ properties.setProperty("tcpListenPort",String.valueOf(listenPort));
+ try {
+ if (localMember != null) {
+ localMember.setHostname(listenHost);
+ localMember.setPort(listenPort);
+ } else {
+ localMember = new MemberImpl(listenHost, listenPort, 0);
+ localMember.setUniqueId(UUIDGenerator.randomUUID(true));
+ localMember.setPayload(getPayload());
+ localMember.setDomain(getDomain());
+ }
+ localMember.getData(true, true);
+ }catch ( IOException x ) {
+ throw new IllegalArgumentException(x);
+ }
+ }
+
+ public void setAddress(String addr) {
+ properties.setProperty("mcastAddress", addr);
+ }
+
+ /**
+ * @deprecated use setAddress
+ * @param addr String
+ */
+ public void setMcastAddr(String addr) {
+ setAddress(addr);
+ }
+
+ public String getAddress() {
+ return properties.getProperty("mcastAddress");
+ }
+
+ /**
+ * @deprecated use getAddress
+ * @return String
+ */
+ public String getMcastAddr() {
+ return getAddress();
+ }
+
+ public void setMcastBindAddress(String bindaddr) {
+ setBind(bindaddr);
+ }
+
+ public void setBind(String bindaddr) {
+ properties.setProperty("mcastBindAddress", bindaddr);
+ }
+ /**
+ * @deprecated use getBind
+ * @return String
+ */
+ public String getMcastBindAddress() {
+ return getBind();
+ }
+
+ public String getBind() {
+ return properties.getProperty("mcastBindAddress");
+ }
+
+ /**
+ * @deprecated use setPort
+ * @param port int
+ */
+ public void setMcastPort(int port) {
+ setPort(port);
+ }
+
+ public void setPort(int port) {
+ properties.setProperty("mcastPort", String.valueOf(port));
+ }
+
+ /**
+ * @deprecated use getPort()
+ * @return int
+ */
+ public int getMcastPort() {
+ return getPort();
+ }
+ public int getPort() {
+ String p = properties.getProperty("mcastPort");
+ return new Integer(p).intValue();
+ }
+
+ /**
+ * @deprecated use setFrequency
+ * @param time long
+ */
+ public void setMcastFrequency(long time) {
+ setFrequency(time);
+ }
+
+ public void setFrequency(long time) {
+ properties.setProperty("mcastFrequency", String.valueOf(time));
+ }
+
+ /**
+ * @deprecated use getFrequency
+ * @return long
+ */
+ public long getMcastFrequency() {
+ return getFrequency();
+ }
+
+ public long getFrequency() {
+ String p = properties.getProperty("mcastFrequency");
+ return new Long(p).longValue();
+ }
+
+ public void setMcastDropTime(long time) {
+ setDropTime(time);
+ }
+ public void setDropTime(long time) {
+ properties.setProperty("memberDropTime", String.valueOf(time));
+ }
+
+ /**
+ * @deprecated use getDropTime
+ * @return long
+ */
+ public long getMcastDropTime() {
+ return getDropTime();
+ }
+
+ public long getDropTime() {
+ String p = properties.getProperty("memberDropTime");
+ return new Long(p).longValue();
+ }
+
+ /**
+ * Check if a required property is available.
+ * @param properties The set of properties
+ * @param name The property to check for
+ */
+ protected void hasProperty(Properties properties, String name){
+ if ( properties.getProperty(name)==null) throw new IllegalArgumentException("McastService:Required property \""+name+"\" is missing.");
+ }
+
+ /**
+ * Start broadcasting and listening to membership pings
+ * @throws java.lang.Exception if a IO error occurs
+ */
+ public void start() throws java.lang.Exception {
+ start(MembershipService.MBR_RX);
+ start(MembershipService.MBR_TX);
+ }
+
+ public void start(int level) throws java.lang.Exception {
+ hasProperty(properties,"mcastPort");
+ hasProperty(properties,"mcastAddress");
+ hasProperty(properties,"memberDropTime");
+ hasProperty(properties,"mcastFrequency");
+ hasProperty(properties,"tcpListenPort");
+ hasProperty(properties,"tcpListenHost");
+
+ if ( impl != null ) {
+ impl.start(level);
+ return;
+ }
+ String host = getProperties().getProperty("tcpListenHost");
+ int port = Integer.parseInt(getProperties().getProperty("tcpListenPort"));
+
+ if ( localMember == null ) {
+ localMember = new MemberImpl(host, port, 100);
+ localMember.setUniqueId(UUIDGenerator.randomUUID(true));
+ } else {
+ localMember.setHostname(host);
+ localMember.setPort(port);
+ localMember.setMemberAliveTime(100);
+ }
+ if ( this.payload != null ) localMember.setPayload(payload);
+ if ( this.domain != null ) localMember.setDomain(domain);
+ localMember.setServiceStartTime(System.currentTimeMillis());
+ java.net.InetAddress bind = null;
+ if ( properties.getProperty("mcastBindAddress")!= null ) {
+ bind = java.net.InetAddress.getByName(properties.getProperty("mcastBindAddress"));
+ }
+ int ttl = -1;
+ int soTimeout = -1;
+ if ( properties.getProperty("mcastTTL") != null ) {
+ try {
+ ttl = Integer.parseInt(properties.getProperty("mcastTTL"));
+ } catch ( Exception x ) {
+ log.error("Unable to parse mcastTTL="+properties.getProperty("mcastTTL"),x);
+ }
+ }
+ if ( properties.getProperty("mcastSoTimeout") != null ) {
+ try {
+ soTimeout = Integer.parseInt(properties.getProperty("mcastSoTimeout"));
+ } catch ( Exception x ) {
+ log.error("Unable to parse mcastSoTimeout="+properties.getProperty("mcastSoTimeout"),x);
+ }
+ }
+
+ impl = new McastServiceImpl((MemberImpl)localMember,Long.parseLong(properties.getProperty("mcastFrequency")),
+ Long.parseLong(properties.getProperty("memberDropTime")),
+ Integer.parseInt(properties.getProperty("mcastPort")),
+ bind,
+ java.net.InetAddress.getByName(properties.getProperty("mcastAddress")),
+ ttl,
+ soTimeout,
+ this);
+
+ impl.start(level);
+
+
+ }
+
+
+ /**
+ * Stop broadcasting and listening to membership pings
+ */
+ public void stop(int svc) {
+ try {
+ if ( impl != null && impl.stop(svc) ) impl = null;
+ } catch ( Exception x) {
+ log.error("Unable to stop the mcast service, level:"+svc+".",x);
+ }
+ }
+
+
+ /**
+ * Return all the members by name
+ */
+ public String[] getMembersByName() {
+ Member[] currentMembers = getMembers();
+ String [] membernames ;
+ if(currentMembers != null) {
+ membernames = new String[currentMembers.length];
+ for (int i = 0; i < currentMembers.length; i++) {
+ membernames[i] = currentMembers[i].toString() ;
+ }
+ } else
+ membernames = new String[0] ;
+ return membernames ;
+ }
+
+ /**
+ * Return the member by name
+ */
+ public Member findMemberByName(String name) {
+ Member[] currentMembers = getMembers();
+ for (int i = 0; i < currentMembers.length; i++) {
+ if (name.equals(currentMembers[i].toString()))
+ return currentMembers[i];
+ }
+ return null;
+ }
+
+ /**
+ * has members?
+ */
+ public boolean hasMembers() {
+ if ( impl == null || impl.membership == null ) return false;
+ return impl.membership.hasMembers();
+ }
+
+ public Member getMember(Member mbr) {
+ if ( impl == null || impl.membership == null ) return null;
+ return impl.membership.getMember(mbr);
+ }
+
+ /**
+ * Return all the members
+ */
+ public Member[] getMembers() {
+ if ( impl == null || impl.membership == null ) return null;
+ return impl.membership.getMembers();
+ }
+ /**
+ * Add a membership listener, this version only supports one listener per service,
+ * so calling this method twice will result in only the second listener being active.
+ * @param listener The listener
+ */
+ public void setMembershipListener(MembershipListener listener) {
+ this.listener = listener;
+ }
+ /**
+ * Remove the membership listener
+ */
+ public void removeMembershipListener(){
+ listener = null;
+ }
+
+ public void memberAdded(Member member) {
+ if ( listener!=null ) listener.memberAdded(member);
+ }
+
+ /**
+ * Callback from the impl when a new member has been received
+ * @param member The member
+ */
+ public void memberDisappeared(Member member)
+ {
+ if ( listener!=null ) listener.memberDisappeared(member);
+ }
+
+ /**
+ * @deprecated use getSoTimeout
+ * @return int
+ */
+ public int getMcastSoTimeout() {
+ return getSoTimeout();
+ }
+
+ public int getSoTimeout() {
+ return mcastSoTimeout;
+ }
+
+ /**
+ * @deprecated use setSoTimeout
+ * @param mcastSoTimeout int
+ */
+ public void setMcastSoTimeout(int mcastSoTimeout) {
+ setSoTimeout(mcastSoTimeout);
+ }
+
+ public void setSoTimeout(int mcastSoTimeout) {
+ this.mcastSoTimeout = mcastSoTimeout;
+ properties.setProperty("mcastSoTimeout", String.valueOf(mcastSoTimeout));
+ }
+
+ /**
+ * @deprecated use getTtl
+ * @return int
+ */
+ public int getMcastTTL() {
+ return getTtl();
+ }
+
+ public int getTtl() {
+ return mcastTTL;
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+
+ public byte[] getDomain() {
+ return domain;
+ }
+
+ /**
+ * @deprecated use setTtl
+ * @param mcastTTL int
+ */
+ public void setMcastTTL(int mcastTTL) {
+ setTtl(mcastTTL);
+ }
+
+ public void setTtl(int mcastTTL) {
+ this.mcastTTL = mcastTTL;
+ properties.setProperty("mcastTTL", String.valueOf(mcastTTL));
+ }
+
+ public void setPayload(byte[] payload) {
+ this.payload = payload;
+ if ( localMember != null ) {
+ localMember.setPayload(payload);
+ localMember.getData(true,true);
+ try {
+ if (impl != null) impl.send(false);
+ }catch ( Exception x ) {
+ log.error("Unable to send payload update.",x);
+ }
+ }
+ }
+
+ public void setDomain(byte[] domain) {
+ this.domain = domain;
+ if ( localMember != null ) {
+ localMember.setDomain(domain);
+ localMember.getData(true,true);
+ try {
+ if (impl != null) impl.send(false);
+ }catch ( Exception x ) {
+ log.error("Unable to send domain update.",x);
+ }
+ }
+ }
+
+ /**
+ * Simple test program
+ * @param args Command-line arguments
+ * @throws Exception If an error occurs
+ */
+ public static void main(String args[]) throws Exception {
+ if(log.isInfoEnabled())
+ log.info("Usage McastService hostname tcpport");
+ McastService service = new McastService();
+ java.util.Properties p = new java.util.Properties();
+ p.setProperty("mcastPort","5555");
+ p.setProperty("mcastAddress","224.10.10.10");
+ p.setProperty("mcastClusterDomain","catalina");
+ p.setProperty("bindAddress","localhost");
+ p.setProperty("memberDropTime","3000");
+ p.setProperty("mcastFrequency","500");
+ p.setProperty("tcpListenPort","4000");
+ p.setProperty("tcpListenHost","127.0.0.1");
+ service.setProperties(p);
+ service.start();
+ Thread.sleep(60*1000*60);
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.membership;\r
-\r
-\r
-import java.io.IOException;\r
-import java.net.DatagramPacket;\r
-import java.net.InetAddress;\r
-import java.net.MulticastSocket;\r
-\r
-import org.apache.catalina.tribes.MembershipListener;\r
-import java.util.Arrays;\r
-import java.net.SocketTimeoutException;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.Channel;\r
-import java.net.InetSocketAddress;\r
-\r
-/**\r
- * A <b>membership</b> implementation using simple multicast.\r
- * This is the representation of a multicast membership service.\r
- * This class is responsible for maintaining a list of active cluster nodes in the cluster.\r
- * If a node fails to send out a heartbeat, the node will be dismissed.\r
- * This is the low level implementation that handles the multicasting sockets.\r
- * Need to fix this, could use java.nio and only need one thread to send and receive, or\r
- * just use a timeout on the receive\r
- * @author Filip Hanik\r
- * @version $Revision: 356540 $, $Date: 2005-12-13 10:53:40 -0600 (Tue, 13 Dec 2005) $\r
- */\r
-public class McastServiceImpl\r
-{\r
- private static org.apache.juli.logging.Log log =\r
- org.apache.juli.logging.LogFactory.getLog( McastService.class );\r
- \r
- protected static int MAX_PACKET_SIZE = 65535;\r
- /**\r
- * Internal flag used for the listen thread that listens to the multicasting socket.\r
- */\r
- protected boolean doRunSender = false;\r
- protected boolean doRunReceiver = false;\r
- protected int startLevel = 0;\r
- /**\r
- * Socket that we intend to listen to\r
- */\r
- protected MulticastSocket socket;\r
- /**\r
- * The local member that we intend to broad cast over and over again\r
- */\r
- protected MemberImpl member;\r
- /**\r
- * The multicast address\r
- */\r
- protected InetAddress address;\r
- /**\r
- * The multicast port\r
- */\r
- protected int port;\r
- /**\r
- * The time it takes for a member to expire.\r
- */\r
- protected long timeToExpiration;\r
- /**\r
- * How often to we send out a broadcast saying we are alive, must be smaller than timeToExpiration\r
- */\r
- protected long sendFrequency;\r
- /**\r
- * Reuse the sendPacket, no need to create a new one everytime\r
- */\r
- protected DatagramPacket sendPacket;\r
- /**\r
- * Reuse the receivePacket, no need to create a new one everytime\r
- */\r
- protected DatagramPacket receivePacket;\r
- /**\r
- * The membership, used so that we calculate memberships when they arrive or don't arrive\r
- */\r
- protected Membership membership;\r
- /**\r
- * The actual listener, for callback when shits goes down\r
- */\r
- protected MembershipListener service;\r
- /**\r
- * Thread to listen for pings\r
- */\r
- protected ReceiverThread receiver;\r
- /**\r
- * Thread to send pings\r
- */\r
- protected SenderThread sender;\r
-\r
- /**\r
- * When was the service started\r
- */\r
- protected long serviceStartTime = System.currentTimeMillis();\r
- \r
- /**\r
- * Time to live for the multicast packets that are being sent out\r
- */\r
- protected int mcastTTL = -1;\r
- /**\r
- * Read timeout on the mcast socket\r
- */\r
- protected int mcastSoTimeout = -1;\r
- /**\r
- * bind address\r
- */\r
- protected InetAddress mcastBindAddress = null;\r
- \r
- /**\r
- * Create a new mcast service impl\r
- * @param member - the local member\r
- * @param sendFrequency - the time (ms) in between pings sent out\r
- * @param expireTime - the time (ms) for a member to expire\r
- * @param port - the mcast port\r
- * @param bind - the bind address (not sure this is used yet)\r
- * @param mcastAddress - the mcast address\r
- * @param service - the callback service\r
- * @throws IOException\r
- */\r
- public McastServiceImpl(\r
- MemberImpl member,\r
- long sendFrequency,\r
- long expireTime,\r
- int port,\r
- InetAddress bind,\r
- InetAddress mcastAddress,\r
- int ttl,\r
- int soTimeout,\r
- MembershipListener service)\r
- throws IOException {\r
- this.member = member;\r
- this.address = mcastAddress;\r
- this.port = port;\r
- this.mcastSoTimeout = soTimeout;\r
- this.mcastTTL = ttl;\r
- this.mcastBindAddress = bind;\r
- this.timeToExpiration = expireTime;\r
- this.service = service;\r
- this.sendFrequency = sendFrequency;\r
- setupSocket();\r
- sendPacket = new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);\r
- sendPacket.setAddress(address);\r
- sendPacket.setPort(port);\r
- receivePacket = new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);\r
- receivePacket.setAddress(address);\r
- receivePacket.setPort(port);\r
- membership = new Membership(member);\r
- }\r
- \r
- protected void setupSocket() throws IOException {\r
- if (mcastBindAddress != null) socket = new MulticastSocket(new InetSocketAddress(mcastBindAddress, port));\r
- else socket = new MulticastSocket(port);\r
- socket.setLoopbackMode(false); //hint that we don't need loop back messages\r
- if (mcastBindAddress != null) {\r
- if(log.isInfoEnabled())\r
- log.info("Setting multihome multicast interface to:" +mcastBindAddress);\r
- socket.setInterface(mcastBindAddress);\r
- } //end if\r
- //force a so timeout so that we don't block forever\r
- if ( mcastSoTimeout <= 0 ) mcastSoTimeout = (int)sendFrequency;\r
- if(log.isInfoEnabled())\r
- log.info("Setting cluster mcast soTimeout to "+mcastSoTimeout);\r
- socket.setSoTimeout(mcastSoTimeout);\r
-\r
- if ( mcastTTL >= 0 ) {\r
- if(log.isInfoEnabled())\r
- log.info("Setting cluster mcast TTL to " + mcastTTL);\r
- socket.setTimeToLive(mcastTTL);\r
- }\r
- }\r
- \r
- \r
-\r
- /**\r
- * Start the service\r
- * @param level 1 starts the receiver, level 2 starts the sender\r
- * @throws IOException if the service fails to start\r
- * @throws IllegalStateException if the service is already started\r
- */\r
- public synchronized void start(int level) throws IOException {\r
- boolean valid = false;\r
- if ( (level & Channel.MBR_RX_SEQ)==Channel.MBR_RX_SEQ ) {\r
- if ( receiver != null ) throw new IllegalStateException("McastService.receive already running.");\r
- if ( sender == null ) socket.joinGroup(address);\r
- doRunReceiver = true;\r
- receiver = new ReceiverThread();\r
- receiver.setDaemon(true);\r
- receiver.start();\r
- valid = true;\r
- } \r
- if ( (level & Channel.MBR_TX_SEQ)==Channel.MBR_TX_SEQ ) {\r
- if ( sender != null ) throw new IllegalStateException("McastService.send already running.");\r
- if ( receiver == null ) socket.joinGroup(address);\r
- //make sure at least one packet gets out there\r
- send(false);\r
- doRunSender = true;\r
- serviceStartTime = System.currentTimeMillis();\r
- sender = new SenderThread(sendFrequency);\r
- sender.setDaemon(true);\r
- sender.start();\r
- //we have started the receiver, but not yet waited for membership to establish\r
- valid = true;\r
- } \r
- if (!valid) {\r
- throw new IllegalArgumentException("Invalid start level. Only acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ");\r
- }\r
- //pause, once or twice\r
- waitForMembers(level);\r
- startLevel = (startLevel | level);\r
- }\r
-\r
- private void waitForMembers(int level) {\r
- long memberwait = sendFrequency*2;\r
- if(log.isInfoEnabled())\r
- log.info("Sleeping for "+memberwait+" milliseconds to establish cluster membership, start level:"+level);\r
- try {Thread.sleep(memberwait);}catch (InterruptedException ignore){}\r
- if(log.isInfoEnabled())\r
- log.info("Done sleeping, membership established, start level:"+level);\r
- }\r
-\r
- /**\r
- * Stops the service\r
- * @throws IOException if the service fails to disconnect from the sockets\r
- */\r
- public synchronized boolean stop(int level) throws IOException {\r
- boolean valid = false;\r
- \r
- if ( (level & Channel.MBR_RX_SEQ)==Channel.MBR_RX_SEQ ) {\r
- valid = true;\r
- doRunReceiver = false;\r
- if ( receiver !=null ) receiver.interrupt();\r
- receiver = null;\r
- } \r
- if ( (level & Channel.MBR_TX_SEQ)==Channel.MBR_TX_SEQ ) {\r
- valid = true;\r
- doRunSender = false;\r
- if ( sender != null )sender.interrupt();\r
- sender = null;\r
- } \r
- \r
- if (!valid) {\r
- throw new IllegalArgumentException("Invalid stop level. Only acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ");\r
- }\r
- startLevel = (startLevel & (~level));\r
- //we're shutting down, send a shutdown message and close the socket\r
- if ( startLevel == 0 ) {\r
- //send a stop message\r
- member.setCommand(Member.SHUTDOWN_PAYLOAD);\r
- member.getData(true, true);\r
- send(false);\r
- //leave mcast group\r
- try {socket.leaveGroup(address);}catch ( Exception ignore){}\r
- serviceStartTime = Long.MAX_VALUE;\r
- }\r
- return (startLevel == 0);\r
- }\r
-\r
- /**\r
- * Receive a datagram packet, locking wait\r
- * @throws IOException\r
- */\r
- public void receive() throws IOException {\r
- try {\r
- socket.receive(receivePacket);\r
- if(receivePacket.getLength() > MAX_PACKET_SIZE) {\r
- log.error("Multicast packet received was too long, dropping package:"+receivePacket.getLength());\r
- } else {\r
- byte[] data = new byte[receivePacket.getLength()];\r
- System.arraycopy(receivePacket.getData(), receivePacket.getOffset(), data, 0, data.length);\r
- final MemberImpl m = MemberImpl.getMember(data);\r
- if (log.isTraceEnabled()) log.trace("Mcast receive ping from member " + m);\r
- Thread t = null;\r
- if (Arrays.equals(m.getCommand(), Member.SHUTDOWN_PAYLOAD)) {\r
- if (log.isDebugEnabled()) log.debug("Member has shutdown:" + m);\r
- membership.removeMember(m);\r
- t = new Thread() {\r
- public void run() {\r
- service.memberDisappeared(m);\r
- }\r
- };\r
- } else if (membership.memberAlive(m)) {\r
- if (log.isDebugEnabled()) log.debug("Mcast add member " + m);\r
- t = new Thread() {\r
- public void run() {\r
- service.memberAdded(m);\r
- }\r
- };\r
- } //end if\r
- if ( t != null ) t.start();\r
- }\r
- } catch (SocketTimeoutException x ) { \r
- //do nothing, this is normal, we don't want to block forever\r
- //since the receive thread is the same thread\r
- //that does membership expiration\r
- }\r
- checkExpired();\r
- }\r
- \r
- protected Object expiredMutex = new Object();\r
- protected void checkExpired() {\r
- synchronized (expiredMutex) {\r
- MemberImpl[] expired = membership.expire(timeToExpiration);\r
- for (int i = 0; i < expired.length; i++) {\r
- final MemberImpl member = expired[i];\r
- if (log.isDebugEnabled())\r
- log.debug("Mcast exipre member " + expired[i]);\r
- try {\r
- Thread t = new Thread() {\r
- public void run() {\r
- service.memberDisappeared(member);\r
- }\r
- };\r
- t.start();\r
- } catch (Exception x) {\r
- log.error("Unable to process member disappeared message.", x);\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Send a ping\r
- * @throws Exception\r
- */ \r
- public void send(boolean checkexpired) throws IOException{\r
- //ignore if we haven't started the sender\r
- //if ( (startLevel&Channel.MBR_TX_SEQ) != Channel.MBR_TX_SEQ ) return;\r
- member.inc();\r
- if(log.isTraceEnabled())\r
- log.trace("Mcast send ping from member " + member);\r
- byte[] data = member.getData();\r
- DatagramPacket p = new DatagramPacket(data,data.length);\r
- p.setAddress(address);\r
- p.setPort(port);\r
- socket.send(p);\r
- if ( checkexpired ) checkExpired();\r
- }\r
-\r
- public long getServiceStartTime() {\r
- return this.serviceStartTime;\r
- }\r
-\r
-\r
- public class ReceiverThread extends Thread {\r
- public ReceiverThread() {\r
- super();\r
- setName("Cluster-MembershipReceiver");\r
- }\r
- public void run() {\r
- while ( doRunReceiver ) {\r
- try {\r
- receive();\r
- } catch ( ArrayIndexOutOfBoundsException ax ) {\r
- //we can ignore this, as it means we have an invalid package\r
- //but we will log it to debug\r
- if ( log.isDebugEnabled() )\r
- log.debug("Invalid member mcast package.",ax);\r
- } catch ( Exception x ) {\r
- log.warn("Error receiving mcast package. Sleeping 500ms",x);\r
- try { Thread.sleep(500); } catch ( Exception ignore ){}\r
- \r
- }\r
- }\r
- }\r
- }//class ReceiverThread\r
-\r
- public class SenderThread extends Thread {\r
- long time;\r
- public SenderThread(long time) {\r
- this.time = time;\r
- setName("Cluster-MembershipSender");\r
-\r
- }\r
- public void run() {\r
- while ( doRunSender ) {\r
- try {\r
- send(true);\r
- } catch ( Exception x ) {\r
- log.warn("Unable to send mcast message.",x);\r
- }\r
- try { Thread.sleep(time); } catch ( Exception ignore ) {}\r
- }\r
- }\r
- }//class SenderThread\r
-}\r
+/*
+ * 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.apache.catalina.tribes.membership;
+
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+
+import org.apache.catalina.tribes.MembershipListener;
+import java.util.Arrays;
+import java.net.SocketTimeoutException;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.Channel;
+import java.net.InetSocketAddress;
+
+/**
+ * A <b>membership</b> implementation using simple multicast.
+ * This is the representation of a multicast membership service.
+ * This class is responsible for maintaining a list of active cluster nodes in the cluster.
+ * If a node fails to send out a heartbeat, the node will be dismissed.
+ * This is the low level implementation that handles the multicasting sockets.
+ * Need to fix this, could use java.nio and only need one thread to send and receive, or
+ * just use a timeout on the receive
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+public class McastServiceImpl
+{
+ private static org.apache.juli.logging.Log log =
+ org.apache.juli.logging.LogFactory.getLog( McastService.class );
+
+ protected static int MAX_PACKET_SIZE = 65535;
+ /**
+ * Internal flag used for the listen thread that listens to the multicasting socket.
+ */
+ protected boolean doRunSender = false;
+ protected boolean doRunReceiver = false;
+ protected int startLevel = 0;
+ /**
+ * Socket that we intend to listen to
+ */
+ protected MulticastSocket socket;
+ /**
+ * The local member that we intend to broad cast over and over again
+ */
+ protected MemberImpl member;
+ /**
+ * The multicast address
+ */
+ protected InetAddress address;
+ /**
+ * The multicast port
+ */
+ protected int port;
+ /**
+ * The time it takes for a member to expire.
+ */
+ protected long timeToExpiration;
+ /**
+ * How often to we send out a broadcast saying we are alive, must be smaller than timeToExpiration
+ */
+ protected long sendFrequency;
+ /**
+ * Reuse the sendPacket, no need to create a new one everytime
+ */
+ protected DatagramPacket sendPacket;
+ /**
+ * Reuse the receivePacket, no need to create a new one everytime
+ */
+ protected DatagramPacket receivePacket;
+ /**
+ * The membership, used so that we calculate memberships when they arrive or don't arrive
+ */
+ protected Membership membership;
+ /**
+ * The actual listener, for callback when shits goes down
+ */
+ protected MembershipListener service;
+ /**
+ * Thread to listen for pings
+ */
+ protected ReceiverThread receiver;
+ /**
+ * Thread to send pings
+ */
+ protected SenderThread sender;
+
+ /**
+ * When was the service started
+ */
+ protected long serviceStartTime = System.currentTimeMillis();
+
+ /**
+ * Time to live for the multicast packets that are being sent out
+ */
+ protected int mcastTTL = -1;
+ /**
+ * Read timeout on the mcast socket
+ */
+ protected int mcastSoTimeout = -1;
+ /**
+ * bind address
+ */
+ protected InetAddress mcastBindAddress = null;
+
+ /**
+ * Create a new mcast service impl
+ * @param member - the local member
+ * @param sendFrequency - the time (ms) in between pings sent out
+ * @param expireTime - the time (ms) for a member to expire
+ * @param port - the mcast port
+ * @param bind - the bind address (not sure this is used yet)
+ * @param mcastAddress - the mcast address
+ * @param service - the callback service
+ * @throws IOException
+ */
+ public McastServiceImpl(
+ MemberImpl member,
+ long sendFrequency,
+ long expireTime,
+ int port,
+ InetAddress bind,
+ InetAddress mcastAddress,
+ int ttl,
+ int soTimeout,
+ MembershipListener service)
+ throws IOException {
+ this.member = member;
+ this.address = mcastAddress;
+ this.port = port;
+ this.mcastSoTimeout = soTimeout;
+ this.mcastTTL = ttl;
+ this.mcastBindAddress = bind;
+ this.timeToExpiration = expireTime;
+ this.service = service;
+ this.sendFrequency = sendFrequency;
+ setupSocket();
+ sendPacket = new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);
+ sendPacket.setAddress(address);
+ sendPacket.setPort(port);
+ receivePacket = new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);
+ receivePacket.setAddress(address);
+ receivePacket.setPort(port);
+ membership = new Membership(member);
+ }
+
+ protected void setupSocket() throws IOException {
+ if (mcastBindAddress != null) socket = new MulticastSocket(new InetSocketAddress(mcastBindAddress, port));
+ else socket = new MulticastSocket(port);
+ socket.setLoopbackMode(false); //hint that we don't need loop back messages
+ if (mcastBindAddress != null) {
+ if(log.isInfoEnabled())
+ log.info("Setting multihome multicast interface to:" +mcastBindAddress);
+ socket.setInterface(mcastBindAddress);
+ } //end if
+ //force a so timeout so that we don't block forever
+ if ( mcastSoTimeout <= 0 ) mcastSoTimeout = (int)sendFrequency;
+ if(log.isInfoEnabled())
+ log.info("Setting cluster mcast soTimeout to "+mcastSoTimeout);
+ socket.setSoTimeout(mcastSoTimeout);
+
+ if ( mcastTTL >= 0 ) {
+ if(log.isInfoEnabled())
+ log.info("Setting cluster mcast TTL to " + mcastTTL);
+ socket.setTimeToLive(mcastTTL);
+ }
+ }
+
+
+
+ /**
+ * Start the service
+ * @param level 1 starts the receiver, level 2 starts the sender
+ * @throws IOException if the service fails to start
+ * @throws IllegalStateException if the service is already started
+ */
+ public synchronized void start(int level) throws IOException {
+ boolean valid = false;
+ if ( (level & Channel.MBR_RX_SEQ)==Channel.MBR_RX_SEQ ) {
+ if ( receiver != null ) throw new IllegalStateException("McastService.receive already running.");
+ if ( sender == null ) socket.joinGroup(address);
+ doRunReceiver = true;
+ receiver = new ReceiverThread();
+ receiver.setDaemon(true);
+ receiver.start();
+ valid = true;
+ }
+ if ( (level & Channel.MBR_TX_SEQ)==Channel.MBR_TX_SEQ ) {
+ if ( sender != null ) throw new IllegalStateException("McastService.send already running.");
+ if ( receiver == null ) socket.joinGroup(address);
+ //make sure at least one packet gets out there
+ send(false);
+ doRunSender = true;
+ serviceStartTime = System.currentTimeMillis();
+ sender = new SenderThread(sendFrequency);
+ sender.setDaemon(true);
+ sender.start();
+ //we have started the receiver, but not yet waited for membership to establish
+ valid = true;
+ }
+ if (!valid) {
+ throw new IllegalArgumentException("Invalid start level. Only acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ");
+ }
+ //pause, once or twice
+ waitForMembers(level);
+ startLevel = (startLevel | level);
+ }
+
+ private void waitForMembers(int level) {
+ long memberwait = sendFrequency*2;
+ if(log.isInfoEnabled())
+ log.info("Sleeping for "+memberwait+" milliseconds to establish cluster membership, start level:"+level);
+ try {Thread.sleep(memberwait);}catch (InterruptedException ignore){}
+ if(log.isInfoEnabled())
+ log.info("Done sleeping, membership established, start level:"+level);
+ }
+
+ /**
+ * Stops the service
+ * @throws IOException if the service fails to disconnect from the sockets
+ */
+ public synchronized boolean stop(int level) throws IOException {
+ boolean valid = false;
+
+ if ( (level & Channel.MBR_RX_SEQ)==Channel.MBR_RX_SEQ ) {
+ valid = true;
+ doRunReceiver = false;
+ if ( receiver !=null ) receiver.interrupt();
+ receiver = null;
+ }
+ if ( (level & Channel.MBR_TX_SEQ)==Channel.MBR_TX_SEQ ) {
+ valid = true;
+ doRunSender = false;
+ if ( sender != null )sender.interrupt();
+ sender = null;
+ }
+
+ if (!valid) {
+ throw new IllegalArgumentException("Invalid stop level. Only acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ");
+ }
+ startLevel = (startLevel & (~level));
+ //we're shutting down, send a shutdown message and close the socket
+ if ( startLevel == 0 ) {
+ //send a stop message
+ member.setCommand(Member.SHUTDOWN_PAYLOAD);
+ member.getData(true, true);
+ send(false);
+ //leave mcast group
+ try {socket.leaveGroup(address);}catch ( Exception ignore){}
+ serviceStartTime = Long.MAX_VALUE;
+ }
+ return (startLevel == 0);
+ }
+
+ /**
+ * Receive a datagram packet, locking wait
+ * @throws IOException
+ */
+ public void receive() throws IOException {
+ try {
+ socket.receive(receivePacket);
+ if(receivePacket.getLength() > MAX_PACKET_SIZE) {
+ log.error("Multicast packet received was too long, dropping package:"+receivePacket.getLength());
+ } else {
+ byte[] data = new byte[receivePacket.getLength()];
+ System.arraycopy(receivePacket.getData(), receivePacket.getOffset(), data, 0, data.length);
+ final MemberImpl m = MemberImpl.getMember(data);
+ if (log.isTraceEnabled()) log.trace("Mcast receive ping from member " + m);
+ Thread t = null;
+ if (Arrays.equals(m.getCommand(), Member.SHUTDOWN_PAYLOAD)) {
+ if (log.isDebugEnabled()) log.debug("Member has shutdown:" + m);
+ membership.removeMember(m);
+ t = new Thread() {
+ public void run() {
+ service.memberDisappeared(m);
+ }
+ };
+ } else if (membership.memberAlive(m)) {
+ if (log.isDebugEnabled()) log.debug("Mcast add member " + m);
+ t = new Thread() {
+ public void run() {
+ service.memberAdded(m);
+ }
+ };
+ } //end if
+ if ( t != null ) t.start();
+ }
+ } catch (SocketTimeoutException x ) {
+ //do nothing, this is normal, we don't want to block forever
+ //since the receive thread is the same thread
+ //that does membership expiration
+ }
+ checkExpired();
+ }
+
+ protected Object expiredMutex = new Object();
+ protected void checkExpired() {
+ synchronized (expiredMutex) {
+ MemberImpl[] expired = membership.expire(timeToExpiration);
+ for (int i = 0; i < expired.length; i++) {
+ final MemberImpl member = expired[i];
+ if (log.isDebugEnabled())
+ log.debug("Mcast exipre member " + expired[i]);
+ try {
+ Thread t = new Thread() {
+ public void run() {
+ service.memberDisappeared(member);
+ }
+ };
+ t.start();
+ } catch (Exception x) {
+ log.error("Unable to process member disappeared message.", x);
+ }
+ }
+ }
+ }
+
+ /**
+ * Send a ping
+ * @throws Exception
+ */
+ public void send(boolean checkexpired) throws IOException{
+ //ignore if we haven't started the sender
+ //if ( (startLevel&Channel.MBR_TX_SEQ) != Channel.MBR_TX_SEQ ) return;
+ member.inc();
+ if(log.isTraceEnabled())
+ log.trace("Mcast send ping from member " + member);
+ byte[] data = member.getData();
+ DatagramPacket p = new DatagramPacket(data,data.length);
+ p.setAddress(address);
+ p.setPort(port);
+ socket.send(p);
+ if ( checkexpired ) checkExpired();
+ }
+
+ public long getServiceStartTime() {
+ return this.serviceStartTime;
+ }
+
+
+ public class ReceiverThread extends Thread {
+ public ReceiverThread() {
+ super();
+ setName("Cluster-MembershipReceiver");
+ }
+ public void run() {
+ while ( doRunReceiver ) {
+ try {
+ receive();
+ } catch ( ArrayIndexOutOfBoundsException ax ) {
+ //we can ignore this, as it means we have an invalid package
+ //but we will log it to debug
+ if ( log.isDebugEnabled() )
+ log.debug("Invalid member mcast package.",ax);
+ } catch ( Exception x ) {
+ log.warn("Error receiving mcast package. Sleeping 500ms",x);
+ try { Thread.sleep(500); } catch ( Exception ignore ){}
+
+ }
+ }
+ }
+ }//class ReceiverThread
+
+ public class SenderThread extends Thread {
+ long time;
+ public SenderThread(long time) {
+ this.time = time;
+ setName("Cluster-MembershipSender");
+
+ }
+ public void run() {
+ while ( doRunSender ) {
+ try {
+ send(true);
+ } catch ( Exception x ) {
+ log.warn("Unable to send mcast message.",x);
+ }
+ try { Thread.sleep(time); } catch ( Exception ignore ) {}
+ }
+ }
+ }//class SenderThread
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.membership;\r
-\r
-import java.io.IOException;\r
-import java.io.ObjectInput;\r
-import java.io.ObjectOutput;\r
-import java.util.Arrays;\r
-\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-import org.apache.catalina.tribes.transport.SenderState;\r
-\r
-/**\r
- * A <b>membership</b> implementation using simple multicast.\r
- * This is the representation of a multicast member.\r
- * Carries the host, and port of the this or other cluster nodes.\r
- *\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $, $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
- */\r
-public class MemberImpl implements Member, java.io.Externalizable {\r
-\r
- /**\r
- * Public properties specific to this implementation\r
- */\r
- public static final transient String TCP_LISTEN_PORT = "tcpListenPort";\r
- public static final transient String TCP_LISTEN_HOST = "tcpListenHost";\r
- public static final transient String MEMBER_NAME = "memberName";\r
- \r
- public static final transient byte[] TRIBES_MBR_BEGIN = new byte[] {84, 82, 73, 66, 69, 83, 45, 66};\r
- public static final transient byte[] TRIBES_MBR_END = new byte[] {84, 82, 73, 66, 69, 83, 45, 69};\r
- \r
- /**\r
- * The listen host for this member\r
- */\r
- protected byte[] host;\r
- protected transient String hostname;\r
- /**\r
- * The tcp listen port for this member\r
- */\r
- protected int port;\r
- \r
- /**\r
- * The tcp/SSL listen port for this member\r
- */\r
- protected int securePort = -1;\r
-\r
- /**\r
- * Counter for how many broadcast messages have been sent from this member\r
- */\r
- protected int msgCount = 0;\r
- /**\r
- * The number of milliseconds since this members was\r
- * created, is kept track of using the start time\r
- */\r
- protected long memberAliveTime = 0;\r
- \r
- /**\r
- * For the local member only\r
- */\r
- protected transient long serviceStartTime;\r
- \r
- /**\r
- * To avoid serialization over and over again, once the local dataPkg\r
- * has been set, we use that to transmit data\r
- */\r
- protected transient byte[] dataPkg = null;\r
-\r
- /**\r
- * Unique session Id for this member\r
- */\r
- protected byte[] uniqueId = new byte[16];\r
- \r
- /**\r
- * Custom payload that an app framework can broadcast\r
- * Also used to transport stop command.\r
- */\r
- protected byte[] payload = new byte[0];\r
- \r
- /**\r
- * Command, so that the custom payload doesn't have to be used\r
- * This is for internal tribes use, such as SHUTDOWN_COMMAND\r
- */\r
- protected byte[] command = new byte[0];\r
-\r
- /**\r
- * Domain if we want to filter based on domain.\r
- */\r
- protected byte[] domain = new byte[0];\r
- \r
- /**\r
- * Empty constructor for serialization\r
- */\r
- public MemberImpl() {\r
- \r
- }\r
-\r
- /**\r
- * Construct a new member object\r
- * @param name - the name of this member, cluster unique\r
- * @param domain - the cluster domain name of this member\r
- * @param host - the tcp listen host\r
- * @param port - the tcp listen port\r
- */\r
- public MemberImpl(String host,\r
- int port,\r
- long aliveTime) throws IOException {\r
- setHostname(host);\r
- this.port = port;\r
- this.memberAliveTime=aliveTime;\r
- }\r
- \r
- public MemberImpl(String host,\r
- int port,\r
- long aliveTime,\r
- byte[] payload) throws IOException {\r
- this(host,port,aliveTime);\r
- setPayload(payload);\r
- }\r
- \r
- public boolean isReady() {\r
- return SenderState.getSenderState(this).isReady();\r
- }\r
- public boolean isSuspect() {\r
- return SenderState.getSenderState(this).isSuspect();\r
- }\r
- public boolean isFailing() {\r
- return SenderState.getSenderState(this).isFailing();\r
- }\r
-\r
- /**\r
- * Increment the message count.\r
- */\r
- protected void inc() {\r
- msgCount++;\r
- }\r
-\r
- /**\r
- * Create a data package to send over the wire representing this member.\r
- * This is faster than serialization.\r
- * @return - the bytes for this member deserialized\r
- * @throws Exception\r
- */\r
- public byte[] getData() {\r
- return getData(true);\r
- }\r
- /**\r
- * Highly optimized version of serializing a member into a byte array\r
- * Returns a cached byte[] reference, do not modify this data\r
- * @param getalive boolean\r
- * @return byte[]\r
- */\r
- public byte[] getData(boolean getalive) {\r
- return getData(getalive,false);\r
- }\r
- \r
- \r
- public int getDataLength() {\r
- return TRIBES_MBR_BEGIN.length+ //start pkg\r
- 4+ //data length\r
- 8+ //alive time\r
- 4+ //port\r
- 4+ //secure port\r
- 1+ //host length\r
- host.length+ //host\r
- 4+ //command length\r
- command.length+ //command\r
- 4+ //domain length\r
- domain.length+ //domain\r
- 16+ //unique id\r
- 4+ //payload length\r
- payload.length+ //payload\r
- TRIBES_MBR_END.length; //end pkg\r
- }\r
- \r
- /**\r
- * \r
- * @param getalive boolean - calculate memberAlive time\r
- * @param reset boolean - reset the cached data package, and create a new one\r
- * @return byte[]\r
- */\r
- public byte[] getData(boolean getalive, boolean reset) {\r
- if ( reset ) dataPkg = null;\r
- //look in cache first\r
- if ( dataPkg!=null ) {\r
- if ( getalive ) {\r
- //you'd be surprised, but System.currentTimeMillis\r
- //shows up on the profiler\r
- long alive=System.currentTimeMillis()-getServiceStartTime();\r
- XByteBuffer.toBytes( (long) alive, dataPkg, TRIBES_MBR_BEGIN.length+4);\r
- }\r
- return dataPkg;\r
- }\r
- \r
- //package looks like\r
- //start package TRIBES_MBR_BEGIN.length\r
- //package length - 4 bytes\r
- //alive - 8 bytes\r
- //port - 4 bytes\r
- //secure port - 4 bytes\r
- //host length - 1 byte\r
- //host - hl bytes\r
- //clen - 4 bytes\r
- //command - clen bytes\r
- //dlen - 4 bytes\r
- //domain - dlen bytes\r
- //uniqueId - 16 bytes\r
- //payload length - 4 bytes\r
- //payload plen bytes\r
- //end package TRIBES_MBR_END.length\r
- byte[] addr = host;\r
- long alive=System.currentTimeMillis()-getServiceStartTime();\r
- byte hl = (byte)addr.length;\r
- byte[] data = new byte[getDataLength()];\r
- \r
- int bodylength = (getDataLength() - TRIBES_MBR_BEGIN.length - TRIBES_MBR_END.length - 4);\r
- \r
- int pos = 0;\r
- \r
- //TRIBES_MBR_BEGIN\r
- System.arraycopy(TRIBES_MBR_BEGIN,0,data,pos,TRIBES_MBR_BEGIN.length);\r
- pos += TRIBES_MBR_BEGIN.length;\r
- \r
- //body length\r
- XByteBuffer.toBytes(bodylength,data,pos);\r
- pos += 4;\r
- \r
- //alive data\r
- XByteBuffer.toBytes((long)alive,data,pos);\r
- pos += 8;\r
- //port\r
- XByteBuffer.toBytes(port,data,pos);\r
- pos += 4;\r
- //secure port\r
- XByteBuffer.toBytes(securePort,data,pos);\r
- pos += 4;\r
- //host length\r
- data[pos++] = hl;\r
- //host\r
- System.arraycopy(addr,0,data,pos,addr.length);\r
- pos+=addr.length;\r
- //clen - 4 bytes\r
- XByteBuffer.toBytes(command.length,data,pos);\r
- pos+=4;\r
- //command - clen bytes\r
- System.arraycopy(command,0,data,pos,command.length);\r
- pos+=command.length;\r
- //dlen - 4 bytes\r
- XByteBuffer.toBytes(domain.length,data,pos);\r
- pos+=4;\r
- //domain - dlen bytes\r
- System.arraycopy(domain,0,data,pos,domain.length);\r
- pos+=domain.length;\r
- //unique Id\r
- System.arraycopy(uniqueId,0,data,pos,uniqueId.length);\r
- pos+=uniqueId.length;\r
- //payload\r
- XByteBuffer.toBytes(payload.length,data,pos);\r
- pos+=4;\r
- System.arraycopy(payload,0,data,pos,payload.length);\r
- pos+=payload.length;\r
- \r
- //TRIBES_MBR_END\r
- System.arraycopy(TRIBES_MBR_END,0,data,pos,TRIBES_MBR_END.length);\r
- pos += TRIBES_MBR_END.length;\r
-\r
- //create local data\r
- dataPkg = data;\r
- return data;\r
- }\r
- /**\r
- * Deserializes a member from data sent over the wire\r
- * @param data - the bytes received\r
- * @return a member object.\r
- */\r
- public static MemberImpl getMember(byte[] data, MemberImpl member) {\r
- return getMember(data,0,data.length,member);\r
- }\r
-\r
- public static MemberImpl getMember(byte[] data, int offset, int length, MemberImpl member) {\r
- //package looks like\r
- //start package TRIBES_MBR_BEGIN.length\r
- //package length - 4 bytes\r
- //alive - 8 bytes\r
- //port - 4 bytes\r
- //secure port - 4 bytes\r
- //host length - 1 byte\r
- //host - hl bytes\r
- //clen - 4 bytes\r
- //command - clen bytes\r
- //dlen - 4 bytes\r
- //domain - dlen bytes\r
- //uniqueId - 16 bytes\r
- //payload length - 4 bytes\r
- //payload plen bytes\r
- //end package TRIBES_MBR_END.length\r
-\r
- int pos = offset;\r
- \r
- if (XByteBuffer.firstIndexOf(data,offset,TRIBES_MBR_BEGIN)!=pos) {\r
- throw new IllegalArgumentException("Invalid package, should start with:"+org.apache.catalina.tribes.util.Arrays.toString(TRIBES_MBR_BEGIN));\r
- }\r
-\r
- if ( length < (TRIBES_MBR_BEGIN.length+4) ) {\r
- throw new ArrayIndexOutOfBoundsException("Member package to small to validate.");\r
- }\r
- \r
- pos += TRIBES_MBR_BEGIN.length;\r
- \r
- int bodylength = XByteBuffer.toInt(data,pos);\r
- pos += 4;\r
- \r
- if ( length < (bodylength+4+TRIBES_MBR_BEGIN.length+TRIBES_MBR_END.length) ) {\r
- throw new ArrayIndexOutOfBoundsException("Not enough bytes in member package.");\r
- }\r
- \r
- int endpos = pos+bodylength;\r
- if (XByteBuffer.firstIndexOf(data,endpos,TRIBES_MBR_END)!=endpos) {\r
- throw new IllegalArgumentException("Invalid package, should end with:"+org.apache.catalina.tribes.util.Arrays.toString(TRIBES_MBR_END));\r
- }\r
-\r
-\r
- byte[] alived = new byte[8];\r
- System.arraycopy(data, pos, alived, 0, 8);\r
- pos += 8;\r
- byte[] portd = new byte[4];\r
- System.arraycopy(data, pos, portd, 0, 4);\r
- pos += 4;\r
- \r
- byte[] sportd = new byte[4];\r
- System.arraycopy(data, pos, sportd, 0, 4);\r
- pos += 4;\r
-\r
-\r
- \r
- byte hl = data[pos++];\r
- byte[] addr = new byte[hl];\r
- System.arraycopy(data, pos, addr, 0, hl);\r
- pos += hl;\r
- \r
- int cl = XByteBuffer.toInt(data, pos);\r
- pos += 4;\r
- \r
- byte[] command = new byte[cl];\r
- System.arraycopy(data, pos, command, 0, command.length);\r
- pos += command.length;\r
- \r
- int dl = XByteBuffer.toInt(data, pos);\r
- pos += 4;\r
- \r
- byte[] domain = new byte[dl];\r
- System.arraycopy(data, pos, domain, 0, domain.length);\r
- pos += domain.length;\r
- \r
- byte[] uniqueId = new byte[16];\r
- System.arraycopy(data, pos, uniqueId, 0, 16);\r
- pos += 16;\r
- \r
- int pl = XByteBuffer.toInt(data, pos);\r
- pos += 4;\r
- \r
- byte[] payload = new byte[pl];\r
- System.arraycopy(data, pos, payload, 0, payload.length);\r
- pos += payload.length;\r
- \r
- member.setHost(addr);\r
- member.setPort(XByteBuffer.toInt(portd, 0));\r
- member.setSecurePort(XByteBuffer.toInt(sportd, 0));\r
- member.setMemberAliveTime(XByteBuffer.toLong(alived, 0));\r
- member.setUniqueId(uniqueId);\r
- member.payload = payload;\r
- member.domain = domain;\r
- member.command = command;\r
- \r
- member.dataPkg = new byte[length];\r
- System.arraycopy(data, offset, member.dataPkg, 0, length);\r
- \r
- return member;\r
- }\r
-\r
- public static MemberImpl getMember(byte[] data) {\r
- return getMember(data,new MemberImpl());\r
- }\r
-\r
- /**\r
- * Return the name of this object\r
- * @return a unique name to the cluster\r
- */\r
- public String getName() {\r
- return "tcp://"+getHostname()+":"+getPort();\r
- }\r
- \r
- /**\r
- * Return the listen port of this member\r
- * @return - tcp listen port\r
- */\r
- public int getPort() {\r
- return this.port;\r
- }\r
-\r
- /**\r
- * Return the TCP listen host for this member\r
- * @return IP address or host name\r
- */\r
- public byte[] getHost() {\r
- return host;\r
- }\r
- \r
- public String getHostname() {\r
- if ( this.hostname != null ) return hostname;\r
- else {\r
- try {\r
- this.hostname = java.net.InetAddress.getByAddress(host).getHostName();\r
- return this.hostname;\r
- }catch ( IOException x ) {\r
- throw new RuntimeException("Unable to parse hostname.",x);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Contains information on how long this member has been online.\r
- * The result is the number of milli seconds this member has been\r
- * broadcasting its membership to the cluster.\r
- * @return nr of milliseconds since this member started.\r
- */\r
- public long getMemberAliveTime() {\r
- return memberAliveTime;\r
- }\r
-\r
- public long getServiceStartTime() {\r
- return serviceStartTime;\r
- }\r
-\r
- public byte[] getUniqueId() {\r
- return uniqueId;\r
- }\r
-\r
- public byte[] getPayload() {\r
- return payload;\r
- }\r
-\r
- public byte[] getCommand() {\r
- return command;\r
- }\r
-\r
- public byte[] getDomain() {\r
- return domain;\r
- }\r
-\r
- public int getSecurePort() {\r
- return securePort;\r
- }\r
-\r
- public void setMemberAliveTime(long time) {\r
- memberAliveTime=time;\r
- }\r
-\r
-\r
-\r
- /**\r
- * String representation of this object\r
- */\r
- public String toString() {\r
- StringBuffer buf = new StringBuffer("org.apache.catalina.tribes.membership.MemberImpl[");\r
- buf.append(getName()).append(",");\r
- buf.append(getHostname()).append(",");\r
- buf.append(port).append(", alive=");\r
- buf.append(memberAliveTime).append(",");\r
- buf.append("id=").append(bToS(this.uniqueId)).append(", ");\r
- buf.append("payload=").append(bToS(this.payload,8)).append(", ");\r
- buf.append("command=").append(bToS(this.command,8)).append(", ");\r
- buf.append("domain=").append(bToS(this.domain,8)).append(", ");\r
- buf.append("]");\r
- return buf.toString();\r
- }\r
- public static String bToS(byte[] data) {\r
- return bToS(data,data.length);\r
- }\r
- public static String bToS(byte[] data, int max) {\r
- StringBuffer buf = new StringBuffer(4*16);\r
- buf.append("{");\r
- for (int i=0; data!=null && i<data.length; i++ ) {\r
- buf.append(String.valueOf(data[i])).append(" ");\r
- if ( i==max ) {\r
- buf.append("...("+data.length+")");\r
- break;\r
- }\r
- }\r
- buf.append("}");\r
- return buf.toString();\r
- }\r
-\r
- /**\r
- * @see java.lang.Object#hashCode()\r
- * @return The hash code\r
- */\r
- public int hashCode() {\r
- return getHost()[0]+getHost()[1]+getHost()[2]+getHost()[3];\r
- }\r
-\r
- /**\r
- * Returns true if the param o is a McastMember with the same name\r
- * @param o\r
- */\r
- public boolean equals(Object o) {\r
- if ( o instanceof MemberImpl ) {\r
- return Arrays.equals(this.getHost(),((MemberImpl)o).getHost()) &&\r
- this.getPort() == ((MemberImpl)o).getPort() &&\r
- Arrays.equals(this.getUniqueId(),((MemberImpl)o).getUniqueId());\r
- }\r
- else\r
- return false;\r
- }\r
- \r
- public void setHost(byte[] host) {\r
- this.host = host;\r
- }\r
- \r
- public void setHostname(String host) throws IOException {\r
- hostname = host;\r
- this.host = java.net.InetAddress.getByName(host).getAddress();\r
- }\r
- \r
- public void setMsgCount(int msgCount) {\r
- this.msgCount = msgCount;\r
- }\r
-\r
- public void setPort(int port) {\r
- this.port = port;\r
- this.dataPkg = null;\r
- }\r
-\r
- public void setServiceStartTime(long serviceStartTime) {\r
- this.serviceStartTime = serviceStartTime;\r
- }\r
-\r
- public void setUniqueId(byte[] uniqueId) {\r
- this.uniqueId = uniqueId!=null?uniqueId:new byte[16];\r
- getData(true,true);\r
- }\r
-\r
- public void setPayload(byte[] payload) {\r
- byte[] oldpayload = this.payload;\r
- this.payload = payload!=null?payload:new byte[0];\r
- if ( this.getData(true,true).length > McastServiceImpl.MAX_PACKET_SIZE ) {\r
- this.payload = oldpayload;\r
- throw new IllegalArgumentException("Payload is to large for tribes to handle.");\r
- }\r
- \r
- }\r
-\r
- public void setCommand(byte[] command) {\r
- this.command = command!=null?command:new byte[0];\r
- getData(true,true);\r
- }\r
-\r
- public void setDomain(byte[] domain) {\r
- this.domain = domain!=null?domain:new byte[0];\r
- getData(true,true);\r
- }\r
-\r
- public void setSecurePort(int securePort) {\r
- this.securePort = securePort;\r
- }\r
-\r
- public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {\r
- int length = in.readInt();\r
- byte[] message = new byte[length];\r
- in.read(message);\r
- getMember(message,this);\r
- \r
- }\r
-\r
- public void writeExternal(ObjectOutput out) throws IOException {\r
- byte[] data = this.getData();\r
- out.writeInt(data.length);\r
- out.write(data);\r
- }\r
- \r
-}\r
+/*
+ * 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.apache.catalina.tribes.membership;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Arrays;
+
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.catalina.tribes.transport.SenderState;
+
+/**
+ * A <b>membership</b> implementation using simple multicast.
+ * This is the representation of a multicast member.
+ * Carries the host, and port of the this or other cluster nodes.
+ *
+ * @author Filip Hanik
+ * @version $Revision$, $Date$
+ */
+public class MemberImpl implements Member, java.io.Externalizable {
+
+ /**
+ * Public properties specific to this implementation
+ */
+ public static final transient String TCP_LISTEN_PORT = "tcpListenPort";
+ public static final transient String TCP_LISTEN_HOST = "tcpListenHost";
+ public static final transient String MEMBER_NAME = "memberName";
+
+ public static final transient byte[] TRIBES_MBR_BEGIN = new byte[] {84, 82, 73, 66, 69, 83, 45, 66};
+ public static final transient byte[] TRIBES_MBR_END = new byte[] {84, 82, 73, 66, 69, 83, 45, 69};
+
+ /**
+ * The listen host for this member
+ */
+ protected byte[] host;
+ protected transient String hostname;
+ /**
+ * The tcp listen port for this member
+ */
+ protected int port;
+
+ /**
+ * The tcp/SSL listen port for this member
+ */
+ protected int securePort = -1;
+
+ /**
+ * Counter for how many broadcast messages have been sent from this member
+ */
+ protected int msgCount = 0;
+ /**
+ * The number of milliseconds since this members was
+ * created, is kept track of using the start time
+ */
+ protected long memberAliveTime = 0;
+
+ /**
+ * For the local member only
+ */
+ protected transient long serviceStartTime;
+
+ /**
+ * To avoid serialization over and over again, once the local dataPkg
+ * has been set, we use that to transmit data
+ */
+ protected transient byte[] dataPkg = null;
+
+ /**
+ * Unique session Id for this member
+ */
+ protected byte[] uniqueId = new byte[16];
+
+ /**
+ * Custom payload that an app framework can broadcast
+ * Also used to transport stop command.
+ */
+ protected byte[] payload = new byte[0];
+
+ /**
+ * Command, so that the custom payload doesn't have to be used
+ * This is for internal tribes use, such as SHUTDOWN_COMMAND
+ */
+ protected byte[] command = new byte[0];
+
+ /**
+ * Domain if we want to filter based on domain.
+ */
+ protected byte[] domain = new byte[0];
+
+ /**
+ * Empty constructor for serialization
+ */
+ public MemberImpl() {
+
+ }
+
+ /**
+ * Construct a new member object
+ * @param name - the name of this member, cluster unique
+ * @param domain - the cluster domain name of this member
+ * @param host - the tcp listen host
+ * @param port - the tcp listen port
+ */
+ public MemberImpl(String host,
+ int port,
+ long aliveTime) throws IOException {
+ setHostname(host);
+ this.port = port;
+ this.memberAliveTime=aliveTime;
+ }
+
+ public MemberImpl(String host,
+ int port,
+ long aliveTime,
+ byte[] payload) throws IOException {
+ this(host,port,aliveTime);
+ setPayload(payload);
+ }
+
+ public boolean isReady() {
+ return SenderState.getSenderState(this).isReady();
+ }
+ public boolean isSuspect() {
+ return SenderState.getSenderState(this).isSuspect();
+ }
+ public boolean isFailing() {
+ return SenderState.getSenderState(this).isFailing();
+ }
+
+ /**
+ * Increment the message count.
+ */
+ protected void inc() {
+ msgCount++;
+ }
+
+ /**
+ * Create a data package to send over the wire representing this member.
+ * This is faster than serialization.
+ * @return - the bytes for this member deserialized
+ * @throws Exception
+ */
+ public byte[] getData() {
+ return getData(true);
+ }
+ /**
+ * Highly optimized version of serializing a member into a byte array
+ * Returns a cached byte[] reference, do not modify this data
+ * @param getalive boolean
+ * @return byte[]
+ */
+ public byte[] getData(boolean getalive) {
+ return getData(getalive,false);
+ }
+
+
+ public int getDataLength() {
+ return TRIBES_MBR_BEGIN.length+ //start pkg
+ 4+ //data length
+ 8+ //alive time
+ 4+ //port
+ 4+ //secure port
+ 1+ //host length
+ host.length+ //host
+ 4+ //command length
+ command.length+ //command
+ 4+ //domain length
+ domain.length+ //domain
+ 16+ //unique id
+ 4+ //payload length
+ payload.length+ //payload
+ TRIBES_MBR_END.length; //end pkg
+ }
+
+ /**
+ *
+ * @param getalive boolean - calculate memberAlive time
+ * @param reset boolean - reset the cached data package, and create a new one
+ * @return byte[]
+ */
+ public byte[] getData(boolean getalive, boolean reset) {
+ if ( reset ) dataPkg = null;
+ //look in cache first
+ if ( dataPkg!=null ) {
+ if ( getalive ) {
+ //you'd be surprised, but System.currentTimeMillis
+ //shows up on the profiler
+ long alive=System.currentTimeMillis()-getServiceStartTime();
+ XByteBuffer.toBytes( (long) alive, dataPkg, TRIBES_MBR_BEGIN.length+4);
+ }
+ return dataPkg;
+ }
+
+ //package looks like
+ //start package TRIBES_MBR_BEGIN.length
+ //package length - 4 bytes
+ //alive - 8 bytes
+ //port - 4 bytes
+ //secure port - 4 bytes
+ //host length - 1 byte
+ //host - hl bytes
+ //clen - 4 bytes
+ //command - clen bytes
+ //dlen - 4 bytes
+ //domain - dlen bytes
+ //uniqueId - 16 bytes
+ //payload length - 4 bytes
+ //payload plen bytes
+ //end package TRIBES_MBR_END.length
+ byte[] addr = host;
+ long alive=System.currentTimeMillis()-getServiceStartTime();
+ byte hl = (byte)addr.length;
+ byte[] data = new byte[getDataLength()];
+
+ int bodylength = (getDataLength() - TRIBES_MBR_BEGIN.length - TRIBES_MBR_END.length - 4);
+
+ int pos = 0;
+
+ //TRIBES_MBR_BEGIN
+ System.arraycopy(TRIBES_MBR_BEGIN,0,data,pos,TRIBES_MBR_BEGIN.length);
+ pos += TRIBES_MBR_BEGIN.length;
+
+ //body length
+ XByteBuffer.toBytes(bodylength,data,pos);
+ pos += 4;
+
+ //alive data
+ XByteBuffer.toBytes((long)alive,data,pos);
+ pos += 8;
+ //port
+ XByteBuffer.toBytes(port,data,pos);
+ pos += 4;
+ //secure port
+ XByteBuffer.toBytes(securePort,data,pos);
+ pos += 4;
+ //host length
+ data[pos++] = hl;
+ //host
+ System.arraycopy(addr,0,data,pos,addr.length);
+ pos+=addr.length;
+ //clen - 4 bytes
+ XByteBuffer.toBytes(command.length,data,pos);
+ pos+=4;
+ //command - clen bytes
+ System.arraycopy(command,0,data,pos,command.length);
+ pos+=command.length;
+ //dlen - 4 bytes
+ XByteBuffer.toBytes(domain.length,data,pos);
+ pos+=4;
+ //domain - dlen bytes
+ System.arraycopy(domain,0,data,pos,domain.length);
+ pos+=domain.length;
+ //unique Id
+ System.arraycopy(uniqueId,0,data,pos,uniqueId.length);
+ pos+=uniqueId.length;
+ //payload
+ XByteBuffer.toBytes(payload.length,data,pos);
+ pos+=4;
+ System.arraycopy(payload,0,data,pos,payload.length);
+ pos+=payload.length;
+
+ //TRIBES_MBR_END
+ System.arraycopy(TRIBES_MBR_END,0,data,pos,TRIBES_MBR_END.length);
+ pos += TRIBES_MBR_END.length;
+
+ //create local data
+ dataPkg = data;
+ return data;
+ }
+ /**
+ * Deserializes a member from data sent over the wire
+ * @param data - the bytes received
+ * @return a member object.
+ */
+ public static MemberImpl getMember(byte[] data, MemberImpl member) {
+ return getMember(data,0,data.length,member);
+ }
+
+ public static MemberImpl getMember(byte[] data, int offset, int length, MemberImpl member) {
+ //package looks like
+ //start package TRIBES_MBR_BEGIN.length
+ //package length - 4 bytes
+ //alive - 8 bytes
+ //port - 4 bytes
+ //secure port - 4 bytes
+ //host length - 1 byte
+ //host - hl bytes
+ //clen - 4 bytes
+ //command - clen bytes
+ //dlen - 4 bytes
+ //domain - dlen bytes
+ //uniqueId - 16 bytes
+ //payload length - 4 bytes
+ //payload plen bytes
+ //end package TRIBES_MBR_END.length
+
+ int pos = offset;
+
+ if (XByteBuffer.firstIndexOf(data,offset,TRIBES_MBR_BEGIN)!=pos) {
+ throw new IllegalArgumentException("Invalid package, should start with:"+org.apache.catalina.tribes.util.Arrays.toString(TRIBES_MBR_BEGIN));
+ }
+
+ if ( length < (TRIBES_MBR_BEGIN.length+4) ) {
+ throw new ArrayIndexOutOfBoundsException("Member package to small to validate.");
+ }
+
+ pos += TRIBES_MBR_BEGIN.length;
+
+ int bodylength = XByteBuffer.toInt(data,pos);
+ pos += 4;
+
+ if ( length < (bodylength+4+TRIBES_MBR_BEGIN.length+TRIBES_MBR_END.length) ) {
+ throw new ArrayIndexOutOfBoundsException("Not enough bytes in member package.");
+ }
+
+ int endpos = pos+bodylength;
+ if (XByteBuffer.firstIndexOf(data,endpos,TRIBES_MBR_END)!=endpos) {
+ throw new IllegalArgumentException("Invalid package, should end with:"+org.apache.catalina.tribes.util.Arrays.toString(TRIBES_MBR_END));
+ }
+
+
+ byte[] alived = new byte[8];
+ System.arraycopy(data, pos, alived, 0, 8);
+ pos += 8;
+ byte[] portd = new byte[4];
+ System.arraycopy(data, pos, portd, 0, 4);
+ pos += 4;
+
+ byte[] sportd = new byte[4];
+ System.arraycopy(data, pos, sportd, 0, 4);
+ pos += 4;
+
+
+
+ byte hl = data[pos++];
+ byte[] addr = new byte[hl];
+ System.arraycopy(data, pos, addr, 0, hl);
+ pos += hl;
+
+ int cl = XByteBuffer.toInt(data, pos);
+ pos += 4;
+
+ byte[] command = new byte[cl];
+ System.arraycopy(data, pos, command, 0, command.length);
+ pos += command.length;
+
+ int dl = XByteBuffer.toInt(data, pos);
+ pos += 4;
+
+ byte[] domain = new byte[dl];
+ System.arraycopy(data, pos, domain, 0, domain.length);
+ pos += domain.length;
+
+ byte[] uniqueId = new byte[16];
+ System.arraycopy(data, pos, uniqueId, 0, 16);
+ pos += 16;
+
+ int pl = XByteBuffer.toInt(data, pos);
+ pos += 4;
+
+ byte[] payload = new byte[pl];
+ System.arraycopy(data, pos, payload, 0, payload.length);
+ pos += payload.length;
+
+ member.setHost(addr);
+ member.setPort(XByteBuffer.toInt(portd, 0));
+ member.setSecurePort(XByteBuffer.toInt(sportd, 0));
+ member.setMemberAliveTime(XByteBuffer.toLong(alived, 0));
+ member.setUniqueId(uniqueId);
+ member.payload = payload;
+ member.domain = domain;
+ member.command = command;
+
+ member.dataPkg = new byte[length];
+ System.arraycopy(data, offset, member.dataPkg, 0, length);
+
+ return member;
+ }
+
+ public static MemberImpl getMember(byte[] data) {
+ return getMember(data,new MemberImpl());
+ }
+
+ /**
+ * Return the name of this object
+ * @return a unique name to the cluster
+ */
+ public String getName() {
+ return "tcp://"+getHostname()+":"+getPort();
+ }
+
+ /**
+ * Return the listen port of this member
+ * @return - tcp listen port
+ */
+ public int getPort() {
+ return this.port;
+ }
+
+ /**
+ * Return the TCP listen host for this member
+ * @return IP address or host name
+ */
+ public byte[] getHost() {
+ return host;
+ }
+
+ public String getHostname() {
+ if ( this.hostname != null ) return hostname;
+ else {
+ try {
+ this.hostname = java.net.InetAddress.getByAddress(host).getHostName();
+ return this.hostname;
+ }catch ( IOException x ) {
+ throw new RuntimeException("Unable to parse hostname.",x);
+ }
+ }
+ }
+
+ /**
+ * Contains information on how long this member has been online.
+ * The result is the number of milli seconds this member has been
+ * broadcasting its membership to the cluster.
+ * @return nr of milliseconds since this member started.
+ */
+ public long getMemberAliveTime() {
+ return memberAliveTime;
+ }
+
+ public long getServiceStartTime() {
+ return serviceStartTime;
+ }
+
+ public byte[] getUniqueId() {
+ return uniqueId;
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+
+ public byte[] getCommand() {
+ return command;
+ }
+
+ public byte[] getDomain() {
+ return domain;
+ }
+
+ public int getSecurePort() {
+ return securePort;
+ }
+
+ public void setMemberAliveTime(long time) {
+ memberAliveTime=time;
+ }
+
+
+
+ /**
+ * String representation of this object
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer("org.apache.catalina.tribes.membership.MemberImpl[");
+ buf.append(getName()).append(",");
+ buf.append(getHostname()).append(",");
+ buf.append(port).append(", alive=");
+ buf.append(memberAliveTime).append(",");
+ buf.append("id=").append(bToS(this.uniqueId)).append(", ");
+ buf.append("payload=").append(bToS(this.payload,8)).append(", ");
+ buf.append("command=").append(bToS(this.command,8)).append(", ");
+ buf.append("domain=").append(bToS(this.domain,8)).append(", ");
+ buf.append("]");
+ return buf.toString();
+ }
+ public static String bToS(byte[] data) {
+ return bToS(data,data.length);
+ }
+ public static String bToS(byte[] data, int max) {
+ StringBuffer buf = new StringBuffer(4*16);
+ buf.append("{");
+ for (int i=0; data!=null && i<data.length; i++ ) {
+ buf.append(String.valueOf(data[i])).append(" ");
+ if ( i==max ) {
+ buf.append("...("+data.length+")");
+ break;
+ }
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ * @return The hash code
+ */
+ public int hashCode() {
+ return getHost()[0]+getHost()[1]+getHost()[2]+getHost()[3];
+ }
+
+ /**
+ * Returns true if the param o is a McastMember with the same name
+ * @param o
+ */
+ public boolean equals(Object o) {
+ if ( o instanceof MemberImpl ) {
+ return Arrays.equals(this.getHost(),((MemberImpl)o).getHost()) &&
+ this.getPort() == ((MemberImpl)o).getPort() &&
+ Arrays.equals(this.getUniqueId(),((MemberImpl)o).getUniqueId());
+ }
+ else
+ return false;
+ }
+
+ public void setHost(byte[] host) {
+ this.host = host;
+ }
+
+ public void setHostname(String host) throws IOException {
+ hostname = host;
+ this.host = java.net.InetAddress.getByName(host).getAddress();
+ }
+
+ public void setMsgCount(int msgCount) {
+ this.msgCount = msgCount;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ this.dataPkg = null;
+ }
+
+ public void setServiceStartTime(long serviceStartTime) {
+ this.serviceStartTime = serviceStartTime;
+ }
+
+ public void setUniqueId(byte[] uniqueId) {
+ this.uniqueId = uniqueId!=null?uniqueId:new byte[16];
+ getData(true,true);
+ }
+
+ public void setPayload(byte[] payload) {
+ byte[] oldpayload = this.payload;
+ this.payload = payload!=null?payload:new byte[0];
+ if ( this.getData(true,true).length > McastServiceImpl.MAX_PACKET_SIZE ) {
+ this.payload = oldpayload;
+ throw new IllegalArgumentException("Payload is to large for tribes to handle.");
+ }
+
+ }
+
+ public void setCommand(byte[] command) {
+ this.command = command!=null?command:new byte[0];
+ getData(true,true);
+ }
+
+ public void setDomain(byte[] domain) {
+ this.domain = domain!=null?domain:new byte[0];
+ getData(true,true);
+ }
+
+ public void setSecurePort(int securePort) {
+ this.securePort = securePort;
+ }
+
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ int length = in.readInt();
+ byte[] message = new byte[length];
+ in.read(message);
+ getMember(message,this);
+
+ }
+
+ public void writeExternal(ObjectOutput out) throws IOException {
+ byte[] data = this.getData();
+ out.writeInt(data.length);
+ out.write(data);
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.membership;\r
-\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.Map;\r
-\r
-import org.apache.catalina.tribes.Member;\r
-import java.util.Comparator;\r
-\r
-/**\r
- * A <b>membership</b> implementation using simple multicast.\r
- * This is the representation of a multicast membership.\r
- * This class is responsible for maintaining a list of active cluster nodes in the cluster.\r
- * If a node fails to send out a heartbeat, the node will be dismissed.\r
- *\r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * @version $Revision: 356540 $, $Date: 2005-12-13 10:53:40 -0600 (Tue, 13 Dec 2005) $\r
- */\r
-public class Membership\r
-{\r
- protected static final MemberImpl[] EMPTY_MEMBERS = new MemberImpl[0];\r
- \r
- /**\r
- * The name of this membership, has to be the same as the name for the local\r
- * member\r
- */\r
- protected MemberImpl local;\r
- \r
- /**\r
- * A map of all the members in the cluster.\r
- */\r
- protected HashMap map = new HashMap();\r
- \r
- /**\r
- * A list of all the members in the cluster.\r
- */\r
- protected MemberImpl[] members = EMPTY_MEMBERS;\r
- \r
- /**\r
- * sort members by alive time\r
- */\r
- protected Comparator memberComparator = new MemberComparator();\r
-\r
- public Object clone() {\r
- synchronized (members) {\r
- Membership clone = new Membership(local, memberComparator);\r
- clone.map = (HashMap) map.clone();\r
- clone.members = new MemberImpl[members.length];\r
- System.arraycopy(members,0,clone.members,0,members.length);\r
- return clone;\r
- }\r
- }\r
-\r
- /**\r
- * Constructs a new membership\r
- * @param name - has to be the name of the local member. Used to filter the local member from the cluster membership\r
- */\r
- public Membership(MemberImpl local, boolean includeLocal) {\r
- this.local = local;\r
- if ( includeLocal ) addMember(local);\r
- }\r
-\r
- public Membership(MemberImpl local) {\r
- this(local,false);\r
- }\r
-\r
- public Membership(MemberImpl local, Comparator comp) {\r
- this(local,comp,false);\r
- }\r
-\r
- public Membership(MemberImpl local, Comparator comp, boolean includeLocal) {\r
- this(local,includeLocal);\r
- this.memberComparator = comp;\r
- }\r
- /**\r
- * Reset the membership and start over fresh.\r
- * Ie, delete all the members and wait for them to ping again and join this membership\r
- */\r
- public synchronized void reset() {\r
- map.clear();\r
- members = EMPTY_MEMBERS ;\r
- }\r
-\r
- /**\r
- * Notify the membership that this member has announced itself.\r
- *\r
- * @param member - the member that just pinged us\r
- * @return - true if this member is new to the cluster, false otherwise.\r
- * @return - false if this member is the local member or updated.\r
- */\r
- public synchronized boolean memberAlive(MemberImpl member) {\r
- boolean result = false;\r
- //ignore ourselves\r
- if ( member.equals(local) ) return result;\r
-\r
- //return true if the membership has changed\r
- MbrEntry entry = (MbrEntry)map.get(member);\r
- if ( entry == null ) {\r
- entry = addMember(member);\r
- result = true;\r
- } else {\r
- //update the member alive time\r
- MemberImpl updateMember = entry.getMember() ;\r
- if(updateMember.getMemberAliveTime() != member.getMemberAliveTime()) {\r
- //update fields that can change\r
- updateMember.setMemberAliveTime(member.getMemberAliveTime());\r
- updateMember.setPayload(member.getPayload());\r
- updateMember.setCommand(member.getCommand());\r
- Arrays.sort(members, memberComparator);\r
- }\r
- }\r
- entry.accessed();\r
- return result;\r
- }\r
-\r
- /**\r
- * Add a member to this component and sort array with memberComparator\r
- * @param member The member to add\r
- */\r
- public synchronized MbrEntry addMember(MemberImpl member) {\r
- synchronized (members) {\r
- MbrEntry entry = new MbrEntry(member);\r
- if (!map.containsKey(member) ) {\r
- map.put(member, entry);\r
- MemberImpl results[] = new MemberImpl[members.length + 1];\r
- for (int i = 0; i < members.length; i++) results[i] = members[i];\r
- results[members.length] = member;\r
- members = results;\r
- Arrays.sort(members, memberComparator);\r
- }\r
- return entry;\r
- }\r
- }\r
- \r
- /**\r
- * Remove a member from this component.\r
- * \r
- * @param member The member to remove\r
- */\r
- public void removeMember(MemberImpl member) {\r
- map.remove(member);\r
- synchronized (members) {\r
- int n = -1;\r
- for (int i = 0; i < members.length; i++) {\r
- if (members[i] == member || members[i].equals(member)) {\r
- n = i;\r
- break;\r
- }\r
- }\r
- if (n < 0) return;\r
- MemberImpl results[] = new MemberImpl[members.length - 1];\r
- int j = 0;\r
- for (int i = 0; i < members.length; i++) {\r
- if (i != n)\r
- results[j++] = members[i];\r
- }\r
- members = results;\r
- }\r
- }\r
- \r
- /**\r
- * Runs a refresh cycle and returns a list of members that has expired.\r
- * This also removes the members from the membership, in such a way that\r
- * getMembers() = getMembers() - expire()\r
- * @param maxtime - the max time a member can remain unannounced before it is considered dead.\r
- * @return the list of expired members\r
- */\r
- public synchronized MemberImpl[] expire(long maxtime) {\r
- if(!hasMembers() )\r
- return EMPTY_MEMBERS;\r
- \r
- ArrayList list = null;\r
- Iterator i = map.values().iterator();\r
- while(i.hasNext()) {\r
- MbrEntry entry = (MbrEntry)i.next();\r
- if( entry.hasExpired(maxtime) ) {\r
- if(list == null) // only need a list when members are expired (smaller gc)\r
- list = new java.util.ArrayList();\r
- list.add(entry.getMember());\r
- }\r
- }\r
- \r
- if(list != null) {\r
- MemberImpl[] result = new MemberImpl[list.size()];\r
- list.toArray(result);\r
- for( int j=0; j<result.length; j++) {\r
- removeMember(result[j]);\r
- }\r
- return result;\r
- } else {\r
- return EMPTY_MEMBERS ;\r
- }\r
- }\r
-\r
- /**\r
- * Returning that service has members or not\r
- */\r
- public boolean hasMembers() {\r
- return members.length > 0 ;\r
- }\r
- \r
- \r
- public MemberImpl getMember(Member mbr) {\r
- if(hasMembers()) {\r
- MemberImpl result = null;\r
- for ( int i=0; i<this.members.length && result==null; i++ ) {\r
- if ( members[i].equals(mbr) ) result = members[i];\r
- }//for\r
- return result;\r
- } else {\r
- return null;\r
- }\r
- }\r
- \r
- public boolean contains(Member mbr) { \r
- return getMember(mbr)!=null;\r
- }\r
- \r
- /**\r
- * Returning a list of all the members in the membership\r
- * We not need a copy: add and remove generate new arrays.\r
- */\r
- public MemberImpl[] getMembers() {\r
- if(hasMembers()) {\r
- return members;\r
- } else {\r
- return EMPTY_MEMBERS;\r
- }\r
- }\r
-\r
- /**\r
- * get a copy from all member entries\r
- */\r
- protected synchronized MbrEntry[] getMemberEntries()\r
- {\r
- MbrEntry[] result = new MbrEntry[map.size()];\r
- java.util.Iterator i = map.entrySet().iterator();\r
- int pos = 0;\r
- while ( i.hasNext() )\r
- result[pos++] = ((MbrEntry)((java.util.Map.Entry)i.next()).getValue());\r
- return result;\r
- }\r
- \r
- // --------------------------------------------- Inner Class\r
-\r
- private class MemberComparator implements java.util.Comparator {\r
-\r
- public int compare(Object o1, Object o2) {\r
- try {\r
- return compare((MemberImpl) o1, (MemberImpl) o2);\r
- } catch (ClassCastException x) {\r
- return 0;\r
- }\r
- }\r
-\r
- public int compare(MemberImpl m1, MemberImpl m2) {\r
- //longer alive time, means sort first\r
- long result = m2.getMemberAliveTime() - m1.getMemberAliveTime();\r
- if (result < 0)\r
- return -1;\r
- else if (result == 0)\r
- return 0;\r
- else\r
- return 1;\r
- }\r
- }\r
- \r
- /**\r
- * Inner class that represents a member entry\r
- */\r
- protected static class MbrEntry\r
- {\r
-\r
- protected MemberImpl mbr;\r
- protected long lastHeardFrom;\r
-\r
- public MbrEntry(MemberImpl mbr) {\r
- this.mbr = mbr;\r
- }\r
-\r
- /**\r
- * Indicate that this member has been accessed.\r
- */\r
- public void accessed(){\r
- lastHeardFrom = System.currentTimeMillis();\r
- }\r
-\r
- /**\r
- * Return the actual Member object\r
- */\r
- public MemberImpl getMember() {\r
- return mbr;\r
- }\r
-\r
- /**\r
- * Check if this dude has expired\r
- * @param maxtime The time threshold\r
- */\r
- public boolean hasExpired(long maxtime) {\r
- long delta = System.currentTimeMillis() - lastHeardFrom;\r
- return delta > maxtime;\r
- }\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.tribes.membership;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.catalina.tribes.Member;
+import java.util.Comparator;
+
+/**
+ * A <b>membership</b> implementation using simple multicast.
+ * This is the representation of a multicast membership.
+ * This class is responsible for maintaining a list of active cluster nodes in the cluster.
+ * If a node fails to send out a heartbeat, the node will be dismissed.
+ *
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$, $Date$
+ */
+public class Membership
+{
+ protected static final MemberImpl[] EMPTY_MEMBERS = new MemberImpl[0];
+
+ /**
+ * The name of this membership, has to be the same as the name for the local
+ * member
+ */
+ protected MemberImpl local;
+
+ /**
+ * A map of all the members in the cluster.
+ */
+ protected HashMap map = new HashMap();
+
+ /**
+ * A list of all the members in the cluster.
+ */
+ protected MemberImpl[] members = EMPTY_MEMBERS;
+
+ /**
+ * sort members by alive time
+ */
+ protected Comparator memberComparator = new MemberComparator();
+
+ public Object clone() {
+ synchronized (members) {
+ Membership clone = new Membership(local, memberComparator);
+ clone.map = (HashMap) map.clone();
+ clone.members = new MemberImpl[members.length];
+ System.arraycopy(members,0,clone.members,0,members.length);
+ return clone;
+ }
+ }
+
+ /**
+ * Constructs a new membership
+ * @param name - has to be the name of the local member. Used to filter the local member from the cluster membership
+ */
+ public Membership(MemberImpl local, boolean includeLocal) {
+ this.local = local;
+ if ( includeLocal ) addMember(local);
+ }
+
+ public Membership(MemberImpl local) {
+ this(local,false);
+ }
+
+ public Membership(MemberImpl local, Comparator comp) {
+ this(local,comp,false);
+ }
+
+ public Membership(MemberImpl local, Comparator comp, boolean includeLocal) {
+ this(local,includeLocal);
+ this.memberComparator = comp;
+ }
+ /**
+ * Reset the membership and start over fresh.
+ * Ie, delete all the members and wait for them to ping again and join this membership
+ */
+ public synchronized void reset() {
+ map.clear();
+ members = EMPTY_MEMBERS ;
+ }
+
+ /**
+ * Notify the membership that this member has announced itself.
+ *
+ * @param member - the member that just pinged us
+ * @return - true if this member is new to the cluster, false otherwise.
+ * @return - false if this member is the local member or updated.
+ */
+ public synchronized boolean memberAlive(MemberImpl member) {
+ boolean result = false;
+ //ignore ourselves
+ if ( member.equals(local) ) return result;
+
+ //return true if the membership has changed
+ MbrEntry entry = (MbrEntry)map.get(member);
+ if ( entry == null ) {
+ entry = addMember(member);
+ result = true;
+ } else {
+ //update the member alive time
+ MemberImpl updateMember = entry.getMember() ;
+ if(updateMember.getMemberAliveTime() != member.getMemberAliveTime()) {
+ //update fields that can change
+ updateMember.setMemberAliveTime(member.getMemberAliveTime());
+ updateMember.setPayload(member.getPayload());
+ updateMember.setCommand(member.getCommand());
+ Arrays.sort(members, memberComparator);
+ }
+ }
+ entry.accessed();
+ return result;
+ }
+
+ /**
+ * Add a member to this component and sort array with memberComparator
+ * @param member The member to add
+ */
+ public synchronized MbrEntry addMember(MemberImpl member) {
+ synchronized (members) {
+ MbrEntry entry = new MbrEntry(member);
+ if (!map.containsKey(member) ) {
+ map.put(member, entry);
+ MemberImpl results[] = new MemberImpl[members.length + 1];
+ for (int i = 0; i < members.length; i++) results[i] = members[i];
+ results[members.length] = member;
+ members = results;
+ Arrays.sort(members, memberComparator);
+ }
+ return entry;
+ }
+ }
+
+ /**
+ * Remove a member from this component.
+ *
+ * @param member The member to remove
+ */
+ public void removeMember(MemberImpl member) {
+ map.remove(member);
+ synchronized (members) {
+ int n = -1;
+ for (int i = 0; i < members.length; i++) {
+ if (members[i] == member || members[i].equals(member)) {
+ n = i;
+ break;
+ }
+ }
+ if (n < 0) return;
+ MemberImpl results[] = new MemberImpl[members.length - 1];
+ int j = 0;
+ for (int i = 0; i < members.length; i++) {
+ if (i != n)
+ results[j++] = members[i];
+ }
+ members = results;
+ }
+ }
+
+ /**
+ * Runs a refresh cycle and returns a list of members that has expired.
+ * This also removes the members from the membership, in such a way that
+ * getMembers() = getMembers() - expire()
+ * @param maxtime - the max time a member can remain unannounced before it is considered dead.
+ * @return the list of expired members
+ */
+ public synchronized MemberImpl[] expire(long maxtime) {
+ if(!hasMembers() )
+ return EMPTY_MEMBERS;
+
+ ArrayList list = null;
+ Iterator i = map.values().iterator();
+ while(i.hasNext()) {
+ MbrEntry entry = (MbrEntry)i.next();
+ if( entry.hasExpired(maxtime) ) {
+ if(list == null) // only need a list when members are expired (smaller gc)
+ list = new java.util.ArrayList();
+ list.add(entry.getMember());
+ }
+ }
+
+ if(list != null) {
+ MemberImpl[] result = new MemberImpl[list.size()];
+ list.toArray(result);
+ for( int j=0; j<result.length; j++) {
+ removeMember(result[j]);
+ }
+ return result;
+ } else {
+ return EMPTY_MEMBERS ;
+ }
+ }
+
+ /**
+ * Returning that service has members or not
+ */
+ public boolean hasMembers() {
+ return members.length > 0 ;
+ }
+
+
+ public MemberImpl getMember(Member mbr) {
+ if(hasMembers()) {
+ MemberImpl result = null;
+ for ( int i=0; i<this.members.length && result==null; i++ ) {
+ if ( members[i].equals(mbr) ) result = members[i];
+ }//for
+ return result;
+ } else {
+ return null;
+ }
+ }
+
+ public boolean contains(Member mbr) {
+ return getMember(mbr)!=null;
+ }
+
+ /**
+ * Returning a list of all the members in the membership
+ * We not need a copy: add and remove generate new arrays.
+ */
+ public MemberImpl[] getMembers() {
+ if(hasMembers()) {
+ return members;
+ } else {
+ return EMPTY_MEMBERS;
+ }
+ }
+
+ /**
+ * get a copy from all member entries
+ */
+ protected synchronized MbrEntry[] getMemberEntries()
+ {
+ MbrEntry[] result = new MbrEntry[map.size()];
+ java.util.Iterator i = map.entrySet().iterator();
+ int pos = 0;
+ while ( i.hasNext() )
+ result[pos++] = ((MbrEntry)((java.util.Map.Entry)i.next()).getValue());
+ return result;
+ }
+
+ // --------------------------------------------- Inner Class
+
+ private class MemberComparator implements java.util.Comparator {
+
+ public int compare(Object o1, Object o2) {
+ try {
+ return compare((MemberImpl) o1, (MemberImpl) o2);
+ } catch (ClassCastException x) {
+ return 0;
+ }
+ }
+
+ public int compare(MemberImpl m1, MemberImpl m2) {
+ //longer alive time, means sort first
+ long result = m2.getMemberAliveTime() - m1.getMemberAliveTime();
+ if (result < 0)
+ return -1;
+ else if (result == 0)
+ return 0;
+ else
+ return 1;
+ }
+ }
+
+ /**
+ * Inner class that represents a member entry
+ */
+ protected static class MbrEntry
+ {
+
+ protected MemberImpl mbr;
+ protected long lastHeardFrom;
+
+ public MbrEntry(MemberImpl mbr) {
+ this.mbr = mbr;
+ }
+
+ /**
+ * Indicate that this member has been accessed.
+ */
+ public void accessed(){
+ lastHeardFrom = System.currentTimeMillis();
+ }
+
+ /**
+ * Return the actual Member object
+ */
+ public MemberImpl getMember() {
+ return mbr;
+ }
+
+ /**
+ * Check if this dude has expired
+ * @param maxtime The time threshold
+ */
+ public boolean hasExpired(long maxtime) {
+ long delta = System.currentTimeMillis() - lastHeardFrom;
+ return delta > maxtime;
+ }
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.membership;\r
-\r
-import java.io.IOException;\r
-import org.apache.catalina.tribes.util.UUIDGenerator;\r
-import org.apache.catalina.tribes.util.Arrays;\r
-\r
-/**\r
- * <p>Title: </p>\r
- *\r
- * <p>Description: </p>\r
- *\r
- * <p>Copyright: Copyright (c) 2006</p>\r
- *\r
- * <p>Company: </p>\r
- *\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-public class StaticMember extends MemberImpl {\r
- public StaticMember() {\r
- super();\r
- }\r
-\r
- public StaticMember(String host, int port, long aliveTime) throws IOException {\r
- super(host, port, aliveTime);\r
- }\r
-\r
- public StaticMember(String host, int port, long aliveTime, byte[] payload) throws IOException {\r
- super(host, port, aliveTime, payload);\r
- }\r
- \r
- /**\r
- * @param host String, either in byte array string format, like {214,116,1,3}\r
- * or as a regular hostname, 127.0.0.1 or tomcat01.mydomain.com\r
- */\r
- public void setHost(String host) {\r
- if ( host == null ) return;\r
- if ( host.startsWith("{") ) setHost(Arrays.fromString(host));\r
- else try { setHostname(host); }catch (IOException x) { throw new RuntimeException(x);}\r
- \r
- }\r
- \r
- /**\r
- * @param domain String, either in byte array string format, like {214,116,1,3}\r
- * or as a regular string value like 'mydomain'. The latter will be converted using ISO-8859-1 encoding\r
- */\r
- public void setDomain(String domain) {\r
- if ( domain == null ) return;\r
- if ( domain.startsWith("{") ) setDomain(Arrays.fromString(domain));\r
- else setDomain(Arrays.convert(domain));\r
- }\r
- \r
- /**\r
- * @param id String, must be in byte array string format, like {214,116,1,3} and exactly 16 bytes long\r
- */\r
- public void setUniqueId(String id) {\r
- byte[] uuid = Arrays.fromString(id);\r
- if ( uuid==null || uuid.length != 16 ) throw new RuntimeException("UUID must be exactly 16 bytes, not:"+id);\r
- setUniqueId(uuid);\r
- }\r
- \r
- \r
+/*
+ * 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.apache.catalina.tribes.membership;
+
+import java.io.IOException;
+import org.apache.catalina.tribes.util.UUIDGenerator;
+import org.apache.catalina.tribes.util.Arrays;
+
+/**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2006</p>
+ *
+ * <p>Company: </p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public class StaticMember extends MemberImpl {
+ public StaticMember() {
+ super();
+ }
+
+ public StaticMember(String host, int port, long aliveTime) throws IOException {
+ super(host, port, aliveTime);
+ }
+
+ public StaticMember(String host, int port, long aliveTime, byte[] payload) throws IOException {
+ super(host, port, aliveTime, payload);
+ }
+
+ /**
+ * @param host String, either in byte array string format, like {214,116,1,3}
+ * or as a regular hostname, 127.0.0.1 or tomcat01.mydomain.com
+ */
+ public void setHost(String host) {
+ if ( host == null ) return;
+ if ( host.startsWith("{") ) setHost(Arrays.fromString(host));
+ else try { setHostname(host); }catch (IOException x) { throw new RuntimeException(x);}
+
+ }
+
+ /**
+ * @param domain String, either in byte array string format, like {214,116,1,3}
+ * or as a regular string value like 'mydomain'. The latter will be converted using ISO-8859-1 encoding
+ */
+ public void setDomain(String domain) {
+ if ( domain == null ) return;
+ if ( domain.startsWith("{") ) setDomain(Arrays.fromString(domain));
+ else setDomain(Arrays.convert(domain));
+ }
+
+ /**
+ * @param id String, must be in byte array string format, like {214,116,1,3} and exactly 16 bytes long
+ */
+ public void setUniqueId(String id) {
+ byte[] uuid = Arrays.fromString(id);
+ if ( uuid==null || uuid.length != 16 ) throw new RuntimeException("UUID must be exactly 16 bytes, not:"+id);
+ setUniqueId(uuid);
+ }
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.tipis;\r
-\r
-import java.io.IOException;\r
-import java.io.ObjectInput;\r
-import java.io.ObjectOutput;\r
-import java.io.Serializable;\r
-import java.io.UnsupportedEncodingException;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.LinkedHashMap;\r
-import java.util.LinkedHashSet;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelListener;\r
-import org.apache.catalina.tribes.Heartbeat;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.MembershipListener;\r
-import org.apache.catalina.tribes.group.Response;\r
-import org.apache.catalina.tribes.group.RpcCallback;\r
-import org.apache.catalina.tribes.group.RpcChannel;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-import org.apache.catalina.tribes.membership.MemberImpl;\r
-import org.apache.catalina.tribes.util.Arrays;\r
-import org.apache.juli.logging.Log;\r
-import org.apache.juli.logging.LogFactory;\r
-import java.util.ConcurrentModificationException;\r
-\r
-/**\r
- *\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public abstract class AbstractReplicatedMap extends LinkedHashMap implements RpcCallback, ChannelListener, MembershipListener, Heartbeat {\r
- protected static Log log = LogFactory.getLog(AbstractReplicatedMap.class);\r
-\r
- /**\r
- * The default initial capacity - MUST be a power of two.\r
- */\r
- public static final int DEFAULT_INITIAL_CAPACITY = 16;\r
-\r
- /**\r
- * The load factor used when none specified in constructor.\r
- **/\r
- public static final float DEFAULT_LOAD_FACTOR = 0.75f;\r
- \r
- /**\r
- * Used to identify the map\r
- */\r
- final String chset = "ISO-8859-1";\r
-\r
-//------------------------------------------------------------------------------\r
-// INSTANCE VARIABLES\r
-//------------------------------------------------------------------------------\r
- \r
- /**\r
- * Timeout for RPC messages, how long we will wait for a reply\r
- */\r
- protected transient long rpcTimeout = 5000;\r
- /**\r
- * Reference to the channel for sending messages\r
- */\r
- protected transient Channel channel;\r
- /**\r
- * The RpcChannel to send RPC messages through\r
- */\r
- protected transient RpcChannel rpcChannel;\r
- /**\r
- * The Map context name makes this map unique, this\r
- * allows us to have more than one map shared\r
- * through one channel\r
- */\r
- protected transient byte[] mapContextName;\r
- /**\r
- * Has the state been transferred\r
- */\r
- protected transient boolean stateTransferred = false;\r
- /**\r
- * Simple lock object for transfers\r
- */\r
- protected transient Object stateMutex = new Object();\r
- /**\r
- * A list of members in our map\r
- */\r
- protected transient HashMap mapMembers = new HashMap();\r
- /**\r
- * Our default send options\r
- */\r
- protected transient int channelSendOptions = Channel.SEND_OPTIONS_DEFAULT;\r
- /**\r
- * The owner of this map, ala a SessionManager for example\r
- */\r
- protected transient Object mapOwner;\r
- /**\r
- * External class loaders if serialization and deserialization is to be performed successfully.\r
- */\r
- protected transient ClassLoader[] externalLoaders;\r
- \r
- /**\r
- * The node we are currently backing up data to, this index will rotate\r
- * on a round robin basis\r
- */\r
- protected transient int currentNode = 0;\r
- \r
- /**\r
- * Since the map keeps internal membership\r
- * this is the timeout for a ping message to be responded to\r
- * If a remote map doesn't respond within this timeframe, \r
- * its considered dead.\r
- */\r
- protected transient long accessTimeout = 5000;\r
- \r
- /**\r
- * Readable string of the mapContextName value\r
- */\r
- protected transient String mapname = "";\r
- \r
-\r
-//------------------------------------------------------------------------------\r
-// CONSTRUCTORS\r
-//------------------------------------------------------------------------------\r
-\r
- /**\r
- * Creates a new map\r
- * @param channel The channel to use for communication\r
- * @param timeout long - timeout for RPC messags\r
- * @param mapContextName String - unique name for this map, to allow multiple maps per channel\r
- * @param initialCapacity int - the size of this map, see HashMap\r
- * @param loadFactor float - load factor, see HashMap\r
- * @param cls - a list of classloaders to be used for deserialization of objects.\r
- */\r
- public AbstractReplicatedMap(Object owner,\r
- Channel channel, \r
- long timeout, \r
- String mapContextName, \r
- int initialCapacity,\r
- float loadFactor,\r
- int channelSendOptions,\r
- ClassLoader[] cls) {\r
- super(initialCapacity, loadFactor);\r
- init(owner, channel, mapContextName, timeout, channelSendOptions, cls);\r
- \r
- }\r
-\r
- /**\r
- * Helper methods, wraps a single member in an array\r
- * @param m Member\r
- * @return Member[]\r
- */\r
- protected Member[] wrap(Member m) {\r
- if ( m == null ) return new Member[0];\r
- else return new Member[] {m};\r
- }\r
-\r
- /**\r
- * Initializes the map by creating the RPC channel, registering itself as a channel listener\r
- * This method is also responsible for initiating the state transfer\r
- * @param owner Object\r
- * @param channel Channel\r
- * @param mapContextName String\r
- * @param timeout long\r
- * @param channelSendOptions int\r
- * @param cls ClassLoader[]\r
- */\r
- protected void init(Object owner, Channel channel, String mapContextName, long timeout, int channelSendOptions,ClassLoader[] cls) {\r
- log.info("Initializing AbstractReplicatedMap with context name:"+mapContextName);\r
- this.mapOwner = owner;\r
- this.externalLoaders = cls;\r
- this.channelSendOptions = channelSendOptions;\r
- this.channel = channel;\r
- this.rpcTimeout = timeout;\r
-\r
- try {\r
- this.mapname = mapContextName;\r
- //unique context is more efficient if it is stored as bytes\r
- this.mapContextName = mapContextName.getBytes(chset);\r
- } catch (UnsupportedEncodingException x) {\r
- log.warn("Unable to encode mapContextName[" + mapContextName + "] using getBytes(" + chset +") using default getBytes()", x);\r
- this.mapContextName = mapContextName.getBytes();\r
- }\r
- if ( log.isTraceEnabled() ) log.trace("Created Lazy Map with name:"+mapContextName+", bytes:"+Arrays.toString(this.mapContextName));\r
-\r
- //create an rpc channel and add the map as a listener\r
- this.rpcChannel = new RpcChannel(this.mapContextName, channel, this);\r
- //add this map as a message listener\r
- this.channel.addChannelListener(this);\r
- //listen for membership notifications\r
- this.channel.addMembershipListener(this);\r
- \r
- \r
- try {\r
- //broadcast our map, this just notifies other members of our existence\r
- broadcast(MapMessage.MSG_INIT, true);\r
- //transfer state from another map\r
- transferState();\r
- //state is transferred, we are ready for messaging\r
- broadcast(MapMessage.MSG_START, true);\r
- } catch (ChannelException x) {\r
- log.warn("Unable to send map start message.");\r
- throw new RuntimeException("Unable to start replicated map.",x);\r
- }\r
- }\r
- \r
- \r
- /**\r
- * Sends a ping out to all the members in the cluster, not just map members\r
- * that this map is alive.\r
- * @param timeout long\r
- * @throws ChannelException\r
- */\r
- protected void ping(long timeout) throws ChannelException {\r
- //send out a map membership message, only wait for the first reply\r
- MapMessage msg = new MapMessage(this.mapContextName, \r
- MapMessage.MSG_INIT,\r
- false, \r
- null, \r
- null, \r
- null, \r
- wrap(channel.getLocalMember(false)));\r
- if ( channel.getMembers().length > 0 ) {\r
- //send a ping, wait for all nodes to reply\r
- Response[] resp = rpcChannel.send(channel.getMembers(), \r
- msg, rpcChannel.ALL_REPLY, \r
- (channelSendOptions),\r
- (int) accessTimeout);\r
- for (int i = 0; i < resp.length; i++) {\r
- memberAlive(resp[i].getSource());\r
- } //for\r
- }\r
- //update our map of members, expire some if we didn't receive a ping back\r
- synchronized (mapMembers) {\r
- Iterator it = mapMembers.entrySet().iterator();\r
- long now = System.currentTimeMillis();\r
- while ( it.hasNext() ) {\r
- Map.Entry entry = (Map.Entry)it.next();\r
- long access = ((Long)entry.getValue()).longValue(); \r
- if ( (now - access) > timeout ) memberDisappeared((Member)entry.getKey());\r
- }\r
- }//synch\r
- }\r
-\r
- /**\r
- * We have received a member alive notification\r
- * @param member Member\r
- */\r
- protected void memberAlive(Member member) {\r
- synchronized (mapMembers) {\r
- if (!mapMembers.containsKey(member)) {\r
- mapMemberAdded(member);\r
- } //end if\r
- mapMembers.put(member, new Long(System.currentTimeMillis()));\r
- }\r
- }\r
- \r
- /**\r
- * Helper method to broadcast a message to all members in a channel\r
- * @param msgtype int\r
- * @param rpc boolean\r
- * @throws ChannelException\r
- */\r
- protected void broadcast(int msgtype, boolean rpc) throws ChannelException {\r
- //send out a map membership message, only wait for the first reply\r
- MapMessage msg = new MapMessage(this.mapContextName, msgtype,\r
- false, null, null, null, wrap(channel.getLocalMember(false)));\r
- if ( rpc) {\r
- Response[] resp = rpcChannel.send(channel.getMembers(), msg, rpcChannel.FIRST_REPLY, (channelSendOptions),rpcTimeout);\r
- for (int i = 0; i < resp.length; i++) {\r
- mapMemberAdded(resp[i].getSource());\r
- messageReceived(resp[i].getMessage(), resp[i].getSource());\r
- }\r
- } else {\r
- channel.send(channel.getMembers(),msg,channelSendOptions);\r
- }\r
- }\r
-\r
- public void breakdown() {\r
- finalize();\r
- }\r
-\r
- public void finalize() {\r
- try {broadcast(MapMessage.MSG_STOP,false); }catch ( Exception ignore){}\r
- //cleanup\r
- if (this.rpcChannel != null) {\r
- this.rpcChannel.breakdown();\r
- }\r
- if (this.channel != null) {\r
- this.channel.removeChannelListener(this);\r
- this.channel.removeMembershipListener(this);\r
- }\r
- this.rpcChannel = null;\r
- this.channel = null;\r
- this.mapMembers.clear();\r
- super.clear();\r
- this.stateTransferred = false;\r
- this.externalLoaders = null;\r
- }\r
- \r
- public int hashCode() {\r
- return Arrays.hashCode(this.mapContextName);\r
- }\r
- \r
- public boolean equals(Object o) {\r
- if ( o == null ) return false;\r
- if ( !(o instanceof AbstractReplicatedMap)) return false;\r
- if ( !(o.getClass().equals(this.getClass())) ) return false;\r
- AbstractReplicatedMap other = (AbstractReplicatedMap)o;\r
- return Arrays.equals(mapContextName,other.mapContextName);\r
- }\r
-\r
-//------------------------------------------------------------------------------\r
-// GROUP COM INTERFACES\r
-//------------------------------------------------------------------------------\r
- public Member[] getMapMembers(HashMap members) {\r
- synchronized (members) {\r
- Member[] result = new Member[members.size()];\r
- members.keySet().toArray(result);\r
- return result;\r
- }\r
- }\r
- public Member[] getMapMembers() {\r
- return getMapMembers(this.mapMembers);\r
- }\r
- \r
- public Member[] getMapMembersExcl(Member[] exclude) {\r
- synchronized (mapMembers) {\r
- HashMap list = (HashMap)mapMembers.clone();\r
- for (int i=0; i<exclude.length;i++) list.remove(exclude[i]);\r
- return getMapMembers(list);\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Replicates any changes to the object since the last time\r
- * The object has to be primary, ie, if the object is a proxy or a backup, it will not be replicated<br>\r
- * @param complete - if set to true, the object is replicated to its backup\r
- * if set to false, only objects that implement ReplicatedMapEntry and the isDirty() returns true will\r
- * be replicated\r
- */\r
- public void replicate(Object key, boolean complete) {\r
- if ( log.isTraceEnabled() )\r
- log.trace("Replicate invoked on key:"+key);\r
- MapEntry entry = (MapEntry)super.get(key);\r
- if ( entry == null ) return;\r
- if ( !entry.isSerializable() ) return;\r
- if (entry != null && entry.isPrimary() && entry.getBackupNodes()!= null && entry.getBackupNodes().length > 0) {\r
- Object value = entry.getValue();\r
- //check to see if we need to replicate this object isDirty()||complete\r
- boolean repl = complete || ( (value instanceof ReplicatedMapEntry) && ( (ReplicatedMapEntry) value).isDirty());\r
- \r
- if (!repl) {\r
- if ( log.isTraceEnabled() )\r
- log.trace("Not replicating:"+key+", no change made");\r
- \r
- return;\r
- }\r
- //check to see if the message is diffable\r
- boolean diff = ( (value instanceof ReplicatedMapEntry) && ( (ReplicatedMapEntry) value).isDiffable());\r
- MapMessage msg = null;\r
- if (diff) {\r
- ReplicatedMapEntry rentry = (ReplicatedMapEntry)entry.getValue();\r
- try {\r
- rentry.lock();\r
- //construct a diff message\r
- msg = new MapMessage(mapContextName, MapMessage.MSG_BACKUP,\r
- true, (Serializable) entry.getKey(), null,\r
- rentry.getDiff(),\r
- entry.getBackupNodes());\r
- } catch (IOException x) {\r
- log.error("Unable to diff object. Will replicate the entire object instead.", x);\r
- } finally {\r
- rentry.unlock();\r
- }\r
- \r
- }\r
- if (msg == null) {\r
- //construct a complete\r
- msg = new MapMessage(mapContextName, MapMessage.MSG_BACKUP,\r
- false, (Serializable) entry.getKey(),\r
- (Serializable) entry.getValue(),\r
- null, entry.getBackupNodes());\r
-\r
- }\r
- try {\r
- if ( channel!=null && entry.getBackupNodes()!= null && entry.getBackupNodes().length > 0 ) {\r
- channel.send(entry.getBackupNodes(), msg, channelSendOptions);\r
- }\r
- } catch (ChannelException x) {\r
- log.error("Unable to replicate data.", x);\r
- }\r
- } //end if\r
-\r
- }\r
-\r
- /**\r
- * This can be invoked by a periodic thread to replicate out any changes.\r
- * For maps that don't store objects that implement ReplicatedMapEntry, this\r
- * method should be used infrequently to avoid large amounts of data transfer\r
- * @param complete boolean\r
- */\r
- public void replicate(boolean complete) {\r
- Iterator i = super.entrySet().iterator();\r
- while (i.hasNext()) {\r
- Map.Entry e = (Map.Entry) i.next();\r
- replicate(e.getKey(), complete);\r
- } //while\r
-\r
- }\r
-\r
- public void transferState() {\r
- try {\r
- Member[] members = getMapMembers();\r
- Member backup = members.length > 0 ? (Member) members[0] : null;\r
- if (backup != null) {\r
- MapMessage msg = new MapMessage(mapContextName, MapMessage.MSG_STATE, false,\r
- null, null, null, null);\r
- Response[] resp = rpcChannel.send(new Member[] {backup}, msg, rpcChannel.FIRST_REPLY, channelSendOptions, rpcTimeout);\r
- if (resp.length > 0) {\r
- synchronized (stateMutex) {\r
- msg = (MapMessage) resp[0].getMessage();\r
- msg.deserialize(getExternalLoaders());\r
- ArrayList list = (ArrayList) msg.getValue();\r
- for (int i = 0; i < list.size(); i++) {\r
- messageReceived( (Serializable) list.get(i), resp[0].getSource());\r
- } //for\r
- }\r
- } else {\r
- log.warn("Transfer state, 0 replies, probably a timeout.");\r
- }\r
- }\r
- } catch (ChannelException x) {\r
- log.error("Unable to transfer LazyReplicatedMap state.", x);\r
- } catch (IOException x) {\r
- log.error("Unable to transfer LazyReplicatedMap state.", x);\r
- } catch (ClassNotFoundException x) {\r
- log.error("Unable to transfer LazyReplicatedMap state.", x);\r
- }\r
- stateTransferred = true;\r
- }\r
-\r
- /**\r
- * @todo implement state transfer\r
- * @param msg Serializable\r
- * @return Serializable - null if no reply should be sent\r
- */\r
- public Serializable replyRequest(Serializable msg, final Member sender) {\r
- if (! (msg instanceof MapMessage))return null;\r
- MapMessage mapmsg = (MapMessage) msg;\r
-\r
- //map init request\r
- if (mapmsg.getMsgType() == mapmsg.MSG_INIT) {\r
- mapmsg.setBackUpNodes(wrap(channel.getLocalMember(false)));\r
- return mapmsg;\r
- }\r
- \r
- //map start request\r
- if (mapmsg.getMsgType() == mapmsg.MSG_START) {\r
- mapmsg.setBackUpNodes(wrap(channel.getLocalMember(false)));\r
- mapMemberAdded(sender);\r
- return mapmsg;\r
- }\r
-\r
- //backup request\r
- if (mapmsg.getMsgType() == mapmsg.MSG_RETRIEVE_BACKUP) {\r
- MapEntry entry = (MapEntry)super.get(mapmsg.getKey());\r
- if (entry == null || (!entry.isSerializable()) )return null;\r
- mapmsg.setValue( (Serializable) entry.getValue());\r
- return mapmsg;\r
- }\r
-\r
- //state transfer request\r
- if (mapmsg.getMsgType() == mapmsg.MSG_STATE) {\r
- synchronized (stateMutex) { //make sure we dont do two things at the same time\r
- ArrayList list = new ArrayList();\r
- Iterator i = super.entrySet().iterator();\r
- while (i.hasNext()) {\r
- Map.Entry e = (Map.Entry) i.next();\r
- MapEntry entry = (MapEntry) e.getValue();\r
- if ( entry.isSerializable() ) {\r
- MapMessage me = new MapMessage(mapContextName, MapMessage.MSG_PROXY,\r
- false, (Serializable) entry.getKey(), null, null, entry.getBackupNodes());\r
- list.add(me);\r
- }\r
- }\r
- mapmsg.setValue(list);\r
- return mapmsg;\r
- \r
- } //synchronized\r
- }\r
-\r
- return null;\r
-\r
- }\r
-\r
- /**\r
- * If the reply has already been sent to the requesting thread,\r
- * the rpc callback can handle any data that comes in after the fact.\r
- * @param msg Serializable\r
- * @param sender Member\r
- */\r
- public void leftOver(Serializable msg, Member sender) {\r
- //left over membership messages\r
- if (! (msg instanceof MapMessage))return;\r
-\r
- MapMessage mapmsg = (MapMessage) msg;\r
- try {\r
- mapmsg.deserialize(getExternalLoaders());\r
- if (mapmsg.getMsgType() == MapMessage.MSG_START) {\r
- mapMemberAdded(mapmsg.getBackupNodes()[0]);\r
- } else if (mapmsg.getMsgType() == MapMessage.MSG_INIT) {\r
- memberAlive(mapmsg.getBackupNodes()[0]);\r
- }\r
- } catch (IOException x ) {\r
- log.error("Unable to deserialize MapMessage.",x);\r
- } catch (ClassNotFoundException x ) {\r
- log.error("Unable to deserialize MapMessage.",x);\r
- }\r
- }\r
-\r
- public void messageReceived(Serializable msg, Member sender) {\r
- if (! (msg instanceof MapMessage)) return;\r
-\r
- MapMessage mapmsg = (MapMessage) msg;\r
- if ( log.isTraceEnabled() ) {\r
- log.trace("Map["+mapname+"] received message:"+mapmsg);\r
- }\r
- \r
- try {\r
- mapmsg.deserialize(getExternalLoaders());\r
- } catch (IOException x) {\r
- log.error("Unable to deserialize MapMessage.", x);\r
- return;\r
- } catch (ClassNotFoundException x) {\r
- log.error("Unable to deserialize MapMessage.", x);\r
- return;\r
- }\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Map message received from:"+sender.getName()+" msg:"+mapmsg);\r
- if (mapmsg.getMsgType() == MapMessage.MSG_START) {\r
- mapMemberAdded(mapmsg.getBackupNodes()[0]);\r
- }\r
-\r
- if (mapmsg.getMsgType() == MapMessage.MSG_STOP) {\r
- memberDisappeared(mapmsg.getBackupNodes()[0]);\r
- }\r
-\r
- if (mapmsg.getMsgType() == MapMessage.MSG_PROXY) {\r
- MapEntry entry = (MapEntry)super.get(mapmsg.getKey());\r
- if ( entry==null ) {\r
- entry = new MapEntry(mapmsg.getKey(), mapmsg.getValue());\r
- entry.setBackup(false);\r
- entry.setProxy(true);\r
- entry.setBackupNodes(mapmsg.getBackupNodes());\r
- super.put(entry.getKey(), entry);\r
- } else {\r
- entry.setProxy(true);\r
- entry.setBackup(false);\r
- entry.setBackupNodes(mapmsg.getBackupNodes());\r
- }\r
- }\r
-\r
- if (mapmsg.getMsgType() == MapMessage.MSG_REMOVE) {\r
- super.remove(mapmsg.getKey());\r
- }\r
-\r
- if (mapmsg.getMsgType() == MapMessage.MSG_BACKUP) {\r
- MapEntry entry = (MapEntry)super.get(mapmsg.getKey());\r
- if (entry == null) {\r
- entry = new MapEntry(mapmsg.getKey(), mapmsg.getValue());\r
- entry.setBackup(true);\r
- entry.setProxy(false);\r
- entry.setBackupNodes(mapmsg.getBackupNodes());\r
- if (mapmsg.getValue()!=null && mapmsg.getValue() instanceof ReplicatedMapEntry ) {\r
- ((ReplicatedMapEntry)mapmsg.getValue()).setOwner(getMapOwner());\r
- }\r
- } else {\r
- entry.setBackup(true);\r
- entry.setProxy(false);\r
- entry.setBackupNodes(mapmsg.getBackupNodes());\r
- if (entry.getValue() instanceof ReplicatedMapEntry) {\r
- ReplicatedMapEntry diff = (ReplicatedMapEntry) entry.getValue();\r
- if (mapmsg.isDiff()) {\r
- try {\r
- diff.lock();\r
- diff.applyDiff(mapmsg.getDiffValue(), 0, mapmsg.getDiffValue().length);\r
- } catch (Exception x) {\r
- log.error("Unable to apply diff to key:" + entry.getKey(), x);\r
- } finally {\r
- diff.unlock();\r
- }\r
- } else {\r
- if ( mapmsg.getValue()!=null ) entry.setValue(mapmsg.getValue());\r
- ((ReplicatedMapEntry)entry.getValue()).setOwner(getMapOwner());\r
- } //end if\r
- } else if (mapmsg.getValue() instanceof ReplicatedMapEntry) {\r
- ReplicatedMapEntry re = (ReplicatedMapEntry)mapmsg.getValue();\r
- re.setOwner(getMapOwner());\r
- entry.setValue(re);\r
- } else {\r
- if ( mapmsg.getValue()!=null ) entry.setValue(mapmsg.getValue());\r
- } //end if\r
- } //end if\r
- super.put(entry.getKey(), entry);\r
- } //end if\r
- }\r
-\r
- public boolean accept(Serializable msg, Member sender) {\r
- boolean result = false;\r
- if (msg instanceof MapMessage) {\r
- if ( log.isTraceEnabled() ) log.trace("Map["+mapname+"] accepting...."+msg);\r
- result = Arrays.equals(mapContextName, ( (MapMessage) msg).getMapId());\r
- if ( log.isTraceEnabled() ) log.trace("Msg["+mapname+"] accepted["+result+"]...."+msg);\r
- }\r
- return result;\r
- }\r
-\r
- public void mapMemberAdded(Member member) {\r
- if ( member.equals(getChannel().getLocalMember(false)) ) return;\r
- boolean memberAdded = false;\r
- //select a backup node if we don't have one\r
- synchronized (mapMembers) {\r
- if (!mapMembers.containsKey(member) ) {\r
- mapMembers.put(member, new Long(System.currentTimeMillis()));\r
- memberAdded = true;\r
- }\r
- }\r
- if ( memberAdded ) {\r
- synchronized (stateMutex) {\r
- Iterator i = super.entrySet().iterator();\r
- while (i.hasNext()) {\r
- Map.Entry e = (Map.Entry) i.next();\r
- MapEntry entry = (MapEntry) e.getValue();\r
- if ( entry == null ) continue;\r
- if (entry.isPrimary() && (entry.getBackupNodes() == null || entry.getBackupNodes().length == 0)) {\r
- try {\r
- Member[] backup = publishEntryInfo(entry.getKey(), entry.getValue());\r
- entry.setBackupNodes(backup);\r
- } catch (ChannelException x) {\r
- log.error("Unable to select backup node.", x);\r
- } //catch\r
- } //end if\r
- } //while\r
- } //synchronized\r
- }//end if\r
- }\r
- \r
- public boolean inSet(Member m, Member[] set) {\r
- if ( set == null ) return false;\r
- boolean result = false;\r
- for (int i=0; i<set.length && (!result); i++ )\r
- if ( m.equals(set[i]) ) result = true;\r
- return result;\r
- }\r
-\r
- public Member[] excludeFromSet(Member[] mbrs, Member[] set) {\r
- ArrayList result = new ArrayList();\r
- for (int i=0; i<set.length; i++ ) {\r
- boolean include = true;\r
- for (int j=0; j<mbrs.length; j++ ) \r
- if ( mbrs[j].equals(set[i]) ) include = false;\r
- if ( include ) result.add(set[i]);\r
- }\r
- return (Member[])result.toArray(new Member[result.size()]);\r
- }\r
-\r
- public void memberAdded(Member member) {\r
- //do nothing\r
- }\r
-\r
- public void memberDisappeared(Member member) {\r
- boolean removed = false;\r
- synchronized (mapMembers) {\r
- removed = (mapMembers.remove(member) != null );\r
- }\r
- Iterator i = super.entrySet().iterator();\r
- while (i.hasNext()) {\r
- Map.Entry e = (Map.Entry) i.next();\r
- MapEntry entry = (MapEntry) e.getValue();\r
- if (entry.isPrimary() && inSet(member,entry.getBackupNodes())) {\r
- try {\r
- Member[] backup = publishEntryInfo(entry.getKey(), entry.getValue());\r
- entry.setBackupNodes(backup);\r
- } catch (ChannelException x) {\r
- log.error("Unable to relocate[" + entry.getKey() + "] to a new backup node", x);\r
- }\r
- } //end if\r
- } //while\r
- }\r
-\r
- public int getNextBackupIndex() {\r
- int size = mapMembers.size();\r
- if (mapMembers.size() == 0)return -1;\r
- int node = currentNode++;\r
- if (node >= size) {\r
- node = 0;\r
- currentNode = 0;\r
- }\r
- return node;\r
- }\r
- public Member getNextBackupNode() {\r
- Member[] members = getMapMembers();\r
- int node = getNextBackupIndex();\r
- if ( members.length == 0 || node==-1) return null;\r
- if ( node >= members.length ) node = 0;\r
- return members[node];\r
- }\r
-\r
- protected abstract Member[] publishEntryInfo(Object key, Object value) throws ChannelException;\r
- \r
- public void heartbeat() {\r
- try {\r
- ping(accessTimeout);\r
- }catch ( Exception x ) {\r
- log.error("Unable to send AbstractReplicatedMap.ping message",x);\r
- }\r
- }\r
-\r
-//------------------------------------------------------------------------------ \r
-// METHODS TO OVERRIDE \r
-//------------------------------------------------------------------------------\r
- \r
- /**\r
- * Removes an object from this map, it will also remove it from \r
- * \r
- * @param key Object\r
- * @return Object\r
- */\r
- public Object remove(Object key) {\r
- MapEntry entry = (MapEntry)super.remove(key);\r
-\r
- try {\r
- if (getMapMembers().length > 0 ) {\r
- MapMessage msg = new MapMessage(getMapContextName(), MapMessage.MSG_REMOVE, false, (Serializable) key, null, null, null);\r
- getChannel().send(getMapMembers(), msg, getChannelSendOptions());\r
- }\r
- } catch ( ChannelException x ) {\r
- log.error("Unable to replicate out data for a LazyReplicatedMap.remove operation",x);\r
- }\r
- return entry!=null?entry.getValue():null;\r
- }\r
- \r
- public Object get(Object key) {\r
- MapEntry entry = (MapEntry)super.get(key);\r
- if (log.isTraceEnabled()) log.trace("Requesting id:"+key+" entry:"+entry);\r
- if ( entry == null ) return null;\r
- if ( !entry.isPrimary() ) {\r
- //if the message is not primary, we need to retrieve the latest value\r
- try {\r
- Member[] backup = null;\r
- MapMessage msg = null;\r
- if ( !entry.isBackup() ) {\r
- //make sure we don't retrieve from ourselves\r
- msg = new MapMessage(getMapContextName(), MapMessage.MSG_RETRIEVE_BACKUP, false,\r
- (Serializable) key, null, null, null);\r
- Response[] resp = getRpcChannel().send(entry.getBackupNodes(),msg, this.getRpcChannel().FIRST_REPLY, Channel.SEND_OPTIONS_DEFAULT, getRpcTimeout());\r
- if (resp == null || resp.length == 0) {\r
- //no responses\r
- log.warn("Unable to retrieve remote object for key:" + key);\r
- return null;\r
- }\r
- msg = (MapMessage) resp[0].getMessage();\r
- msg.deserialize(getExternalLoaders());\r
- backup = entry.getBackupNodes();\r
- if ( entry.getValue() instanceof ReplicatedMapEntry ) {\r
- ReplicatedMapEntry val = (ReplicatedMapEntry)entry.getValue();\r
- val.setOwner(getMapOwner());\r
- }\r
- if ( msg.getValue()!=null ) entry.setValue(msg.getValue());\r
- }\r
- if (entry.isBackup()) {\r
- //select a new backup node\r
- backup = publishEntryInfo(key, entry.getValue());\r
- } else if ( entry.isProxy() ) {\r
- //invalidate the previous primary\r
- msg = new MapMessage(getMapContextName(),MapMessage.MSG_PROXY,false,(Serializable)key,null,null,backup);\r
- Member[] dest = getMapMembersExcl(backup);\r
- if ( dest!=null && dest.length >0) {\r
- getChannel().send(dest, msg, getChannelSendOptions());\r
- }\r
- }\r
-\r
- entry.setBackupNodes(backup);\r
- entry.setBackup(false);\r
- entry.setProxy(false);\r
-\r
-\r
- } catch (Exception x) {\r
- log.error("Unable to replicate out data for a LazyReplicatedMap.get operation", x);\r
- return null;\r
- }\r
- }\r
- if (log.isTraceEnabled()) log.trace("Requesting id:"+key+" result:"+entry.getValue());\r
- if ( entry.getValue() != null && entry.getValue() instanceof ReplicatedMapEntry ) {\r
- ReplicatedMapEntry val = (ReplicatedMapEntry)entry.getValue();\r
- //hack, somehow this is not being set above\r
- val.setOwner(getMapOwner());\r
- \r
- }\r
- return entry.getValue();\r
- } \r
-\r
- \r
- protected void printMap(String header) {\r
- try {\r
- System.out.println("\nDEBUG MAP:"+header);\r
- System.out.println("Map["+ new String(mapContextName, chset) + ", Map Size:" + super.size());\r
- Member[] mbrs = getMapMembers();\r
- for ( int i=0; i<mbrs.length;i++ ) {\r
- System.out.println("Mbr["+(i+1)+"="+mbrs[i].getName());\r
- }\r
- Iterator i = super.entrySet().iterator();\r
- int cnt = 0;\r
-\r
- while (i.hasNext()) {\r
- Map.Entry e = (Map.Entry) i.next();\r
- System.out.println( (++cnt) + ". " + e.getValue());\r
- }\r
- System.out.println("EndMap]\n\n");\r
- }catch ( Exception ignore) {\r
- ignore.printStackTrace();\r
- }\r
- }\r
- \r
- /**\r
- * Returns true if the key has an entry in the map.\r
- * The entry can be a proxy or a backup entry, invoking <code>get(key)</code>\r
- * will make this entry primary for the group\r
- * @param key Object\r
- * @return boolean\r
- */\r
- public boolean containsKey(Object key) {\r
- return super.containsKey(key);\r
- }\r
- \r
- \r
- public Object put(Object key, Object value) {\r
- MapEntry entry = new MapEntry(key,value);\r
- entry.setBackup(false);\r
- entry.setProxy(false);\r
- \r
- Object old = null;\r
- \r
- //make sure that any old values get removed\r
- if ( containsKey(key) ) old = remove(key);\r
- try {\r
- Member[] backup = publishEntryInfo(key, value);\r
- entry.setBackupNodes(backup);\r
- } catch (ChannelException x) {\r
- log.error("Unable to replicate out data for a LazyReplicatedMap.put operation", x);\r
- }\r
- super.put(key,entry);\r
- return old;\r
- }\r
- \r
- \r
- /**\r
- * Copies all values from one map to this instance\r
- * @param m Map\r
- */\r
- public void putAll(Map m) {\r
- Iterator i = m.entrySet().iterator();\r
- while ( i.hasNext() ) {\r
- Map.Entry entry = (Map.Entry)i.next();\r
- put(entry.getKey(),entry.getValue());\r
- }\r
- }\r
- \r
- public void clear() {\r
- //only delete active keys\r
- Iterator keys = keySet().iterator();\r
- while ( keys.hasNext() ) remove(keys.next());\r
- }\r
- \r
- public boolean containsValue(Object value) {\r
- if ( value == null ) {\r
- return super.containsValue(value);\r
- } else {\r
- Iterator i = super.entrySet().iterator();\r
- while (i.hasNext()) {\r
- Map.Entry e = (Map.Entry) i.next();\r
- MapEntry entry = (MapEntry) e.getValue();\r
- if (entry.isPrimary() && value.equals(entry.getValue())) return true;\r
- }//while\r
- return false;\r
- }//end if\r
- }\r
- \r
- public Object clone() {\r
- throw new UnsupportedOperationException("This operation is not valid on a replicated map");\r
- }\r
- \r
- /**\r
- * Returns the entire contents of the map\r
- * Map.Entry.getValue() will return a LazyReplicatedMap.MapEntry object containing all the information \r
- * about the object.\r
- * @return Set\r
- */\r
- public Set entrySetFull() {\r
- return super.entrySet();\r
- }\r
- \r
- public Set keySetFull() {\r
- return super.keySet();\r
- }\r
- \r
- public int sizeFull() {\r
- return super.size();\r
- }\r
- \r
- public Set entrySet() {\r
- LinkedHashSet set = new LinkedHashSet(super.size());\r
- Iterator i = super.entrySet().iterator();\r
- while ( i.hasNext() ) {\r
- Map.Entry e = (Map.Entry)i.next();\r
- MapEntry entry = (MapEntry)e.getValue();\r
- if ( entry.isPrimary() ) set.add(entry);\r
- }\r
- return Collections.unmodifiableSet(set);\r
- }\r
- \r
- public Set keySet() {\r
- //todo implement\r
- //should only return keys where this is active.\r
- LinkedHashSet set = new LinkedHashSet(super.size());\r
- Iterator i = super.entrySet().iterator();\r
- while ( i.hasNext() ) {\r
- Map.Entry e = (Map.Entry)i.next();\r
- MapEntry entry = (MapEntry)e.getValue();\r
- if ( entry.isPrimary() ) set.add(entry.getKey());\r
- }\r
- return Collections.unmodifiableSet(set);\r
- }\r
- \r
- \r
- public int size() {\r
- //todo, implement a counter variable instead\r
- //only count active members in this node\r
- int counter = 0;\r
- Iterator it = Collections.unmodifiableSet(super.entrySet()).iterator();\r
- while (it.hasNext() ) {\r
- Map.Entry e = (Map.Entry) it.next();\r
- if ( e != null ) {\r
- MapEntry entry = (MapEntry) e.getValue();\r
- if (entry.isPrimary() && entry.getValue() != null) counter++;\r
- }\r
- }\r
- return counter;\r
- }\r
- \r
- protected boolean removeEldestEntry(Map.Entry eldest) {\r
- return false;\r
- }\r
- \r
- public boolean isEmpty() {\r
- return size()==0;\r
- }\r
- \r
- public Collection values() {\r
- ArrayList values = new ArrayList();\r
- Iterator i = super.entrySet().iterator();\r
- while ( i.hasNext() ) {\r
- Map.Entry e = (Map.Entry)i.next();\r
- MapEntry entry = (MapEntry)e.getValue();\r
- if ( entry.isPrimary() && entry.getValue()!=null) values.add(entry.getValue());\r
- }\r
- return Collections.unmodifiableCollection(values);\r
- }\r
- \r
-\r
-//------------------------------------------------------------------------------\r
-// Map Entry class\r
-//------------------------------------------------------------------------------\r
- public static class MapEntry implements Map.Entry {\r
- private boolean backup;\r
- private boolean proxy;\r
- private Member[] backupNodes;\r
-\r
- private Object key;\r
- private Object value;\r
-\r
- public MapEntry(Object key, Object value) {\r
- setKey(key);\r
- setValue(value);\r
- \r
- }\r
- \r
- public boolean isKeySerializable() {\r
- return (key == null) || (key instanceof Serializable);\r
- }\r
- \r
- public boolean isValueSerializable() {\r
- return (value==null) || (value instanceof Serializable);\r
- }\r
- \r
- public boolean isSerializable() {\r
- return isKeySerializable() && isValueSerializable();\r
- }\r
- \r
- public boolean isBackup() {\r
- return backup;\r
- }\r
-\r
- public void setBackup(boolean backup) {\r
- this.backup = backup;\r
- }\r
-\r
- public boolean isProxy() {\r
- return proxy;\r
- }\r
-\r
- public boolean isPrimary() {\r
- return ( (!proxy) && (!backup));\r
- }\r
-\r
- public void setProxy(boolean proxy) {\r
- this.proxy = proxy;\r
- }\r
-\r
- public boolean isDiffable() {\r
- return (value instanceof ReplicatedMapEntry) &&\r
- ((ReplicatedMapEntry)value).isDiffable();\r
- }\r
-\r
- public void setBackupNodes(Member[] nodes) {\r
- this.backupNodes = nodes;\r
- }\r
-\r
- public Member[] getBackupNodes() {\r
- return backupNodes;\r
- }\r
-\r
- public Object getValue() {\r
- return value;\r
- }\r
-\r
- public Object setValue(Object value) {\r
- Object old = this.value;\r
- this.value = (Serializable) value;\r
- return old;\r
- }\r
-\r
- public Object getKey() {\r
- return key;\r
- }\r
- \r
- public Object setKey(Object key) {\r
- Object old = this.key;\r
- this.key = (Serializable)key;\r
- return old;\r
- }\r
-\r
- public int hashCode() {\r
- return key.hashCode();\r
- }\r
-\r
- public boolean equals(Object o) {\r
- return key.equals(o);\r
- }\r
-\r
- /**\r
- * apply a diff, or an entire object\r
- * @param data byte[]\r
- * @param offset int\r
- * @param length int\r
- * @param diff boolean\r
- * @throws IOException\r
- * @throws ClassNotFoundException\r
- */\r
- public void apply(byte[] data, int offset, int length, boolean diff) throws IOException, ClassNotFoundException {\r
- if (isDiffable() && diff) {\r
- ReplicatedMapEntry rentry = (ReplicatedMapEntry) value;\r
- try {\r
- rentry.lock();\r
- rentry.applyDiff(data, offset, length);\r
- } finally {\r
- rentry.unlock();\r
- }\r
- } else if (length == 0) {\r
- value = null;\r
- proxy = true;\r
- } else {\r
- value = XByteBuffer.deserialize(data, offset, length);\r
- }\r
- }\r
- \r
- public String toString() {\r
- StringBuffer buf = new StringBuffer("MapEntry[key:");\r
- buf.append(getKey()).append("; ");\r
- buf.append("value:").append(getValue()).append("; ");\r
- buf.append("primary:").append(isPrimary()).append("; ");\r
- buf.append("backup:").append(isBackup()).append("; ");\r
- buf.append("proxy:").append(isProxy()).append(";]");\r
- return buf.toString();\r
- }\r
-\r
- }\r
-\r
-//------------------------------------------------------------------------------\r
-// map message to send to and from other maps\r
-//------------------------------------------------------------------------------\r
-\r
- public static class MapMessage implements Serializable {\r
- public static final int MSG_BACKUP = 1;\r
- public static final int MSG_RETRIEVE_BACKUP = 2;\r
- public static final int MSG_PROXY = 3;\r
- public static final int MSG_REMOVE = 4;\r
- public static final int MSG_STATE = 5;\r
- public static final int MSG_START = 6;\r
- public static final int MSG_STOP = 7;\r
- public static final int MSG_INIT = 8;\r
-\r
- private byte[] mapId;\r
- private int msgtype;\r
- private boolean diff;\r
- private transient Serializable key;\r
- private transient Serializable value;\r
- private byte[] valuedata;\r
- private byte[] keydata;\r
- private byte[] diffvalue;\r
- private Member[] nodes;\r
- \r
- public String toString() {\r
- StringBuffer buf = new StringBuffer("MapMessage[context=");\r
- buf.append(new String(mapId));\r
- buf.append("; type=");\r
- buf.append(getTypeDesc());\r
- buf.append("; key=");\r
- buf.append(key);\r
- buf.append("; value=");\r
- buf.append(value);\r
- return buf.toString();\r
- }\r
- \r
- public String getTypeDesc() {\r
- switch (msgtype) {\r
- case MSG_BACKUP: return "MSG_BACKUP";\r
- case MSG_RETRIEVE_BACKUP: return "MSG_RETRIEVE_BACKUP";\r
- case MSG_PROXY: return "MSG_PROXY";\r
- case MSG_REMOVE: return "MSG_REMOVE";\r
- case MSG_STATE: return "MSG_STATE";\r
- case MSG_START: return "MSG_START";\r
- case MSG_STOP: return "MSG_STOP";\r
- case MSG_INIT: return "MSG_INIT";\r
- default : return "UNKNOWN";\r
- }\r
- }\r
-\r
- public MapMessage() {}\r
-\r
- public MapMessage(byte[] mapId,int msgtype, boolean diff,\r
- Serializable key, Serializable value,\r
- byte[] diffvalue, Member[] nodes) {\r
- this.mapId = mapId;\r
- this.msgtype = msgtype;\r
- this.diff = diff;\r
- this.key = key;\r
- this.value = value;\r
- this.diffvalue = diffvalue;\r
- this.nodes = nodes;\r
- setValue(value);\r
- setKey(key);\r
- }\r
- \r
- public void deserialize(ClassLoader[] cls) throws IOException, ClassNotFoundException {\r
- key(cls);\r
- value(cls);\r
- }\r
-\r
- public int getMsgType() {\r
- return msgtype;\r
- }\r
-\r
- public boolean isDiff() {\r
- return diff;\r
- }\r
-\r
- public Serializable getKey() {\r
- try {\r
- return key(null);\r
- } catch ( Exception x ) {\r
- log.error("Deserialization error of the MapMessage.key",x);\r
- return null;\r
- }\r
- }\r
-\r
- public Serializable key(ClassLoader[] cls) throws IOException, ClassNotFoundException {\r
- if ( key!=null ) return key;\r
- if ( keydata == null || keydata.length == 0 ) return null;\r
- key = XByteBuffer.deserialize(keydata,0,keydata.length,cls);\r
- keydata = null;\r
- return key;\r
- }\r
- \r
- public byte[] getKeyData() {\r
- return keydata;\r
- }\r
- \r
- public Serializable getValue() {\r
- try {\r
- return value(null);\r
- } catch ( Exception x ) {\r
- log.error("Deserialization error of the MapMessage.value",x);\r
- return null;\r
- }\r
- }\r
-\r
- public Serializable value(ClassLoader[] cls) throws IOException, ClassNotFoundException {\r
- if ( value!=null ) return value;\r
- if ( valuedata == null || valuedata.length == 0 ) return null;\r
- value = XByteBuffer.deserialize(valuedata,0,valuedata.length,cls);\r
- valuedata = null;;\r
- return value;\r
- }\r
- \r
- public byte[] getValueData() {\r
- return valuedata;\r
- }\r
-\r
- public byte[] getDiffValue() {\r
- return diffvalue;\r
- }\r
-\r
- public Member[] getBackupNodes() {\r
- return nodes;\r
- }\r
-\r
- private void setBackUpNodes(Member[] nodes) {\r
- this.nodes = nodes;\r
- }\r
-\r
- public byte[] getMapId() {\r
- return mapId;\r
- }\r
-\r
- public void setValue(Serializable value) {\r
- try {\r
- if ( value != null ) valuedata = XByteBuffer.serialize(value);\r
- this.value = value;\r
- }catch ( IOException x ) {\r
- throw new RuntimeException(x);\r
- }\r
- }\r
- \r
- public void setKey(Serializable key) {\r
- try {\r
- if (key != null) keydata = XByteBuffer.serialize(key);\r
- this.key = key;\r
- } catch (IOException x) {\r
- throw new RuntimeException(x);\r
- }\r
- }\r
- \r
- protected Member[] readMembers(ObjectInput in) throws IOException, ClassNotFoundException {\r
- int nodecount = in.readInt();\r
- Member[] members = new Member[nodecount];\r
- for ( int i=0; i<members.length; i++ ) {\r
- byte[] d = new byte[in.readInt()];\r
- in.read(d);\r
- if (d.length > 0) members[i] = MemberImpl.getMember(d);\r
- }\r
- return members;\r
- }\r
- \r
- protected void writeMembers(ObjectOutput out,Member[] members) throws IOException {\r
- if ( members == null ) members = new Member[0];\r
- out.writeInt(members.length);\r
- for (int i=0; i<members.length; i++ ) {\r
- if ( members[i] != null ) {\r
- byte[] d = members[i] != null ? ( (MemberImpl)members[i]).getData(false) : new byte[0];\r
- out.writeInt(d.length);\r
- out.write(d);\r
- }\r
- }\r
- }\r
- \r
- \r
- /**\r
- * shallow clone\r
- * @return Object\r
- */\r
- public Object clone() {\r
- MapMessage msg = new MapMessage(this.mapId, this.msgtype, this.diff, this.key, this.value, this.diffvalue, this.nodes);\r
- msg.keydata = this.keydata;\r
- msg.valuedata = this.valuedata;\r
- return msg;\r
- }\r
- } //MapMessage\r
-\r
-\r
- public Channel getChannel() {\r
- return channel;\r
- }\r
-\r
- public byte[] getMapContextName() {\r
- return mapContextName;\r
- }\r
-\r
- public RpcChannel getRpcChannel() {\r
- return rpcChannel;\r
- }\r
-\r
- public long getRpcTimeout() {\r
- return rpcTimeout;\r
- }\r
-\r
- public Object getStateMutex() {\r
- return stateMutex;\r
- }\r
-\r
- public boolean isStateTransferred() {\r
- return stateTransferred;\r
- }\r
-\r
- public Object getMapOwner() {\r
- return mapOwner;\r
- }\r
-\r
- public ClassLoader[] getExternalLoaders() {\r
- return externalLoaders;\r
- }\r
-\r
- public int getChannelSendOptions() {\r
- return channelSendOptions;\r
- }\r
-\r
- public long getAccessTimeout() {\r
- return accessTimeout;\r
- }\r
-\r
- public void setMapOwner(Object mapOwner) {\r
- this.mapOwner = mapOwner;\r
- }\r
-\r
- public void setExternalLoaders(ClassLoader[] externalLoaders) {\r
- this.externalLoaders = externalLoaders;\r
- }\r
-\r
- public void setChannelSendOptions(int channelSendOptions) {\r
- this.channelSendOptions = channelSendOptions;\r
- }\r
-\r
- public void setAccessTimeout(long accessTimeout) {\r
- this.accessTimeout = accessTimeout;\r
- }\r
-\r
+/*
+ * 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.apache.catalina.tribes.tipis;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelListener;
+import org.apache.catalina.tribes.Heartbeat;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipListener;
+import org.apache.catalina.tribes.group.Response;
+import org.apache.catalina.tribes.group.RpcCallback;
+import org.apache.catalina.tribes.group.RpcChannel;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.catalina.tribes.membership.MemberImpl;
+import org.apache.catalina.tribes.util.Arrays;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import java.util.ConcurrentModificationException;
+
+/**
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public abstract class AbstractReplicatedMap extends LinkedHashMap implements RpcCallback, ChannelListener, MembershipListener, Heartbeat {
+ protected static Log log = LogFactory.getLog(AbstractReplicatedMap.class);
+
+ /**
+ * The default initial capacity - MUST be a power of two.
+ */
+ public static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ /**
+ * The load factor used when none specified in constructor.
+ **/
+ public static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ /**
+ * Used to identify the map
+ */
+ final String chset = "ISO-8859-1";
+
+//------------------------------------------------------------------------------
+// INSTANCE VARIABLES
+//------------------------------------------------------------------------------
+
+ /**
+ * Timeout for RPC messages, how long we will wait for a reply
+ */
+ protected transient long rpcTimeout = 5000;
+ /**
+ * Reference to the channel for sending messages
+ */
+ protected transient Channel channel;
+ /**
+ * The RpcChannel to send RPC messages through
+ */
+ protected transient RpcChannel rpcChannel;
+ /**
+ * The Map context name makes this map unique, this
+ * allows us to have more than one map shared
+ * through one channel
+ */
+ protected transient byte[] mapContextName;
+ /**
+ * Has the state been transferred
+ */
+ protected transient boolean stateTransferred = false;
+ /**
+ * Simple lock object for transfers
+ */
+ protected transient Object stateMutex = new Object();
+ /**
+ * A list of members in our map
+ */
+ protected transient HashMap mapMembers = new HashMap();
+ /**
+ * Our default send options
+ */
+ protected transient int channelSendOptions = Channel.SEND_OPTIONS_DEFAULT;
+ /**
+ * The owner of this map, ala a SessionManager for example
+ */
+ protected transient Object mapOwner;
+ /**
+ * External class loaders if serialization and deserialization is to be performed successfully.
+ */
+ protected transient ClassLoader[] externalLoaders;
+
+ /**
+ * The node we are currently backing up data to, this index will rotate
+ * on a round robin basis
+ */
+ protected transient int currentNode = 0;
+
+ /**
+ * Since the map keeps internal membership
+ * this is the timeout for a ping message to be responded to
+ * If a remote map doesn't respond within this timeframe,
+ * its considered dead.
+ */
+ protected transient long accessTimeout = 5000;
+
+ /**
+ * Readable string of the mapContextName value
+ */
+ protected transient String mapname = "";
+
+
+//------------------------------------------------------------------------------
+// CONSTRUCTORS
+//------------------------------------------------------------------------------
+
+ /**
+ * Creates a new map
+ * @param channel The channel to use for communication
+ * @param timeout long - timeout for RPC messags
+ * @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param initialCapacity int - the size of this map, see HashMap
+ * @param loadFactor float - load factor, see HashMap
+ * @param cls - a list of classloaders to be used for deserialization of objects.
+ */
+ public AbstractReplicatedMap(Object owner,
+ Channel channel,
+ long timeout,
+ String mapContextName,
+ int initialCapacity,
+ float loadFactor,
+ int channelSendOptions,
+ ClassLoader[] cls) {
+ super(initialCapacity, loadFactor);
+ init(owner, channel, mapContextName, timeout, channelSendOptions, cls);
+
+ }
+
+ /**
+ * Helper methods, wraps a single member in an array
+ * @param m Member
+ * @return Member[]
+ */
+ protected Member[] wrap(Member m) {
+ if ( m == null ) return new Member[0];
+ else return new Member[] {m};
+ }
+
+ /**
+ * Initializes the map by creating the RPC channel, registering itself as a channel listener
+ * This method is also responsible for initiating the state transfer
+ * @param owner Object
+ * @param channel Channel
+ * @param mapContextName String
+ * @param timeout long
+ * @param channelSendOptions int
+ * @param cls ClassLoader[]
+ */
+ protected void init(Object owner, Channel channel, String mapContextName, long timeout, int channelSendOptions,ClassLoader[] cls) {
+ log.info("Initializing AbstractReplicatedMap with context name:"+mapContextName);
+ this.mapOwner = owner;
+ this.externalLoaders = cls;
+ this.channelSendOptions = channelSendOptions;
+ this.channel = channel;
+ this.rpcTimeout = timeout;
+
+ try {
+ this.mapname = mapContextName;
+ //unique context is more efficient if it is stored as bytes
+ this.mapContextName = mapContextName.getBytes(chset);
+ } catch (UnsupportedEncodingException x) {
+ log.warn("Unable to encode mapContextName[" + mapContextName + "] using getBytes(" + chset +") using default getBytes()", x);
+ this.mapContextName = mapContextName.getBytes();
+ }
+ if ( log.isTraceEnabled() ) log.trace("Created Lazy Map with name:"+mapContextName+", bytes:"+Arrays.toString(this.mapContextName));
+
+ //create an rpc channel and add the map as a listener
+ this.rpcChannel = new RpcChannel(this.mapContextName, channel, this);
+ //add this map as a message listener
+ this.channel.addChannelListener(this);
+ //listen for membership notifications
+ this.channel.addMembershipListener(this);
+
+
+ try {
+ //broadcast our map, this just notifies other members of our existence
+ broadcast(MapMessage.MSG_INIT, true);
+ //transfer state from another map
+ transferState();
+ //state is transferred, we are ready for messaging
+ broadcast(MapMessage.MSG_START, true);
+ } catch (ChannelException x) {
+ log.warn("Unable to send map start message.");
+ throw new RuntimeException("Unable to start replicated map.",x);
+ }
+ }
+
+
+ /**
+ * Sends a ping out to all the members in the cluster, not just map members
+ * that this map is alive.
+ * @param timeout long
+ * @throws ChannelException
+ */
+ protected void ping(long timeout) throws ChannelException {
+ //send out a map membership message, only wait for the first reply
+ MapMessage msg = new MapMessage(this.mapContextName,
+ MapMessage.MSG_INIT,
+ false,
+ null,
+ null,
+ null,
+ wrap(channel.getLocalMember(false)));
+ if ( channel.getMembers().length > 0 ) {
+ //send a ping, wait for all nodes to reply
+ Response[] resp = rpcChannel.send(channel.getMembers(),
+ msg, rpcChannel.ALL_REPLY,
+ (channelSendOptions),
+ (int) accessTimeout);
+ for (int i = 0; i < resp.length; i++) {
+ memberAlive(resp[i].getSource());
+ } //for
+ }
+ //update our map of members, expire some if we didn't receive a ping back
+ synchronized (mapMembers) {
+ Iterator it = mapMembers.entrySet().iterator();
+ long now = System.currentTimeMillis();
+ while ( it.hasNext() ) {
+ Map.Entry entry = (Map.Entry)it.next();
+ long access = ((Long)entry.getValue()).longValue();
+ if ( (now - access) > timeout ) memberDisappeared((Member)entry.getKey());
+ }
+ }//synch
+ }
+
+ /**
+ * We have received a member alive notification
+ * @param member Member
+ */
+ protected void memberAlive(Member member) {
+ synchronized (mapMembers) {
+ if (!mapMembers.containsKey(member)) {
+ mapMemberAdded(member);
+ } //end if
+ mapMembers.put(member, new Long(System.currentTimeMillis()));
+ }
+ }
+
+ /**
+ * Helper method to broadcast a message to all members in a channel
+ * @param msgtype int
+ * @param rpc boolean
+ * @throws ChannelException
+ */
+ protected void broadcast(int msgtype, boolean rpc) throws ChannelException {
+ //send out a map membership message, only wait for the first reply
+ MapMessage msg = new MapMessage(this.mapContextName, msgtype,
+ false, null, null, null, wrap(channel.getLocalMember(false)));
+ if ( rpc) {
+ Response[] resp = rpcChannel.send(channel.getMembers(), msg, rpcChannel.FIRST_REPLY, (channelSendOptions),rpcTimeout);
+ for (int i = 0; i < resp.length; i++) {
+ mapMemberAdded(resp[i].getSource());
+ messageReceived(resp[i].getMessage(), resp[i].getSource());
+ }
+ } else {
+ channel.send(channel.getMembers(),msg,channelSendOptions);
+ }
+ }
+
+ public void breakdown() {
+ finalize();
+ }
+
+ public void finalize() {
+ try {broadcast(MapMessage.MSG_STOP,false); }catch ( Exception ignore){}
+ //cleanup
+ if (this.rpcChannel != null) {
+ this.rpcChannel.breakdown();
+ }
+ if (this.channel != null) {
+ this.channel.removeChannelListener(this);
+ this.channel.removeMembershipListener(this);
+ }
+ this.rpcChannel = null;
+ this.channel = null;
+ this.mapMembers.clear();
+ super.clear();
+ this.stateTransferred = false;
+ this.externalLoaders = null;
+ }
+
+ public int hashCode() {
+ return Arrays.hashCode(this.mapContextName);
+ }
+
+ public boolean equals(Object o) {
+ if ( o == null ) return false;
+ if ( !(o instanceof AbstractReplicatedMap)) return false;
+ if ( !(o.getClass().equals(this.getClass())) ) return false;
+ AbstractReplicatedMap other = (AbstractReplicatedMap)o;
+ return Arrays.equals(mapContextName,other.mapContextName);
+ }
+
+//------------------------------------------------------------------------------
+// GROUP COM INTERFACES
+//------------------------------------------------------------------------------
+ public Member[] getMapMembers(HashMap members) {
+ synchronized (members) {
+ Member[] result = new Member[members.size()];
+ members.keySet().toArray(result);
+ return result;
+ }
+ }
+ public Member[] getMapMembers() {
+ return getMapMembers(this.mapMembers);
+ }
+
+ public Member[] getMapMembersExcl(Member[] exclude) {
+ synchronized (mapMembers) {
+ HashMap list = (HashMap)mapMembers.clone();
+ for (int i=0; i<exclude.length;i++) list.remove(exclude[i]);
+ return getMapMembers(list);
+ }
+ }
+
+
+ /**
+ * Replicates any changes to the object since the last time
+ * The object has to be primary, ie, if the object is a proxy or a backup, it will not be replicated<br>
+ * @param complete - if set to true, the object is replicated to its backup
+ * if set to false, only objects that implement ReplicatedMapEntry and the isDirty() returns true will
+ * be replicated
+ */
+ public void replicate(Object key, boolean complete) {
+ if ( log.isTraceEnabled() )
+ log.trace("Replicate invoked on key:"+key);
+ MapEntry entry = (MapEntry)super.get(key);
+ if ( entry == null ) return;
+ if ( !entry.isSerializable() ) return;
+ if (entry != null && entry.isPrimary() && entry.getBackupNodes()!= null && entry.getBackupNodes().length > 0) {
+ Object value = entry.getValue();
+ //check to see if we need to replicate this object isDirty()||complete
+ boolean repl = complete || ( (value instanceof ReplicatedMapEntry) && ( (ReplicatedMapEntry) value).isDirty());
+
+ if (!repl) {
+ if ( log.isTraceEnabled() )
+ log.trace("Not replicating:"+key+", no change made");
+
+ return;
+ }
+ //check to see if the message is diffable
+ boolean diff = ( (value instanceof ReplicatedMapEntry) && ( (ReplicatedMapEntry) value).isDiffable());
+ MapMessage msg = null;
+ if (diff) {
+ ReplicatedMapEntry rentry = (ReplicatedMapEntry)entry.getValue();
+ try {
+ rentry.lock();
+ //construct a diff message
+ msg = new MapMessage(mapContextName, MapMessage.MSG_BACKUP,
+ true, (Serializable) entry.getKey(), null,
+ rentry.getDiff(),
+ entry.getBackupNodes());
+ } catch (IOException x) {
+ log.error("Unable to diff object. Will replicate the entire object instead.", x);
+ } finally {
+ rentry.unlock();
+ }
+
+ }
+ if (msg == null) {
+ //construct a complete
+ msg = new MapMessage(mapContextName, MapMessage.MSG_BACKUP,
+ false, (Serializable) entry.getKey(),
+ (Serializable) entry.getValue(),
+ null, entry.getBackupNodes());
+
+ }
+ try {
+ if ( channel!=null && entry.getBackupNodes()!= null && entry.getBackupNodes().length > 0 ) {
+ channel.send(entry.getBackupNodes(), msg, channelSendOptions);
+ }
+ } catch (ChannelException x) {
+ log.error("Unable to replicate data.", x);
+ }
+ } //end if
+
+ }
+
+ /**
+ * This can be invoked by a periodic thread to replicate out any changes.
+ * For maps that don't store objects that implement ReplicatedMapEntry, this
+ * method should be used infrequently to avoid large amounts of data transfer
+ * @param complete boolean
+ */
+ public void replicate(boolean complete) {
+ Iterator i = super.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry e = (Map.Entry) i.next();
+ replicate(e.getKey(), complete);
+ } //while
+
+ }
+
+ public void transferState() {
+ try {
+ Member[] members = getMapMembers();
+ Member backup = members.length > 0 ? (Member) members[0] : null;
+ if (backup != null) {
+ MapMessage msg = new MapMessage(mapContextName, MapMessage.MSG_STATE, false,
+ null, null, null, null);
+ Response[] resp = rpcChannel.send(new Member[] {backup}, msg, rpcChannel.FIRST_REPLY, channelSendOptions, rpcTimeout);
+ if (resp.length > 0) {
+ synchronized (stateMutex) {
+ msg = (MapMessage) resp[0].getMessage();
+ msg.deserialize(getExternalLoaders());
+ ArrayList list = (ArrayList) msg.getValue();
+ for (int i = 0; i < list.size(); i++) {
+ messageReceived( (Serializable) list.get(i), resp[0].getSource());
+ } //for
+ }
+ } else {
+ log.warn("Transfer state, 0 replies, probably a timeout.");
+ }
+ }
+ } catch (ChannelException x) {
+ log.error("Unable to transfer LazyReplicatedMap state.", x);
+ } catch (IOException x) {
+ log.error("Unable to transfer LazyReplicatedMap state.", x);
+ } catch (ClassNotFoundException x) {
+ log.error("Unable to transfer LazyReplicatedMap state.", x);
+ }
+ stateTransferred = true;
+ }
+
+ /**
+ * @todo implement state transfer
+ * @param msg Serializable
+ * @return Serializable - null if no reply should be sent
+ */
+ public Serializable replyRequest(Serializable msg, final Member sender) {
+ if (! (msg instanceof MapMessage))return null;
+ MapMessage mapmsg = (MapMessage) msg;
+
+ //map init request
+ if (mapmsg.getMsgType() == mapmsg.MSG_INIT) {
+ mapmsg.setBackUpNodes(wrap(channel.getLocalMember(false)));
+ return mapmsg;
+ }
+
+ //map start request
+ if (mapmsg.getMsgType() == mapmsg.MSG_START) {
+ mapmsg.setBackUpNodes(wrap(channel.getLocalMember(false)));
+ mapMemberAdded(sender);
+ return mapmsg;
+ }
+
+ //backup request
+ if (mapmsg.getMsgType() == mapmsg.MSG_RETRIEVE_BACKUP) {
+ MapEntry entry = (MapEntry)super.get(mapmsg.getKey());
+ if (entry == null || (!entry.isSerializable()) )return null;
+ mapmsg.setValue( (Serializable) entry.getValue());
+ return mapmsg;
+ }
+
+ //state transfer request
+ if (mapmsg.getMsgType() == mapmsg.MSG_STATE) {
+ synchronized (stateMutex) { //make sure we dont do two things at the same time
+ ArrayList list = new ArrayList();
+ Iterator i = super.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry e = (Map.Entry) i.next();
+ MapEntry entry = (MapEntry) e.getValue();
+ if ( entry.isSerializable() ) {
+ MapMessage me = new MapMessage(mapContextName, MapMessage.MSG_PROXY,
+ false, (Serializable) entry.getKey(), null, null, entry.getBackupNodes());
+ list.add(me);
+ }
+ }
+ mapmsg.setValue(list);
+ return mapmsg;
+
+ } //synchronized
+ }
+
+ return null;
+
+ }
+
+ /**
+ * If the reply has already been sent to the requesting thread,
+ * the rpc callback can handle any data that comes in after the fact.
+ * @param msg Serializable
+ * @param sender Member
+ */
+ public void leftOver(Serializable msg, Member sender) {
+ //left over membership messages
+ if (! (msg instanceof MapMessage))return;
+
+ MapMessage mapmsg = (MapMessage) msg;
+ try {
+ mapmsg.deserialize(getExternalLoaders());
+ if (mapmsg.getMsgType() == MapMessage.MSG_START) {
+ mapMemberAdded(mapmsg.getBackupNodes()[0]);
+ } else if (mapmsg.getMsgType() == MapMessage.MSG_INIT) {
+ memberAlive(mapmsg.getBackupNodes()[0]);
+ }
+ } catch (IOException x ) {
+ log.error("Unable to deserialize MapMessage.",x);
+ } catch (ClassNotFoundException x ) {
+ log.error("Unable to deserialize MapMessage.",x);
+ }
+ }
+
+ public void messageReceived(Serializable msg, Member sender) {
+ if (! (msg instanceof MapMessage)) return;
+
+ MapMessage mapmsg = (MapMessage) msg;
+ if ( log.isTraceEnabled() ) {
+ log.trace("Map["+mapname+"] received message:"+mapmsg);
+ }
+
+ try {
+ mapmsg.deserialize(getExternalLoaders());
+ } catch (IOException x) {
+ log.error("Unable to deserialize MapMessage.", x);
+ return;
+ } catch (ClassNotFoundException x) {
+ log.error("Unable to deserialize MapMessage.", x);
+ return;
+ }
+ if ( log.isTraceEnabled() )
+ log.trace("Map message received from:"+sender.getName()+" msg:"+mapmsg);
+ if (mapmsg.getMsgType() == MapMessage.MSG_START) {
+ mapMemberAdded(mapmsg.getBackupNodes()[0]);
+ }
+
+ if (mapmsg.getMsgType() == MapMessage.MSG_STOP) {
+ memberDisappeared(mapmsg.getBackupNodes()[0]);
+ }
+
+ if (mapmsg.getMsgType() == MapMessage.MSG_PROXY) {
+ MapEntry entry = (MapEntry)super.get(mapmsg.getKey());
+ if ( entry==null ) {
+ entry = new MapEntry(mapmsg.getKey(), mapmsg.getValue());
+ entry.setBackup(false);
+ entry.setProxy(true);
+ entry.setBackupNodes(mapmsg.getBackupNodes());
+ super.put(entry.getKey(), entry);
+ } else {
+ entry.setProxy(true);
+ entry.setBackup(false);
+ entry.setBackupNodes(mapmsg.getBackupNodes());
+ }
+ }
+
+ if (mapmsg.getMsgType() == MapMessage.MSG_REMOVE) {
+ super.remove(mapmsg.getKey());
+ }
+
+ if (mapmsg.getMsgType() == MapMessage.MSG_BACKUP) {
+ MapEntry entry = (MapEntry)super.get(mapmsg.getKey());
+ if (entry == null) {
+ entry = new MapEntry(mapmsg.getKey(), mapmsg.getValue());
+ entry.setBackup(true);
+ entry.setProxy(false);
+ entry.setBackupNodes(mapmsg.getBackupNodes());
+ if (mapmsg.getValue()!=null && mapmsg.getValue() instanceof ReplicatedMapEntry ) {
+ ((ReplicatedMapEntry)mapmsg.getValue()).setOwner(getMapOwner());
+ }
+ } else {
+ entry.setBackup(true);
+ entry.setProxy(false);
+ entry.setBackupNodes(mapmsg.getBackupNodes());
+ if (entry.getValue() instanceof ReplicatedMapEntry) {
+ ReplicatedMapEntry diff = (ReplicatedMapEntry) entry.getValue();
+ if (mapmsg.isDiff()) {
+ try {
+ diff.lock();
+ diff.applyDiff(mapmsg.getDiffValue(), 0, mapmsg.getDiffValue().length);
+ } catch (Exception x) {
+ log.error("Unable to apply diff to key:" + entry.getKey(), x);
+ } finally {
+ diff.unlock();
+ }
+ } else {
+ if ( mapmsg.getValue()!=null ) entry.setValue(mapmsg.getValue());
+ ((ReplicatedMapEntry)entry.getValue()).setOwner(getMapOwner());
+ } //end if
+ } else if (mapmsg.getValue() instanceof ReplicatedMapEntry) {
+ ReplicatedMapEntry re = (ReplicatedMapEntry)mapmsg.getValue();
+ re.setOwner(getMapOwner());
+ entry.setValue(re);
+ } else {
+ if ( mapmsg.getValue()!=null ) entry.setValue(mapmsg.getValue());
+ } //end if
+ } //end if
+ super.put(entry.getKey(), entry);
+ } //end if
+ }
+
+ public boolean accept(Serializable msg, Member sender) {
+ boolean result = false;
+ if (msg instanceof MapMessage) {
+ if ( log.isTraceEnabled() ) log.trace("Map["+mapname+"] accepting...."+msg);
+ result = Arrays.equals(mapContextName, ( (MapMessage) msg).getMapId());
+ if ( log.isTraceEnabled() ) log.trace("Msg["+mapname+"] accepted["+result+"]...."+msg);
+ }
+ return result;
+ }
+
+ public void mapMemberAdded(Member member) {
+ if ( member.equals(getChannel().getLocalMember(false)) ) return;
+ boolean memberAdded = false;
+ //select a backup node if we don't have one
+ synchronized (mapMembers) {
+ if (!mapMembers.containsKey(member) ) {
+ mapMembers.put(member, new Long(System.currentTimeMillis()));
+ memberAdded = true;
+ }
+ }
+ if ( memberAdded ) {
+ synchronized (stateMutex) {
+ Iterator i = super.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry e = (Map.Entry) i.next();
+ MapEntry entry = (MapEntry) e.getValue();
+ if ( entry == null ) continue;
+ if (entry.isPrimary() && (entry.getBackupNodes() == null || entry.getBackupNodes().length == 0)) {
+ try {
+ Member[] backup = publishEntryInfo(entry.getKey(), entry.getValue());
+ entry.setBackupNodes(backup);
+ } catch (ChannelException x) {
+ log.error("Unable to select backup node.", x);
+ } //catch
+ } //end if
+ } //while
+ } //synchronized
+ }//end if
+ }
+
+ public boolean inSet(Member m, Member[] set) {
+ if ( set == null ) return false;
+ boolean result = false;
+ for (int i=0; i<set.length && (!result); i++ )
+ if ( m.equals(set[i]) ) result = true;
+ return result;
+ }
+
+ public Member[] excludeFromSet(Member[] mbrs, Member[] set) {
+ ArrayList result = new ArrayList();
+ for (int i=0; i<set.length; i++ ) {
+ boolean include = true;
+ for (int j=0; j<mbrs.length; j++ )
+ if ( mbrs[j].equals(set[i]) ) include = false;
+ if ( include ) result.add(set[i]);
+ }
+ return (Member[])result.toArray(new Member[result.size()]);
+ }
+
+ public void memberAdded(Member member) {
+ //do nothing
+ }
+
+ public void memberDisappeared(Member member) {
+ boolean removed = false;
+ synchronized (mapMembers) {
+ removed = (mapMembers.remove(member) != null );
+ }
+ Iterator i = super.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry e = (Map.Entry) i.next();
+ MapEntry entry = (MapEntry) e.getValue();
+ if (entry.isPrimary() && inSet(member,entry.getBackupNodes())) {
+ try {
+ Member[] backup = publishEntryInfo(entry.getKey(), entry.getValue());
+ entry.setBackupNodes(backup);
+ } catch (ChannelException x) {
+ log.error("Unable to relocate[" + entry.getKey() + "] to a new backup node", x);
+ }
+ } //end if
+ } //while
+ }
+
+ public int getNextBackupIndex() {
+ int size = mapMembers.size();
+ if (mapMembers.size() == 0)return -1;
+ int node = currentNode++;
+ if (node >= size) {
+ node = 0;
+ currentNode = 0;
+ }
+ return node;
+ }
+ public Member getNextBackupNode() {
+ Member[] members = getMapMembers();
+ int node = getNextBackupIndex();
+ if ( members.length == 0 || node==-1) return null;
+ if ( node >= members.length ) node = 0;
+ return members[node];
+ }
+
+ protected abstract Member[] publishEntryInfo(Object key, Object value) throws ChannelException;
+
+ public void heartbeat() {
+ try {
+ ping(accessTimeout);
+ }catch ( Exception x ) {
+ log.error("Unable to send AbstractReplicatedMap.ping message",x);
+ }
+ }
+
+//------------------------------------------------------------------------------
+// METHODS TO OVERRIDE
+//------------------------------------------------------------------------------
+
+ /**
+ * Removes an object from this map, it will also remove it from
+ *
+ * @param key Object
+ * @return Object
+ */
+ public Object remove(Object key) {
+ MapEntry entry = (MapEntry)super.remove(key);
+
+ try {
+ if (getMapMembers().length > 0 ) {
+ MapMessage msg = new MapMessage(getMapContextName(), MapMessage.MSG_REMOVE, false, (Serializable) key, null, null, null);
+ getChannel().send(getMapMembers(), msg, getChannelSendOptions());
+ }
+ } catch ( ChannelException x ) {
+ log.error("Unable to replicate out data for a LazyReplicatedMap.remove operation",x);
+ }
+ return entry!=null?entry.getValue():null;
+ }
+
+ public Object get(Object key) {
+ MapEntry entry = (MapEntry)super.get(key);
+ if (log.isTraceEnabled()) log.trace("Requesting id:"+key+" entry:"+entry);
+ if ( entry == null ) return null;
+ if ( !entry.isPrimary() ) {
+ //if the message is not primary, we need to retrieve the latest value
+ try {
+ Member[] backup = null;
+ MapMessage msg = null;
+ if ( !entry.isBackup() ) {
+ //make sure we don't retrieve from ourselves
+ msg = new MapMessage(getMapContextName(), MapMessage.MSG_RETRIEVE_BACKUP, false,
+ (Serializable) key, null, null, null);
+ Response[] resp = getRpcChannel().send(entry.getBackupNodes(),msg, this.getRpcChannel().FIRST_REPLY, Channel.SEND_OPTIONS_DEFAULT, getRpcTimeout());
+ if (resp == null || resp.length == 0) {
+ //no responses
+ log.warn("Unable to retrieve remote object for key:" + key);
+ return null;
+ }
+ msg = (MapMessage) resp[0].getMessage();
+ msg.deserialize(getExternalLoaders());
+ backup = entry.getBackupNodes();
+ if ( entry.getValue() instanceof ReplicatedMapEntry ) {
+ ReplicatedMapEntry val = (ReplicatedMapEntry)entry.getValue();
+ val.setOwner(getMapOwner());
+ }
+ if ( msg.getValue()!=null ) entry.setValue(msg.getValue());
+ }
+ if (entry.isBackup()) {
+ //select a new backup node
+ backup = publishEntryInfo(key, entry.getValue());
+ } else if ( entry.isProxy() ) {
+ //invalidate the previous primary
+ msg = new MapMessage(getMapContextName(),MapMessage.MSG_PROXY,false,(Serializable)key,null,null,backup);
+ Member[] dest = getMapMembersExcl(backup);
+ if ( dest!=null && dest.length >0) {
+ getChannel().send(dest, msg, getChannelSendOptions());
+ }
+ }
+
+ entry.setBackupNodes(backup);
+ entry.setBackup(false);
+ entry.setProxy(false);
+
+
+ } catch (Exception x) {
+ log.error("Unable to replicate out data for a LazyReplicatedMap.get operation", x);
+ return null;
+ }
+ }
+ if (log.isTraceEnabled()) log.trace("Requesting id:"+key+" result:"+entry.getValue());
+ if ( entry.getValue() != null && entry.getValue() instanceof ReplicatedMapEntry ) {
+ ReplicatedMapEntry val = (ReplicatedMapEntry)entry.getValue();
+ //hack, somehow this is not being set above
+ val.setOwner(getMapOwner());
+
+ }
+ return entry.getValue();
+ }
+
+
+ protected void printMap(String header) {
+ try {
+ System.out.println("\nDEBUG MAP:"+header);
+ System.out.println("Map["+ new String(mapContextName, chset) + ", Map Size:" + super.size());
+ Member[] mbrs = getMapMembers();
+ for ( int i=0; i<mbrs.length;i++ ) {
+ System.out.println("Mbr["+(i+1)+"="+mbrs[i].getName());
+ }
+ Iterator i = super.entrySet().iterator();
+ int cnt = 0;
+
+ while (i.hasNext()) {
+ Map.Entry e = (Map.Entry) i.next();
+ System.out.println( (++cnt) + ". " + e.getValue());
+ }
+ System.out.println("EndMap]\n\n");
+ }catch ( Exception ignore) {
+ ignore.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns true if the key has an entry in the map.
+ * The entry can be a proxy or a backup entry, invoking <code>get(key)</code>
+ * will make this entry primary for the group
+ * @param key Object
+ * @return boolean
+ */
+ public boolean containsKey(Object key) {
+ return super.containsKey(key);
+ }
+
+
+ public Object put(Object key, Object value) {
+ MapEntry entry = new MapEntry(key,value);
+ entry.setBackup(false);
+ entry.setProxy(false);
+
+ Object old = null;
+
+ //make sure that any old values get removed
+ if ( containsKey(key) ) old = remove(key);
+ try {
+ Member[] backup = publishEntryInfo(key, value);
+ entry.setBackupNodes(backup);
+ } catch (ChannelException x) {
+ log.error("Unable to replicate out data for a LazyReplicatedMap.put operation", x);
+ }
+ super.put(key,entry);
+ return old;
+ }
+
+
+ /**
+ * Copies all values from one map to this instance
+ * @param m Map
+ */
+ public void putAll(Map m) {
+ Iterator i = m.entrySet().iterator();
+ while ( i.hasNext() ) {
+ Map.Entry entry = (Map.Entry)i.next();
+ put(entry.getKey(),entry.getValue());
+ }
+ }
+
+ public void clear() {
+ //only delete active keys
+ Iterator keys = keySet().iterator();
+ while ( keys.hasNext() ) remove(keys.next());
+ }
+
+ public boolean containsValue(Object value) {
+ if ( value == null ) {
+ return super.containsValue(value);
+ } else {
+ Iterator i = super.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry e = (Map.Entry) i.next();
+ MapEntry entry = (MapEntry) e.getValue();
+ if (entry.isPrimary() && value.equals(entry.getValue())) return true;
+ }//while
+ return false;
+ }//end if
+ }
+
+ public Object clone() {
+ throw new UnsupportedOperationException("This operation is not valid on a replicated map");
+ }
+
+ /**
+ * Returns the entire contents of the map
+ * Map.Entry.getValue() will return a LazyReplicatedMap.MapEntry object containing all the information
+ * about the object.
+ * @return Set
+ */
+ public Set entrySetFull() {
+ return super.entrySet();
+ }
+
+ public Set keySetFull() {
+ return super.keySet();
+ }
+
+ public int sizeFull() {
+ return super.size();
+ }
+
+ public Set entrySet() {
+ LinkedHashSet set = new LinkedHashSet(super.size());
+ Iterator i = super.entrySet().iterator();
+ while ( i.hasNext() ) {
+ Map.Entry e = (Map.Entry)i.next();
+ MapEntry entry = (MapEntry)e.getValue();
+ if ( entry.isPrimary() ) set.add(entry);
+ }
+ return Collections.unmodifiableSet(set);
+ }
+
+ public Set keySet() {
+ //todo implement
+ //should only return keys where this is active.
+ LinkedHashSet set = new LinkedHashSet(super.size());
+ Iterator i = super.entrySet().iterator();
+ while ( i.hasNext() ) {
+ Map.Entry e = (Map.Entry)i.next();
+ MapEntry entry = (MapEntry)e.getValue();
+ if ( entry.isPrimary() ) set.add(entry.getKey());
+ }
+ return Collections.unmodifiableSet(set);
+ }
+
+
+ public int size() {
+ //todo, implement a counter variable instead
+ //only count active members in this node
+ int counter = 0;
+ Iterator it = Collections.unmodifiableSet(super.entrySet()).iterator();
+ while (it.hasNext() ) {
+ Map.Entry e = (Map.Entry) it.next();
+ if ( e != null ) {
+ MapEntry entry = (MapEntry) e.getValue();
+ if (entry.isPrimary() && entry.getValue() != null) counter++;
+ }
+ }
+ return counter;
+ }
+
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return false;
+ }
+
+ public boolean isEmpty() {
+ return size()==0;
+ }
+
+ public Collection values() {
+ ArrayList values = new ArrayList();
+ Iterator i = super.entrySet().iterator();
+ while ( i.hasNext() ) {
+ Map.Entry e = (Map.Entry)i.next();
+ MapEntry entry = (MapEntry)e.getValue();
+ if ( entry.isPrimary() && entry.getValue()!=null) values.add(entry.getValue());
+ }
+ return Collections.unmodifiableCollection(values);
+ }
+
+
+//------------------------------------------------------------------------------
+// Map Entry class
+//------------------------------------------------------------------------------
+ public static class MapEntry implements Map.Entry {
+ private boolean backup;
+ private boolean proxy;
+ private Member[] backupNodes;
+
+ private Object key;
+ private Object value;
+
+ public MapEntry(Object key, Object value) {
+ setKey(key);
+ setValue(value);
+
+ }
+
+ public boolean isKeySerializable() {
+ return (key == null) || (key instanceof Serializable);
+ }
+
+ public boolean isValueSerializable() {
+ return (value==null) || (value instanceof Serializable);
+ }
+
+ public boolean isSerializable() {
+ return isKeySerializable() && isValueSerializable();
+ }
+
+ public boolean isBackup() {
+ return backup;
+ }
+
+ public void setBackup(boolean backup) {
+ this.backup = backup;
+ }
+
+ public boolean isProxy() {
+ return proxy;
+ }
+
+ public boolean isPrimary() {
+ return ( (!proxy) && (!backup));
+ }
+
+ public void setProxy(boolean proxy) {
+ this.proxy = proxy;
+ }
+
+ public boolean isDiffable() {
+ return (value instanceof ReplicatedMapEntry) &&
+ ((ReplicatedMapEntry)value).isDiffable();
+ }
+
+ public void setBackupNodes(Member[] nodes) {
+ this.backupNodes = nodes;
+ }
+
+ public Member[] getBackupNodes() {
+ return backupNodes;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public Object setValue(Object value) {
+ Object old = this.value;
+ this.value = (Serializable) value;
+ return old;
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public Object setKey(Object key) {
+ Object old = this.key;
+ this.key = (Serializable)key;
+ return old;
+ }
+
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ return key.equals(o);
+ }
+
+ /**
+ * apply a diff, or an entire object
+ * @param data byte[]
+ * @param offset int
+ * @param length int
+ * @param diff boolean
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ public void apply(byte[] data, int offset, int length, boolean diff) throws IOException, ClassNotFoundException {
+ if (isDiffable() && diff) {
+ ReplicatedMapEntry rentry = (ReplicatedMapEntry) value;
+ try {
+ rentry.lock();
+ rentry.applyDiff(data, offset, length);
+ } finally {
+ rentry.unlock();
+ }
+ } else if (length == 0) {
+ value = null;
+ proxy = true;
+ } else {
+ value = XByteBuffer.deserialize(data, offset, length);
+ }
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer("MapEntry[key:");
+ buf.append(getKey()).append("; ");
+ buf.append("value:").append(getValue()).append("; ");
+ buf.append("primary:").append(isPrimary()).append("; ");
+ buf.append("backup:").append(isBackup()).append("; ");
+ buf.append("proxy:").append(isProxy()).append(";]");
+ return buf.toString();
+ }
+
+ }
+
+//------------------------------------------------------------------------------
+// map message to send to and from other maps
+//------------------------------------------------------------------------------
+
+ public static class MapMessage implements Serializable {
+ public static final int MSG_BACKUP = 1;
+ public static final int MSG_RETRIEVE_BACKUP = 2;
+ public static final int MSG_PROXY = 3;
+ public static final int MSG_REMOVE = 4;
+ public static final int MSG_STATE = 5;
+ public static final int MSG_START = 6;
+ public static final int MSG_STOP = 7;
+ public static final int MSG_INIT = 8;
+
+ private byte[] mapId;
+ private int msgtype;
+ private boolean diff;
+ private transient Serializable key;
+ private transient Serializable value;
+ private byte[] valuedata;
+ private byte[] keydata;
+ private byte[] diffvalue;
+ private Member[] nodes;
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer("MapMessage[context=");
+ buf.append(new String(mapId));
+ buf.append("; type=");
+ buf.append(getTypeDesc());
+ buf.append("; key=");
+ buf.append(key);
+ buf.append("; value=");
+ buf.append(value);
+ return buf.toString();
+ }
+
+ public String getTypeDesc() {
+ switch (msgtype) {
+ case MSG_BACKUP: return "MSG_BACKUP";
+ case MSG_RETRIEVE_BACKUP: return "MSG_RETRIEVE_BACKUP";
+ case MSG_PROXY: return "MSG_PROXY";
+ case MSG_REMOVE: return "MSG_REMOVE";
+ case MSG_STATE: return "MSG_STATE";
+ case MSG_START: return "MSG_START";
+ case MSG_STOP: return "MSG_STOP";
+ case MSG_INIT: return "MSG_INIT";
+ default : return "UNKNOWN";
+ }
+ }
+
+ public MapMessage() {}
+
+ public MapMessage(byte[] mapId,int msgtype, boolean diff,
+ Serializable key, Serializable value,
+ byte[] diffvalue, Member[] nodes) {
+ this.mapId = mapId;
+ this.msgtype = msgtype;
+ this.diff = diff;
+ this.key = key;
+ this.value = value;
+ this.diffvalue = diffvalue;
+ this.nodes = nodes;
+ setValue(value);
+ setKey(key);
+ }
+
+ public void deserialize(ClassLoader[] cls) throws IOException, ClassNotFoundException {
+ key(cls);
+ value(cls);
+ }
+
+ public int getMsgType() {
+ return msgtype;
+ }
+
+ public boolean isDiff() {
+ return diff;
+ }
+
+ public Serializable getKey() {
+ try {
+ return key(null);
+ } catch ( Exception x ) {
+ log.error("Deserialization error of the MapMessage.key",x);
+ return null;
+ }
+ }
+
+ public Serializable key(ClassLoader[] cls) throws IOException, ClassNotFoundException {
+ if ( key!=null ) return key;
+ if ( keydata == null || keydata.length == 0 ) return null;
+ key = XByteBuffer.deserialize(keydata,0,keydata.length,cls);
+ keydata = null;
+ return key;
+ }
+
+ public byte[] getKeyData() {
+ return keydata;
+ }
+
+ public Serializable getValue() {
+ try {
+ return value(null);
+ } catch ( Exception x ) {
+ log.error("Deserialization error of the MapMessage.value",x);
+ return null;
+ }
+ }
+
+ public Serializable value(ClassLoader[] cls) throws IOException, ClassNotFoundException {
+ if ( value!=null ) return value;
+ if ( valuedata == null || valuedata.length == 0 ) return null;
+ value = XByteBuffer.deserialize(valuedata,0,valuedata.length,cls);
+ valuedata = null;;
+ return value;
+ }
+
+ public byte[] getValueData() {
+ return valuedata;
+ }
+
+ public byte[] getDiffValue() {
+ return diffvalue;
+ }
+
+ public Member[] getBackupNodes() {
+ return nodes;
+ }
+
+ private void setBackUpNodes(Member[] nodes) {
+ this.nodes = nodes;
+ }
+
+ public byte[] getMapId() {
+ return mapId;
+ }
+
+ public void setValue(Serializable value) {
+ try {
+ if ( value != null ) valuedata = XByteBuffer.serialize(value);
+ this.value = value;
+ }catch ( IOException x ) {
+ throw new RuntimeException(x);
+ }
+ }
+
+ public void setKey(Serializable key) {
+ try {
+ if (key != null) keydata = XByteBuffer.serialize(key);
+ this.key = key;
+ } catch (IOException x) {
+ throw new RuntimeException(x);
+ }
+ }
+
+ protected Member[] readMembers(ObjectInput in) throws IOException, ClassNotFoundException {
+ int nodecount = in.readInt();
+ Member[] members = new Member[nodecount];
+ for ( int i=0; i<members.length; i++ ) {
+ byte[] d = new byte[in.readInt()];
+ in.read(d);
+ if (d.length > 0) members[i] = MemberImpl.getMember(d);
+ }
+ return members;
+ }
+
+ protected void writeMembers(ObjectOutput out,Member[] members) throws IOException {
+ if ( members == null ) members = new Member[0];
+ out.writeInt(members.length);
+ for (int i=0; i<members.length; i++ ) {
+ if ( members[i] != null ) {
+ byte[] d = members[i] != null ? ( (MemberImpl)members[i]).getData(false) : new byte[0];
+ out.writeInt(d.length);
+ out.write(d);
+ }
+ }
+ }
+
+
+ /**
+ * shallow clone
+ * @return Object
+ */
+ public Object clone() {
+ MapMessage msg = new MapMessage(this.mapId, this.msgtype, this.diff, this.key, this.value, this.diffvalue, this.nodes);
+ msg.keydata = this.keydata;
+ msg.valuedata = this.valuedata;
+ return msg;
+ }
+ } //MapMessage
+
+
+ public Channel getChannel() {
+ return channel;
+ }
+
+ public byte[] getMapContextName() {
+ return mapContextName;
+ }
+
+ public RpcChannel getRpcChannel() {
+ return rpcChannel;
+ }
+
+ public long getRpcTimeout() {
+ return rpcTimeout;
+ }
+
+ public Object getStateMutex() {
+ return stateMutex;
+ }
+
+ public boolean isStateTransferred() {
+ return stateTransferred;
+ }
+
+ public Object getMapOwner() {
+ return mapOwner;
+ }
+
+ public ClassLoader[] getExternalLoaders() {
+ return externalLoaders;
+ }
+
+ public int getChannelSendOptions() {
+ return channelSendOptions;
+ }
+
+ public long getAccessTimeout() {
+ return accessTimeout;
+ }
+
+ public void setMapOwner(Object mapOwner) {
+ this.mapOwner = mapOwner;
+ }
+
+ public void setExternalLoaders(ClassLoader[] externalLoaders) {
+ this.externalLoaders = externalLoaders;
+ }
+
+ public void setChannelSendOptions(int channelSendOptions) {
+ this.channelSendOptions = channelSendOptions;
+ }
+
+ public void setAccessTimeout(long accessTimeout) {
+ this.accessTimeout = accessTimeout;
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.tipis;\r
-\r
-import java.io.Serializable;\r
-\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelListener;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.MembershipListener;\r
-import org.apache.catalina.tribes.group.RpcCallback;\r
-import org.apache.catalina.tribes.util.Arrays;\r
-import org.apache.catalina.tribes.UniqueId;\r
-\r
-/**\r
- * A smart implementation of a stateful replicated map. uses primary/secondary backup strategy. \r
- * One node is always the primary and one node is always the backup.\r
- * This map is synchronized across a cluster, and only has one backup member.<br/>\r
- * A perfect usage for this map would be a session map for a session manager in a clustered environment.<br/>\r
- * The only way to modify this list is to use the <code>put, putAll, remove</code> methods.\r
- * entrySet, entrySetFull, keySet, keySetFull, returns all non modifiable sets.<br><br>\r
- * If objects (values) in the map change without invoking <code>put()</code> or <code>remove()</code>\r
- * the data can be distributed using two different methods:<br>\r
- * <code>replicate(boolean)</code> and <code>replicate(Object, boolean)</code><br>\r
- * These two methods are very important two understand. The map can work with two set of value objects:<br>\r
- * 1. Serializable - the entire object gets serialized each time it is replicated<br>\r
- * 2. ReplicatedMapEntry - this interface allows for a isDirty() flag and to replicate diffs if desired.<br>\r
- * Implementing the <code>ReplicatedMapEntry</code> interface allows you to decide what objects \r
- * get replicated and how much data gets replicated each time.<br>\r
- * If you implement a smart AOP mechanism to detect changes in underlying objects, you can replicate\r
- * only those changes by implementing the ReplicatedMapEntry interface, and return true when isDiffable()\r
- * is invoked.<br><br>\r
- * \r
- * This map implementation doesn't have a background thread running to replicate changes.\r
- * If you do have changes without invoking put/remove then you need to invoke one of the following methods:\r
- * <ul>\r
- * <li><code>replicate(Object,boolean)</code> - replicates only the object that belongs to the key</li>\r
- * <li><code>replicate(boolean)</code> - Scans the entire map for changes and replicates data</li>\r
- * </ul>\r
- * the <code>boolean</code> value in the <code>replicate</code> method used to decide \r
- * whether to only replicate objects that implement the <code>ReplicatedMapEntry</code> interface\r
- * or to replicate all objects. If an object doesn't implement the <code>ReplicatedMapEntry</code> interface\r
- * each time the object gets replicated the entire object gets serialized, hence a call to <code>replicate(true)</code>\r
- * will replicate all objects in this map that are using this node as primary.\r
- * \r
- * <br><br><b>REMBER TO CALL <code>breakdown()</code> or <code>finalize()</code> when you are done with the map to \r
- * avoid memory leaks.<br><br>\r
- * @todo implement periodic sync/transfer thread\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class LazyReplicatedMap extends AbstractReplicatedMap \r
- implements RpcCallback, ChannelListener, MembershipListener {\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(LazyReplicatedMap.class);\r
- \r
- \r
- \r
-//------------------------------------------------------------------------------ \r
-// CONSTRUCTORS / DESTRUCTORS\r
-//------------------------------------------------------------------------------ \r
- /**\r
- * Creates a new map\r
- * @param channel The channel to use for communication\r
- * @param timeout long - timeout for RPC messags\r
- * @param mapContextName String - unique name for this map, to allow multiple maps per channel\r
- * @param initialCapacity int - the size of this map, see HashMap\r
- * @param loadFactor float - load factor, see HashMap\r
- */\r
- public LazyReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, int initialCapacity, float loadFactor, ClassLoader[] cls) {\r
- super(owner,channel,timeout,mapContextName,initialCapacity,loadFactor, Channel.SEND_OPTIONS_DEFAULT,cls);\r
- }\r
-\r
- /**\r
- * Creates a new map\r
- * @param channel The channel to use for communication\r
- * @param timeout long - timeout for RPC messags\r
- * @param mapContextName String - unique name for this map, to allow multiple maps per channel\r
- * @param initialCapacity int - the size of this map, see HashMap\r
- */\r
- public LazyReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, int initialCapacity, ClassLoader[] cls) {\r
- super(owner, channel,timeout,mapContextName,initialCapacity, LazyReplicatedMap.DEFAULT_LOAD_FACTOR, Channel.SEND_OPTIONS_DEFAULT, cls);\r
- }\r
-\r
- /**\r
- * Creates a new map\r
- * @param channel The channel to use for communication\r
- * @param timeout long - timeout for RPC messags\r
- * @param mapContextName String - unique name for this map, to allow multiple maps per channel\r
- */\r
- public LazyReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, ClassLoader[] cls) {\r
- super(owner, channel,timeout,mapContextName, LazyReplicatedMap.DEFAULT_INITIAL_CAPACITY,LazyReplicatedMap.DEFAULT_LOAD_FACTOR,Channel.SEND_OPTIONS_DEFAULT, cls);\r
- }\r
-\r
-\r
-\r
-\r
- \r
-//------------------------------------------------------------------------------ \r
-// METHODS TO OVERRIDE \r
-//------------------------------------------------------------------------------\r
- /**\r
- * publish info about a map pair (key/value) to other nodes in the cluster\r
- * @param key Object\r
- * @param value Object\r
- * @return Member - the backup node\r
- * @throws ChannelException\r
- */\r
- protected Member[] publishEntryInfo(Object key, Object value) throws ChannelException {\r
- if (! (key instanceof Serializable && value instanceof Serializable) ) return new Member[0];\r
- Member[] members = getMapMembers();\r
- int firstIdx = getNextBackupIndex();\r
- int nextIdx = firstIdx;\r
- Member[] backup = new Member[0];\r
- \r
- //there are no backups\r
- if ( members.length == 0 || firstIdx == -1 ) return backup;\r
- \r
- boolean success = false;\r
- do {\r
- //select a backup node\r
- Member next = members[firstIdx];\r
- \r
- //increment for the next round of back up selection\r
- nextIdx = firstIdx + 1;\r
- if ( nextIdx >= members.length ) nextIdx = 0;\r
- \r
- if (next == null) {\r
- continue;\r
- }\r
- MapMessage msg = null;\r
- try {\r
- backup = wrap(next);\r
- //publish the backup data to one node\r
- msg = new MapMessage(getMapContextName(), MapMessage.MSG_BACKUP, false,\r
- (Serializable) key, (Serializable) value, null, backup);\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Publishing backup data:"+msg+" to: "+next.getName());\r
- UniqueId id = getChannel().send(backup, msg, getChannelSendOptions());\r
- if ( log.isTraceEnabled() )\r
- log.trace("Data published:"+msg+" msg Id:"+id);\r
- //we published out to a backup, mark the test success\r
- success = true;\r
- }catch ( ChannelException x ) {\r
- log.error("Unable to replicate backup key:"+key+" to backup:"+next+". Reason:"+x.getMessage(),x);\r
- }\r
- try {\r
- //publish the data out to all nodes\r
- Member[] proxies = excludeFromSet(backup, getMapMembers());\r
- if (success && proxies.length > 0 ) {\r
- msg = new MapMessage(getMapContextName(), MapMessage.MSG_PROXY, false,\r
- (Serializable) key, null, null, backup);\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Publishing proxy data:"+msg+" to: "+Arrays.toNameString(proxies));\r
- getChannel().send(proxies, msg, getChannelSendOptions());\r
- }\r
- }catch ( ChannelException x ) {\r
- //log the error, but proceed, this should only happen if a node went down,\r
- //and if the node went down, then it can't receive the message, the others\r
- //should still get it.\r
- log.error("Unable to replicate proxy key:"+key+" to backup:"+next+". Reason:"+x.getMessage(),x);\r
- }\r
- } while ( !success && (firstIdx!=nextIdx));\r
- return backup;\r
- }\r
- \r
- \r
-\r
-\r
-\r
+/*
+ * 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.apache.catalina.tribes.tipis;
+
+import java.io.Serializable;
+
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelListener;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipListener;
+import org.apache.catalina.tribes.group.RpcCallback;
+import org.apache.catalina.tribes.util.Arrays;
+import org.apache.catalina.tribes.UniqueId;
+
+/**
+ * A smart implementation of a stateful replicated map. uses primary/secondary backup strategy.
+ * One node is always the primary and one node is always the backup.
+ * This map is synchronized across a cluster, and only has one backup member.<br/>
+ * A perfect usage for this map would be a session map for a session manager in a clustered environment.<br/>
+ * The only way to modify this list is to use the <code>put, putAll, remove</code> methods.
+ * entrySet, entrySetFull, keySet, keySetFull, returns all non modifiable sets.<br><br>
+ * If objects (values) in the map change without invoking <code>put()</code> or <code>remove()</code>
+ * the data can be distributed using two different methods:<br>
+ * <code>replicate(boolean)</code> and <code>replicate(Object, boolean)</code><br>
+ * These two methods are very important two understand. The map can work with two set of value objects:<br>
+ * 1. Serializable - the entire object gets serialized each time it is replicated<br>
+ * 2. ReplicatedMapEntry - this interface allows for a isDirty() flag and to replicate diffs if desired.<br>
+ * Implementing the <code>ReplicatedMapEntry</code> interface allows you to decide what objects
+ * get replicated and how much data gets replicated each time.<br>
+ * If you implement a smart AOP mechanism to detect changes in underlying objects, you can replicate
+ * only those changes by implementing the ReplicatedMapEntry interface, and return true when isDiffable()
+ * is invoked.<br><br>
+ *
+ * This map implementation doesn't have a background thread running to replicate changes.
+ * If you do have changes without invoking put/remove then you need to invoke one of the following methods:
+ * <ul>
+ * <li><code>replicate(Object,boolean)</code> - replicates only the object that belongs to the key</li>
+ * <li><code>replicate(boolean)</code> - Scans the entire map for changes and replicates data</li>
+ * </ul>
+ * the <code>boolean</code> value in the <code>replicate</code> method used to decide
+ * whether to only replicate objects that implement the <code>ReplicatedMapEntry</code> interface
+ * or to replicate all objects. If an object doesn't implement the <code>ReplicatedMapEntry</code> interface
+ * each time the object gets replicated the entire object gets serialized, hence a call to <code>replicate(true)</code>
+ * will replicate all objects in this map that are using this node as primary.
+ *
+ * <br><br><b>REMBER TO CALL <code>breakdown()</code> or <code>finalize()</code> when you are done with the map to
+ * avoid memory leaks.<br><br>
+ * @todo implement periodic sync/transfer thread
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class LazyReplicatedMap extends AbstractReplicatedMap
+ implements RpcCallback, ChannelListener, MembershipListener {
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(LazyReplicatedMap.class);
+
+
+
+//------------------------------------------------------------------------------
+// CONSTRUCTORS / DESTRUCTORS
+//------------------------------------------------------------------------------
+ /**
+ * Creates a new map
+ * @param channel The channel to use for communication
+ * @param timeout long - timeout for RPC messags
+ * @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param initialCapacity int - the size of this map, see HashMap
+ * @param loadFactor float - load factor, see HashMap
+ */
+ public LazyReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, int initialCapacity, float loadFactor, ClassLoader[] cls) {
+ super(owner,channel,timeout,mapContextName,initialCapacity,loadFactor, Channel.SEND_OPTIONS_DEFAULT,cls);
+ }
+
+ /**
+ * Creates a new map
+ * @param channel The channel to use for communication
+ * @param timeout long - timeout for RPC messags
+ * @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param initialCapacity int - the size of this map, see HashMap
+ */
+ public LazyReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, int initialCapacity, ClassLoader[] cls) {
+ super(owner, channel,timeout,mapContextName,initialCapacity, LazyReplicatedMap.DEFAULT_LOAD_FACTOR, Channel.SEND_OPTIONS_DEFAULT, cls);
+ }
+
+ /**
+ * Creates a new map
+ * @param channel The channel to use for communication
+ * @param timeout long - timeout for RPC messags
+ * @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ */
+ public LazyReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, ClassLoader[] cls) {
+ super(owner, channel,timeout,mapContextName, LazyReplicatedMap.DEFAULT_INITIAL_CAPACITY,LazyReplicatedMap.DEFAULT_LOAD_FACTOR,Channel.SEND_OPTIONS_DEFAULT, cls);
+ }
+
+
+
+
+
+//------------------------------------------------------------------------------
+// METHODS TO OVERRIDE
+//------------------------------------------------------------------------------
+ /**
+ * publish info about a map pair (key/value) to other nodes in the cluster
+ * @param key Object
+ * @param value Object
+ * @return Member - the backup node
+ * @throws ChannelException
+ */
+ protected Member[] publishEntryInfo(Object key, Object value) throws ChannelException {
+ if (! (key instanceof Serializable && value instanceof Serializable) ) return new Member[0];
+ Member[] members = getMapMembers();
+ int firstIdx = getNextBackupIndex();
+ int nextIdx = firstIdx;
+ Member[] backup = new Member[0];
+
+ //there are no backups
+ if ( members.length == 0 || firstIdx == -1 ) return backup;
+
+ boolean success = false;
+ do {
+ //select a backup node
+ Member next = members[firstIdx];
+
+ //increment for the next round of back up selection
+ nextIdx = firstIdx + 1;
+ if ( nextIdx >= members.length ) nextIdx = 0;
+
+ if (next == null) {
+ continue;
+ }
+ MapMessage msg = null;
+ try {
+ backup = wrap(next);
+ //publish the backup data to one node
+ msg = new MapMessage(getMapContextName(), MapMessage.MSG_BACKUP, false,
+ (Serializable) key, (Serializable) value, null, backup);
+ if ( log.isTraceEnabled() )
+ log.trace("Publishing backup data:"+msg+" to: "+next.getName());
+ UniqueId id = getChannel().send(backup, msg, getChannelSendOptions());
+ if ( log.isTraceEnabled() )
+ log.trace("Data published:"+msg+" msg Id:"+id);
+ //we published out to a backup, mark the test success
+ success = true;
+ }catch ( ChannelException x ) {
+ log.error("Unable to replicate backup key:"+key+" to backup:"+next+". Reason:"+x.getMessage(),x);
+ }
+ try {
+ //publish the data out to all nodes
+ Member[] proxies = excludeFromSet(backup, getMapMembers());
+ if (success && proxies.length > 0 ) {
+ msg = new MapMessage(getMapContextName(), MapMessage.MSG_PROXY, false,
+ (Serializable) key, null, null, backup);
+ if ( log.isTraceEnabled() )
+ log.trace("Publishing proxy data:"+msg+" to: "+Arrays.toNameString(proxies));
+ getChannel().send(proxies, msg, getChannelSendOptions());
+ }
+ }catch ( ChannelException x ) {
+ //log the error, but proceed, this should only happen if a node went down,
+ //and if the node went down, then it can't receive the message, the others
+ //should still get it.
+ log.error("Unable to replicate proxy key:"+key+" to backup:"+next+". Reason:"+x.getMessage(),x);
+ }
+ } while ( !success && (firstIdx!=nextIdx));
+ return backup;
+ }
+
+
+
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.tipis;\r
-\r
-import java.io.Serializable;\r
-\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelListener;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.MembershipListener;\r
-import org.apache.catalina.tribes.group.RpcCallback;\r
-\r
-/**\r
- * All-to-all replication for a hash map implementation. Each node in the cluster will carry an identical \r
- * copy of the map.<br><br>\r
- * This map implementation doesn't have a background thread running to replicate changes.\r
- * If you do have changes without invoking put/remove then you need to invoke one of the following methods:\r
- * <ul>\r
- * <li><code>replicate(Object,boolean)</code> - replicates only the object that belongs to the key</li>\r
- * <li><code>replicate(boolean)</code> - Scans the entire map for changes and replicates data</li>\r
- * </ul>\r
- * the <code>boolean</code> value in the <code>replicate</code> method used to decide\r
- * whether to only replicate objects that implement the <code>ReplicatedMapEntry</code> interface\r
- * or to replicate all objects. If an object doesn't implement the <code>ReplicatedMapEntry</code> interface\r
- * each time the object gets replicated the entire object gets serialized, hence a call to <code>replicate(true)</code>\r
- * will replicate all objects in this map that are using this node as primary.\r
- *\r
- * <br><br><b>REMBER TO CALL <code>breakdown()</code> or <code>finalize()</code> when you are done with the map to\r
- * avoid memory leaks.<br><br>\r
- * @todo implement periodic sync/transfer thread\r
- * @author Filip Hanik\r
- * @version 1.0\r
- * \r
- * @todo memberDisappeared, should do nothing except change map membership\r
- * by default it relocates the primary objects\r
- */\r
-public class ReplicatedMap extends AbstractReplicatedMap implements RpcCallback, ChannelListener, MembershipListener {\r
-\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ReplicatedMap.class);\r
-\r
-//------------------------------------------------------------------------------\r
-// CONSTRUCTORS / DESTRUCTORS\r
-//------------------------------------------------------------------------------\r
- /**\r
- * Creates a new map\r
- * @param channel The channel to use for communication\r
- * @param timeout long - timeout for RPC messags\r
- * @param mapContextName String - unique name for this map, to allow multiple maps per channel\r
- * @param initialCapacity int - the size of this map, see HashMap\r
- * @param loadFactor float - load factor, see HashMap\r
- */\r
- public ReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, int initialCapacity,float loadFactor, ClassLoader[] cls) {\r
- super(owner,channel, timeout, mapContextName, initialCapacity, loadFactor, Channel.SEND_OPTIONS_DEFAULT, cls);\r
- }\r
-\r
- /**\r
- * Creates a new map\r
- * @param channel The channel to use for communication\r
- * @param timeout long - timeout for RPC messags\r
- * @param mapContextName String - unique name for this map, to allow multiple maps per channel\r
- * @param initialCapacity int - the size of this map, see HashMap\r
- */\r
- public ReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, int initialCapacity, ClassLoader[] cls) {\r
- super(owner,channel, timeout, mapContextName, initialCapacity, AbstractReplicatedMap.DEFAULT_LOAD_FACTOR,Channel.SEND_OPTIONS_DEFAULT, cls);\r
- }\r
-\r
- /**\r
- * Creates a new map\r
- * @param channel The channel to use for communication\r
- * @param timeout long - timeout for RPC messags\r
- * @param mapContextName String - unique name for this map, to allow multiple maps per channel\r
- */\r
- public ReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, ClassLoader[] cls) {\r
- super(owner, channel, timeout, mapContextName,AbstractReplicatedMap.DEFAULT_INITIAL_CAPACITY, AbstractReplicatedMap.DEFAULT_LOAD_FACTOR, Channel.SEND_OPTIONS_DEFAULT, cls);\r
- }\r
-\r
-//------------------------------------------------------------------------------\r
-// METHODS TO OVERRIDE\r
-//------------------------------------------------------------------------------\r
- /**\r
- * publish info about a map pair (key/value) to other nodes in the cluster\r
- * @param key Object\r
- * @param value Object\r
- * @return Member - the backup node\r
- * @throws ChannelException\r
- */\r
- protected Member[] publishEntryInfo(Object key, Object value) throws ChannelException {\r
- if (! (key instanceof Serializable && value instanceof Serializable) ) return new Member[0];\r
- //select a backup node\r
- Member[] backup = getMapMembers();\r
-\r
- if (backup == null || backup.length == 0) return null;\r
-\r
- //publish the data out to all nodes\r
- MapMessage msg = new MapMessage(getMapContextName(), MapMessage.MSG_BACKUP, false,\r
- (Serializable) key, null, null, backup);\r
-\r
- getChannel().send(getMapMembers(), msg, getChannelSendOptions());\r
-\r
- return backup;\r
- }\r
-\r
+/*
+ * 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.apache.catalina.tribes.tipis;
+
+import java.io.Serializable;
+
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelListener;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipListener;
+import org.apache.catalina.tribes.group.RpcCallback;
+
+/**
+ * All-to-all replication for a hash map implementation. Each node in the cluster will carry an identical
+ * copy of the map.<br><br>
+ * This map implementation doesn't have a background thread running to replicate changes.
+ * If you do have changes without invoking put/remove then you need to invoke one of the following methods:
+ * <ul>
+ * <li><code>replicate(Object,boolean)</code> - replicates only the object that belongs to the key</li>
+ * <li><code>replicate(boolean)</code> - Scans the entire map for changes and replicates data</li>
+ * </ul>
+ * the <code>boolean</code> value in the <code>replicate</code> method used to decide
+ * whether to only replicate objects that implement the <code>ReplicatedMapEntry</code> interface
+ * or to replicate all objects. If an object doesn't implement the <code>ReplicatedMapEntry</code> interface
+ * each time the object gets replicated the entire object gets serialized, hence a call to <code>replicate(true)</code>
+ * will replicate all objects in this map that are using this node as primary.
+ *
+ * <br><br><b>REMBER TO CALL <code>breakdown()</code> or <code>finalize()</code> when you are done with the map to
+ * avoid memory leaks.<br><br>
+ * @todo implement periodic sync/transfer thread
+ * @author Filip Hanik
+ * @version 1.0
+ *
+ * @todo memberDisappeared, should do nothing except change map membership
+ * by default it relocates the primary objects
+ */
+public class ReplicatedMap extends AbstractReplicatedMap implements RpcCallback, ChannelListener, MembershipListener {
+
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ReplicatedMap.class);
+
+//------------------------------------------------------------------------------
+// CONSTRUCTORS / DESTRUCTORS
+//------------------------------------------------------------------------------
+ /**
+ * Creates a new map
+ * @param channel The channel to use for communication
+ * @param timeout long - timeout for RPC messags
+ * @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param initialCapacity int - the size of this map, see HashMap
+ * @param loadFactor float - load factor, see HashMap
+ */
+ public ReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, int initialCapacity,float loadFactor, ClassLoader[] cls) {
+ super(owner,channel, timeout, mapContextName, initialCapacity, loadFactor, Channel.SEND_OPTIONS_DEFAULT, cls);
+ }
+
+ /**
+ * Creates a new map
+ * @param channel The channel to use for communication
+ * @param timeout long - timeout for RPC messags
+ * @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ * @param initialCapacity int - the size of this map, see HashMap
+ */
+ public ReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, int initialCapacity, ClassLoader[] cls) {
+ super(owner,channel, timeout, mapContextName, initialCapacity, AbstractReplicatedMap.DEFAULT_LOAD_FACTOR,Channel.SEND_OPTIONS_DEFAULT, cls);
+ }
+
+ /**
+ * Creates a new map
+ * @param channel The channel to use for communication
+ * @param timeout long - timeout for RPC messags
+ * @param mapContextName String - unique name for this map, to allow multiple maps per channel
+ */
+ public ReplicatedMap(Object owner, Channel channel, long timeout, String mapContextName, ClassLoader[] cls) {
+ super(owner, channel, timeout, mapContextName,AbstractReplicatedMap.DEFAULT_INITIAL_CAPACITY, AbstractReplicatedMap.DEFAULT_LOAD_FACTOR, Channel.SEND_OPTIONS_DEFAULT, cls);
+ }
+
+//------------------------------------------------------------------------------
+// METHODS TO OVERRIDE
+//------------------------------------------------------------------------------
+ /**
+ * publish info about a map pair (key/value) to other nodes in the cluster
+ * @param key Object
+ * @param value Object
+ * @return Member - the backup node
+ * @throws ChannelException
+ */
+ protected Member[] publishEntryInfo(Object key, Object value) throws ChannelException {
+ if (! (key instanceof Serializable && value instanceof Serializable) ) return new Member[0];
+ //select a backup node
+ Member[] backup = getMapMembers();
+
+ if (backup == null || backup.length == 0) return null;
+
+ //publish the data out to all nodes
+ MapMessage msg = new MapMessage(getMapContextName(), MapMessage.MSG_BACKUP, false,
+ (Serializable) key, null, null, backup);
+
+ getChannel().send(getMapMembers(), msg, getChannelSendOptions());
+
+ return backup;
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.tipis;\r
-\r
-import java.io.IOException;\r
-import java.io.Serializable;\r
-\r
-/**\r
- * \r
- * For smarter replication, an object can implement this interface to replicate diffs<br>\r
- * The replication logic will call the methods in the following order:<br>\r
- * <code>\r
- * 1. if ( entry.isDirty() ) <br>\r
- * try {\r
- * 2. entry.lock();<br>\r
- * 3. byte[] diff = entry.getDiff();<br>\r
- * 4. entry.reset();<br>\r
- * } finally {<br>\r
- * 5. entry.unlock();<br>\r
- * }<br>\r
- * }<br>\r
- * </code>\r
- * <br>\r
- * <br>\r
- * When the data is deserialized the logic is called in the following order<br>\r
- * <code>\r
- * 1. ReplicatedMapEntry entry = (ReplicatedMapEntry)objectIn.readObject();<br>\r
- * 2. if ( isBackup(entry)||isPrimary(entry) ) entry.setOwner(owner); <br>\r
- * </code>\r
- * <br>\r
- * \r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public interface ReplicatedMapEntry extends Serializable {\r
- \r
- /**\r
- * Has the object changed since last replication\r
- * and is not in a locked state\r
- * @return boolean\r
- */\r
- public boolean isDirty();\r
- \r
- /**\r
- * If this returns true, the map will extract the diff using getDiff()\r
- * Otherwise it will serialize the entire object.\r
- * @return boolean\r
- */\r
- public boolean isDiffable();\r
- \r
- /**\r
- * Returns a diff and sets the dirty map to false\r
- * @return byte[]\r
- * @throws IOException\r
- */\r
- public byte[] getDiff() throws IOException;\r
- \r
- \r
- /**\r
- * Applies a diff to an existing object.\r
- * @param diff byte[]\r
- * @param offset int\r
- * @param length int\r
- * @throws IOException\r
- */\r
- public void applyDiff(byte[] diff, int offset, int length) throws IOException, ClassNotFoundException;\r
- \r
- /**\r
- * Resets the current diff state and resets the dirty flag\r
- */\r
- public void resetDiff();\r
- \r
- /**\r
- * Lock during serialization\r
- */\r
- public void lock();\r
- \r
- /**\r
- * Unlock after serialization\r
- */\r
- public void unlock();\r
- \r
- /**\r
- * This method is called after the object has been \r
- * created on a remote map. On this method,\r
- * the object can initialize itself for any data that wasn't \r
- * \r
- * @param owner Object\r
- */\r
- public void setOwner(Object owner);\r
- \r
- /**\r
- * For accuracy checking, a serialized attribute can contain a version number\r
- * This number increases as modifications are made to the data.\r
- * The replicated map can use this to ensure accuracy on a periodic basis\r
- * @return long - the version number or -1 if the data is not versioned\r
- */\r
- public long getVersion();\r
- \r
- /**\r
- * Forces a certain version to a replicated map entry<br>\r
- * @param version long\r
- */\r
- public void setVersion(long version);\r
- \r
- \r
- \r
- \r
+/*
+ * 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.apache.catalina.tribes.tipis;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ *
+ * For smarter replication, an object can implement this interface to replicate diffs<br>
+ * The replication logic will call the methods in the following order:<br>
+ * <code>
+ * 1. if ( entry.isDirty() ) <br>
+ * try {
+ * 2. entry.lock();<br>
+ * 3. byte[] diff = entry.getDiff();<br>
+ * 4. entry.reset();<br>
+ * } finally {<br>
+ * 5. entry.unlock();<br>
+ * }<br>
+ * }<br>
+ * </code>
+ * <br>
+ * <br>
+ * When the data is deserialized the logic is called in the following order<br>
+ * <code>
+ * 1. ReplicatedMapEntry entry = (ReplicatedMapEntry)objectIn.readObject();<br>
+ * 2. if ( isBackup(entry)||isPrimary(entry) ) entry.setOwner(owner); <br>
+ * </code>
+ * <br>
+ *
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public interface ReplicatedMapEntry extends Serializable {
+
+ /**
+ * Has the object changed since last replication
+ * and is not in a locked state
+ * @return boolean
+ */
+ public boolean isDirty();
+
+ /**
+ * If this returns true, the map will extract the diff using getDiff()
+ * Otherwise it will serialize the entire object.
+ * @return boolean
+ */
+ public boolean isDiffable();
+
+ /**
+ * Returns a diff and sets the dirty map to false
+ * @return byte[]
+ * @throws IOException
+ */
+ public byte[] getDiff() throws IOException;
+
+
+ /**
+ * Applies a diff to an existing object.
+ * @param diff byte[]
+ * @param offset int
+ * @param length int
+ * @throws IOException
+ */
+ public void applyDiff(byte[] diff, int offset, int length) throws IOException, ClassNotFoundException;
+
+ /**
+ * Resets the current diff state and resets the dirty flag
+ */
+ public void resetDiff();
+
+ /**
+ * Lock during serialization
+ */
+ public void lock();
+
+ /**
+ * Unlock after serialization
+ */
+ public void unlock();
+
+ /**
+ * This method is called after the object has been
+ * created on a remote map. On this method,
+ * the object can initialize itself for any data that wasn't
+ *
+ * @param owner Object
+ */
+ public void setOwner(Object owner);
+
+ /**
+ * For accuracy checking, a serialized attribute can contain a version number
+ * This number increases as modifications are made to the data.
+ * The replicated map can use this to ensure accuracy on a periodic basis
+ * @return long - the version number or -1 if the data is not versioned
+ */
+ public long getVersion();
+
+ /**
+ * Forces a certain version to a replicated map entry<br>
+ * @param version long
+ */
+ public void setVersion(long version);
+
+
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.tipis;\r
-\r
-import java.io.IOException;\r
-\r
-/**\r
- * Example usage:\r
- * <code><pre>\r
- * byte[] data = new byte[1024];\r
- * Streamable st = ....;\r
- * while ( !st.eof() ) {\r
- * int length = st.read(data,0,data.length);\r
- * String s = new String(data,0,length);\r
- * System.out.println(s);\r
- * }\r
- * </pre></code>\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public interface Streamable {\r
- \r
- /**\r
- * returns true if the stream has reached its end\r
- * @return boolean\r
- */\r
- public boolean eof();\r
- \r
- /**\r
- * write data into the byte array starting at offset, maximum bytes read are (data.length-offset)\r
- * @param data byte[] - the array to read data into\r
- * @param offset int - start position for writing data\r
- * @return int - the number of bytes written into the data buffer\r
- */\r
- public int write(byte[] data, int offset, int length) throws IOException;\r
- \r
- /**\r
- * read data into the byte array starting at offset\r
- * @param data byte[] - the array to read data into\r
- * @param offset int - start position for writing data\r
- * @param length - the desired read length\r
- * @return int - the number of bytes read from the data buffer\r
- */\r
- public int read(byte[] data, int offset, int length) throws IOException;\r
-\r
- \r
+/*
+ * 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.apache.catalina.tribes.tipis;
+
+import java.io.IOException;
+
+/**
+ * Example usage:
+ * <code><pre>
+ * byte[] data = new byte[1024];
+ * Streamable st = ....;
+ * while ( !st.eof() ) {
+ * int length = st.read(data,0,data.length);
+ * String s = new String(data,0,length);
+ * System.out.println(s);
+ * }
+ * </pre></code>
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public interface Streamable {
+
+ /**
+ * returns true if the stream has reached its end
+ * @return boolean
+ */
+ public boolean eof();
+
+ /**
+ * write data into the byte array starting at offset, maximum bytes read are (data.length-offset)
+ * @param data byte[] - the array to read data into
+ * @param offset int - start position for writing data
+ * @return int - the number of bytes written into the data buffer
+ */
+ public int write(byte[] data, int offset, int length) throws IOException;
+
+ /**
+ * read data into the byte array starting at offset
+ * @param data byte[] - the array to read data into
+ * @param offset int - start position for writing data
+ * @param length - the desired read length
+ * @return int - the number of bytes read from the data buffer
+ */
+ public int read(byte[] data, int offset, int length) throws IOException;
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.tribes.transport;\r
-\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-\r
-/**\r
- * Manifest constants for the <code>org.apache.catalina.tribes.transport</code>\r
- * package.\r
- * @author Filip Hanik\r
- * @author Peter Rossbach\r
- * @version $Revision: 303753 $ $Date: 2005-03-14 15:24:30 -0600 (Mon, 14 Mar 2005) $\r
- */\r
-\r
-public class Constants {\r
-\r
- public static final String Package = "org.apache.catalina.tribes.transport";\r
- \r
- /*\r
- * Do not change any of these values!\r
- */\r
- public static final byte[] ACK_DATA = new byte[] {6, 2, 3};\r
- public static final byte[] FAIL_ACK_DATA = new byte[] {11, 0, 5};\r
- public static final byte[] ACK_COMMAND = XByteBuffer.createDataPackage(ACK_DATA);\r
- public static final byte[] FAIL_ACK_COMMAND = XByteBuffer.createDataPackage(FAIL_ACK_DATA);\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport;
+
+import org.apache.catalina.tribes.io.XByteBuffer;
+
+/**
+ * Manifest constants for the <code>org.apache.catalina.tribes.transport</code>
+ * package.
+ * @author Filip Hanik
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+
+public class Constants {
+
+ public static final String Package = "org.apache.catalina.tribes.transport";
+
+ /*
+ * Do not change any of these values!
+ */
+ public static final byte[] ACK_DATA = new byte[] {6, 2, 3};
+ public static final byte[] FAIL_ACK_DATA = new byte[] {11, 0, 5};
+ public static final byte[] ACK_COMMAND = XByteBuffer.createDataPackage(ACK_DATA);
+ public static final byte[] FAIL_ACK_COMMAND = XByteBuffer.createDataPackage(FAIL_ACK_DATA);
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.transport;\r
-\r
-import java.io.IOException;\r
-\r
-/**\r
- * <p>Title: </p>\r
- *\r
- * <p>Description: </p>\r
- *\r
- * <p>Copyright: Copyright (c) 2005</p>\r
- *\r
- * <p>Company: </p>\r
- *\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-public interface DataSender {\r
- public void connect() throws IOException;\r
- public void disconnect();\r
- public boolean isConnected();\r
- public void setRxBufSize(int size);\r
- public void setTxBufSize(int size);\r
- public boolean keepalive();\r
- public void setTimeout(long timeout);\r
- public void setKeepAliveCount(int maxRequests);\r
- public void setKeepAliveTime(long keepAliveTimeInMs);\r
- public int getRequestCount();\r
- public long getConnectTime();\r
- \r
- \r
+/*
+ * 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.apache.catalina.tribes.transport;
+
+import java.io.IOException;
+
+/**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2005</p>
+ *
+ * <p>Company: </p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public interface DataSender {
+ public void connect() throws IOException;
+ public void disconnect();
+ public boolean isConnected();
+ public void setRxBufSize(int size);
+ public void setTxBufSize(int size);
+ public boolean keepalive();
+ public void setTimeout(long timeout);
+ public void setKeepAliveCount(int maxRequests);
+ public void setKeepAliveTime(long keepAliveTimeInMs);
+ public int getRequestCount();
+ public long getConnectTime();
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.Member;\r
-\r
-/**\r
- * @author Filip Hanik\r
- * @version $Revision: 303993 $ $Date: 2005-07-16 16:05:54 -0500 (Sat, 16 Jul 2005) $\r
- * @since 5.5.16\r
- */\r
-\r
-public interface MultiPointSender extends DataSender\r
-{\r
- public void sendMessage(Member[] destination, ChannelMessage data) throws ChannelException;\r
- public void setRxBufSize(int size);\r
- public void setTxBufSize(int size);\r
- public void setMaxRetryAttempts(int attempts);\r
- public void setDirectBuffer(boolean directBuf);\r
- public void memberAdded(Member member);\r
- public void memberDisappeared(Member member);\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.Member;
+
+/**
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ * @since 5.5.16
+ */
+
+public interface MultiPointSender extends DataSender
+{
+ public void sendMessage(Member[] destination, ChannelMessage data) throws ChannelException;
+ public void setRxBufSize(int size);
+ public void setTxBufSize(int size);
+ public void setMaxRetryAttempts(int attempts);
+ public void setDirectBuffer(boolean directBuf);
+ public void memberAdded(Member member);
+ public void memberDisappeared(Member member);
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.transport;\r
-\r
-import java.io.IOException;\r
-import java.util.List;\r
-\r
-/**\r
- * <p>Title: </p>\r
- *\r
- * <p>Description: </p>\r
- *\r
- * <p>Copyright: Copyright (c) 2005</p>\r
- *\r
- * <p>Company: </p>\r
- *\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-public abstract class PooledSender extends AbstractSender implements MultiPointSender {\r
- \r
- private SenderQueue queue = null;\r
- private int poolSize = 25;\r
- public PooledSender() {\r
- queue = new SenderQueue(this,poolSize);\r
- }\r
- \r
- public abstract DataSender getNewDataSender();\r
- \r
- public DataSender getSender() {\r
- return queue.getSender(getTimeout());\r
- }\r
- \r
- public void returnSender(DataSender sender) {\r
- sender.keepalive();\r
- queue.returnSender(sender);\r
- }\r
- \r
- public synchronized void connect() throws IOException {\r
- //do nothing, happens in the socket sender itself\r
- queue.open();\r
- setConnected(true);\r
- }\r
- \r
- public synchronized void disconnect() {\r
- queue.close();\r
- setConnected(false);\r
- }\r
- \r
- \r
- public int getInPoolSize() {\r
- return queue.getInPoolSize();\r
- }\r
-\r
- public int getInUsePoolSize() {\r
- return queue.getInUsePoolSize();\r
- }\r
-\r
-\r
- public void setPoolSize(int poolSize) {\r
- this.poolSize = poolSize;\r
- queue.setLimit(poolSize);\r
- }\r
-\r
- public int getPoolSize() {\r
- return poolSize;\r
- }\r
-\r
- public boolean keepalive() {\r
- //do nothing, the pool checks on every return\r
- return false;\r
- }\r
-\r
- \r
-\r
- // ----------------------------------------------------- Inner Class\r
-\r
- private class SenderQueue {\r
- private int limit = 25;\r
-\r
- PooledSender parent = null;\r
-\r
- private List notinuse = null;\r
-\r
- private List inuse = null;\r
-\r
- private boolean isOpen = true;\r
-\r
- public SenderQueue(PooledSender parent, int limit) {\r
- this.limit = limit;\r
- this.parent = parent;\r
- notinuse = new java.util.LinkedList();\r
- inuse = new java.util.LinkedList();\r
- }\r
-\r
- /**\r
- * @return Returns the limit.\r
- */\r
- public int getLimit() {\r
- return limit;\r
- }\r
- /**\r
- * @param limit The limit to set.\r
- */\r
- public void setLimit(int limit) {\r
- this.limit = limit;\r
- }\r
- /**\r
- * @return\r
- */\r
- public int getInUsePoolSize() {\r
- return inuse.size();\r
- }\r
-\r
- /**\r
- * @return\r
- */\r
- public int getInPoolSize() {\r
- return notinuse.size();\r
- }\r
-\r
- public synchronized DataSender getSender(long timeout) {\r
- long start = System.currentTimeMillis();\r
- while ( true ) {\r
- if (!isOpen)throw new IllegalStateException("Queue is closed");\r
- DataSender sender = null;\r
- if (notinuse.size() == 0 && inuse.size() < limit) {\r
- sender = parent.getNewDataSender();\r
- } else if (notinuse.size() > 0) {\r
- sender = (DataSender) notinuse.remove(0);\r
- }\r
- if (sender != null) {\r
- inuse.add(sender);\r
- return sender;\r
- }//end if\r
- long delta = System.currentTimeMillis() - start;\r
- if ( delta > timeout && timeout>0) return null;\r
- else {\r
- try {\r
- wait(Math.max(timeout - delta,1));\r
- }catch (InterruptedException x){}\r
- }//end if\r
- }\r
- }\r
-\r
- public synchronized void returnSender(DataSender sender) {\r
- if ( !isOpen) {\r
- sender.disconnect();\r
- return;\r
- }\r
- //to do\r
- inuse.remove(sender);\r
- //just in case the limit has changed\r
- if ( notinuse.size() < this.getLimit() ) notinuse.add(sender);\r
- else try {sender.disconnect(); } catch ( Exception ignore){}\r
- notify();\r
- }\r
-\r
- public synchronized void close() {\r
- isOpen = false;\r
- Object[] unused = notinuse.toArray();\r
- Object[] used = inuse.toArray();\r
- for (int i = 0; i < unused.length; i++) {\r
- DataSender sender = (DataSender) unused[i];\r
- sender.disconnect();\r
- }//for\r
- for (int i = 0; i < used.length; i++) {\r
- DataSender sender = (DataSender) used[i];\r
- sender.disconnect();\r
- }//for\r
- notinuse.clear();\r
- inuse.clear();\r
- notify();\r
- \r
-\r
-\r
- }\r
-\r
- public synchronized void open() {\r
- isOpen = true;\r
- notify();\r
- }\r
- }\r
+/*
+ * 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.apache.catalina.tribes.transport;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2005</p>
+ *
+ * <p>Company: </p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public abstract class PooledSender extends AbstractSender implements MultiPointSender {
+
+ private SenderQueue queue = null;
+ private int poolSize = 25;
+ public PooledSender() {
+ queue = new SenderQueue(this,poolSize);
+ }
+
+ public abstract DataSender getNewDataSender();
+
+ public DataSender getSender() {
+ return queue.getSender(getTimeout());
+ }
+
+ public void returnSender(DataSender sender) {
+ sender.keepalive();
+ queue.returnSender(sender);
+ }
+
+ public synchronized void connect() throws IOException {
+ //do nothing, happens in the socket sender itself
+ queue.open();
+ setConnected(true);
+ }
+
+ public synchronized void disconnect() {
+ queue.close();
+ setConnected(false);
+ }
+
+
+ public int getInPoolSize() {
+ return queue.getInPoolSize();
+ }
+
+ public int getInUsePoolSize() {
+ return queue.getInUsePoolSize();
+ }
+
+
+ public void setPoolSize(int poolSize) {
+ this.poolSize = poolSize;
+ queue.setLimit(poolSize);
+ }
+
+ public int getPoolSize() {
+ return poolSize;
+ }
+
+ public boolean keepalive() {
+ //do nothing, the pool checks on every return
+ return false;
+ }
+
+
+
+ // ----------------------------------------------------- Inner Class
+
+ private class SenderQueue {
+ private int limit = 25;
+
+ PooledSender parent = null;
+
+ private List notinuse = null;
+
+ private List inuse = null;
+
+ private boolean isOpen = true;
+
+ public SenderQueue(PooledSender parent, int limit) {
+ this.limit = limit;
+ this.parent = parent;
+ notinuse = new java.util.LinkedList();
+ inuse = new java.util.LinkedList();
+ }
+
+ /**
+ * @return Returns the limit.
+ */
+ public int getLimit() {
+ return limit;
+ }
+ /**
+ * @param limit The limit to set.
+ */
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+ /**
+ * @return
+ */
+ public int getInUsePoolSize() {
+ return inuse.size();
+ }
+
+ /**
+ * @return
+ */
+ public int getInPoolSize() {
+ return notinuse.size();
+ }
+
+ public synchronized DataSender getSender(long timeout) {
+ long start = System.currentTimeMillis();
+ while ( true ) {
+ if (!isOpen)throw new IllegalStateException("Queue is closed");
+ DataSender sender = null;
+ if (notinuse.size() == 0 && inuse.size() < limit) {
+ sender = parent.getNewDataSender();
+ } else if (notinuse.size() > 0) {
+ sender = (DataSender) notinuse.remove(0);
+ }
+ if (sender != null) {
+ inuse.add(sender);
+ return sender;
+ }//end if
+ long delta = System.currentTimeMillis() - start;
+ if ( delta > timeout && timeout>0) return null;
+ else {
+ try {
+ wait(Math.max(timeout - delta,1));
+ }catch (InterruptedException x){}
+ }//end if
+ }
+ }
+
+ public synchronized void returnSender(DataSender sender) {
+ if ( !isOpen) {
+ sender.disconnect();
+ return;
+ }
+ //to do
+ inuse.remove(sender);
+ //just in case the limit has changed
+ if ( notinuse.size() < this.getLimit() ) notinuse.add(sender);
+ else try {sender.disconnect(); } catch ( Exception ignore){}
+ notify();
+ }
+
+ public synchronized void close() {
+ isOpen = false;
+ Object[] unused = notinuse.toArray();
+ Object[] used = inuse.toArray();
+ for (int i = 0; i < unused.length; i++) {
+ DataSender sender = (DataSender) unused[i];
+ sender.disconnect();
+ }//for
+ for (int i = 0; i < used.length; i++) {
+ DataSender sender = (DataSender) used[i];
+ sender.disconnect();
+ }//for
+ notinuse.clear();
+ inuse.clear();
+ notify();
+
+
+
+ }
+
+ public synchronized void open() {
+ isOpen = true;
+ notify();
+ }
+ }
}
\ No newline at end of file
* type
*
* @author Filip Hanik
- * @version $Revision: 379956 $ $Date: 2006-02-22 16:57:35 -0600 (Wed, 22 Feb 2006) $
+ * @version $Revision$ $Date$
*/
public class ReplicationTransmitter implements ChannelSender {
private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ReplicationTransmitter.class);
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport;\r
-\r
-import org.apache.catalina.tribes.Member;\r
-import java.util.HashMap;\r
-\r
-\r
-/**\r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- * @since 5.5.16\r
- */\r
-\r
-public class SenderState {\r
- \r
- public static final int READY = 0;\r
- public static final int SUSPECT = 1;\r
- public static final int FAILING = 2;\r
- /**\r
- * The descriptive information about this implementation.\r
- */\r
- private static final String info = "SenderState/1.0";\r
- \r
- \r
- protected static HashMap memberStates = new HashMap();\r
- \r
- public static SenderState getSenderState(Member member) {\r
- return getSenderState(member,true);\r
- }\r
-\r
- public static SenderState getSenderState(Member member, boolean create) {\r
- SenderState state = (SenderState)memberStates.get(member);\r
- if ( state == null && create) {\r
- synchronized ( memberStates ) {\r
- state = (SenderState)memberStates.get(member);\r
- if ( state == null ) {\r
- state = new SenderState();\r
- memberStates.put(member,state);\r
- }\r
- }\r
- }\r
- return state;\r
- }\r
- \r
- public static void removeSenderState(Member member) {\r
- synchronized ( memberStates ) {\r
- memberStates.remove(member);\r
- }\r
- }\r
- \r
-\r
- // ----------------------------------------------------- Instance Variables\r
-\r
- private int state = READY;\r
-\r
- // ----------------------------------------------------- Constructor\r
-\r
- \r
- private SenderState() {\r
- this(READY);\r
- }\r
-\r
- private SenderState(int state) {\r
- this.state = state;\r
- }\r
- \r
- /**\r
- * \r
- * @return boolean\r
- */\r
- public boolean isSuspect() {\r
- return (state == SUSPECT) || (state == FAILING);\r
- }\r
-\r
- public void setSuspect() {\r
- state = SUSPECT;\r
- }\r
- \r
- public boolean isReady() {\r
- return state == READY;\r
- }\r
- \r
- public void setReady() {\r
- state = READY;\r
- }\r
- \r
- public boolean isFailing() {\r
- return state == FAILING;\r
- }\r
- \r
- public void setFailing() {\r
- state = FAILING;\r
- }\r
- \r
-\r
- // ----------------------------------------------------- Public Properties\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport;
+
+import org.apache.catalina.tribes.Member;
+import java.util.HashMap;
+
+
+/**
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ * @since 5.5.16
+ */
+
+public class SenderState {
+
+ public static final int READY = 0;
+ public static final int SUSPECT = 1;
+ public static final int FAILING = 2;
+ /**
+ * The descriptive information about this implementation.
+ */
+ private static final String info = "SenderState/1.0";
+
+
+ protected static HashMap memberStates = new HashMap();
+
+ public static SenderState getSenderState(Member member) {
+ return getSenderState(member,true);
+ }
+
+ public static SenderState getSenderState(Member member, boolean create) {
+ SenderState state = (SenderState)memberStates.get(member);
+ if ( state == null && create) {
+ synchronized ( memberStates ) {
+ state = (SenderState)memberStates.get(member);
+ if ( state == null ) {
+ state = new SenderState();
+ memberStates.put(member,state);
+ }
+ }
+ }
+ return state;
+ }
+
+ public static void removeSenderState(Member member) {
+ synchronized ( memberStates ) {
+ memberStates.remove(member);
+ }
+ }
+
+
+ // ----------------------------------------------------- Instance Variables
+
+ private int state = READY;
+
+ // ----------------------------------------------------- Constructor
+
+
+ private SenderState() {
+ this(READY);
+ }
+
+ private SenderState(int state) {
+ this.state = state;
+ }
+
+ /**
+ *
+ * @return boolean
+ */
+ public boolean isSuspect() {
+ return (state == SUSPECT) || (state == FAILING);
+ }
+
+ public void setSuspect() {
+ state = SUSPECT;
+ }
+
+ public boolean isReady() {
+ return state == READY;
+ }
+
+ public void setReady() {
+ state = READY;
+ }
+
+ public boolean isFailing() {
+ return state == FAILING;
+ }
+
+ public void setFailing() {
+ state = FAILING;
+ }
+
+
+ // ----------------------------------------------------- Public Properties
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport;\r
-import java.util.Iterator;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-\r
-/**\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-\r
-public class ThreadPool\r
-{\r
- /**\r
- * A very simple thread pool class. The pool size is set at\r
- * construction time and remains fixed. Threads are cycled\r
- * through a FIFO idle queue.\r
- */\r
-\r
- List idle = new LinkedList();\r
- List used = new LinkedList();\r
- \r
- Object mutex = new Object();\r
- boolean running = true;\r
- \r
- private static int counter = 1;\r
- private int maxThreads;\r
- private int minThreads;\r
- \r
- private ThreadCreator creator = null;\r
-\r
- private static synchronized int inc() {\r
- return counter++;\r
- }\r
-\r
- \r
- public ThreadPool (int maxThreads, int minThreads, ThreadCreator creator) throws Exception {\r
- // fill up the pool with worker threads\r
- this.maxThreads = maxThreads;\r
- this.minThreads = minThreads;\r
- this.creator = creator;\r
- //for (int i = 0; i < minThreads; i++) {\r
- for (int i = 0; i < maxThreads; i++) { //temporary fix for thread hand off problem\r
- WorkerThread thread = creator.getWorkerThread();\r
- setupThread(thread);\r
- idle.add (thread);\r
- }\r
- }\r
- \r
- protected void setupThread(WorkerThread thread) {\r
- synchronized (thread) {\r
- thread.setPool(this);\r
- thread.setName(thread.getClass().getName() + "[" + inc() + "]");\r
- thread.setDaemon(true);\r
- thread.setPriority(Thread.MAX_PRIORITY);\r
- thread.start();\r
- try {thread.wait(500); }catch ( InterruptedException x ) {}\r
- }\r
- }\r
-\r
- /**\r
- * Find an idle worker thread, if any. Could return null.\r
- */\r
- public WorkerThread getWorker()\r
- {\r
- WorkerThread worker = null;\r
- synchronized (mutex) {\r
- while ( worker == null && running ) {\r
- if (idle.size() > 0) {\r
- try {\r
- worker = (WorkerThread) idle.remove(0);\r
- } catch (java.util.NoSuchElementException x) {\r
- //this means that there are no available workers\r
- worker = null;\r
- }\r
- } else if ( used.size() < this.maxThreads && creator != null) {\r
- worker = creator.getWorkerThread();\r
- setupThread(worker);\r
- } else {\r
- try { mutex.wait(); } catch ( java.lang.InterruptedException x ) {Thread.currentThread().interrupted();}\r
- }\r
- }//while\r
- if ( worker != null ) used.add(worker);\r
- }\r
- return (worker);\r
- }\r
- \r
- public int available() {\r
- return idle.size();\r
- }\r
-\r
- /**\r
- * Called by the worker thread to return itself to the\r
- * idle pool.\r
- */\r
- public void returnWorker (WorkerThread worker) {\r
- if ( running ) {\r
- synchronized (mutex) {\r
- used.remove(worker);\r
- //if ( idle.size() < minThreads && !idle.contains(worker)) idle.add(worker);\r
- if ( idle.size() < maxThreads && !idle.contains(worker)) idle.add(worker); //let max be the upper limit\r
- else {\r
- worker.setDoRun(false);\r
- synchronized (worker){worker.notify();}\r
- }\r
- mutex.notify();\r
- }\r
- }else {\r
- worker.setDoRun(false);\r
- synchronized (worker){worker.notify();}\r
- }\r
- }\r
-\r
- public int getMaxThreads() {\r
- return maxThreads;\r
- }\r
-\r
- public int getMinThreads() {\r
- return minThreads;\r
- }\r
-\r
- public void stop() {\r
- running = false;\r
- synchronized (mutex) {\r
- Iterator i = idle.iterator();\r
- while ( i.hasNext() ) {\r
- WorkerThread worker = (WorkerThread)i.next();\r
- returnWorker(worker);\r
- i.remove();\r
- }\r
- }\r
- }\r
-\r
- public void setMaxThreads(int maxThreads) {\r
- this.maxThreads = maxThreads;\r
- }\r
-\r
- public void setMinThreads(int minThreads) {\r
- this.minThreads = minThreads;\r
- }\r
-\r
- public ThreadCreator getThreadCreator() {\r
- return this.creator;\r
- }\r
- \r
- public static interface ThreadCreator {\r
- public WorkerThread getWorkerThread();\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author not attributable
+ * @version 1.0
+ */
+
+public class ThreadPool
+{
+ /**
+ * A very simple thread pool class. The pool size is set at
+ * construction time and remains fixed. Threads are cycled
+ * through a FIFO idle queue.
+ */
+
+ List idle = new LinkedList();
+ List used = new LinkedList();
+
+ Object mutex = new Object();
+ boolean running = true;
+
+ private static int counter = 1;
+ private int maxThreads;
+ private int minThreads;
+
+ private ThreadCreator creator = null;
+
+ private static synchronized int inc() {
+ return counter++;
+ }
+
+
+ public ThreadPool (int maxThreads, int minThreads, ThreadCreator creator) throws Exception {
+ // fill up the pool with worker threads
+ this.maxThreads = maxThreads;
+ this.minThreads = minThreads;
+ this.creator = creator;
+ //for (int i = 0; i < minThreads; i++) {
+ for (int i = 0; i < maxThreads; i++) { //temporary fix for thread hand off problem
+ WorkerThread thread = creator.getWorkerThread();
+ setupThread(thread);
+ idle.add (thread);
+ }
+ }
+
+ protected void setupThread(WorkerThread thread) {
+ synchronized (thread) {
+ thread.setPool(this);
+ thread.setName(thread.getClass().getName() + "[" + inc() + "]");
+ thread.setDaemon(true);
+ thread.setPriority(Thread.MAX_PRIORITY);
+ thread.start();
+ try {thread.wait(500); }catch ( InterruptedException x ) {}
+ }
+ }
+
+ /**
+ * Find an idle worker thread, if any. Could return null.
+ */
+ public WorkerThread getWorker()
+ {
+ WorkerThread worker = null;
+ synchronized (mutex) {
+ while ( worker == null && running ) {
+ if (idle.size() > 0) {
+ try {
+ worker = (WorkerThread) idle.remove(0);
+ } catch (java.util.NoSuchElementException x) {
+ //this means that there are no available workers
+ worker = null;
+ }
+ } else if ( used.size() < this.maxThreads && creator != null) {
+ worker = creator.getWorkerThread();
+ setupThread(worker);
+ } else {
+ try { mutex.wait(); } catch ( java.lang.InterruptedException x ) {Thread.currentThread().interrupted();}
+ }
+ }//while
+ if ( worker != null ) used.add(worker);
+ }
+ return (worker);
+ }
+
+ public int available() {
+ return idle.size();
+ }
+
+ /**
+ * Called by the worker thread to return itself to the
+ * idle pool.
+ */
+ public void returnWorker (WorkerThread worker) {
+ if ( running ) {
+ synchronized (mutex) {
+ used.remove(worker);
+ //if ( idle.size() < minThreads && !idle.contains(worker)) idle.add(worker);
+ if ( idle.size() < maxThreads && !idle.contains(worker)) idle.add(worker); //let max be the upper limit
+ else {
+ worker.setDoRun(false);
+ synchronized (worker){worker.notify();}
+ }
+ mutex.notify();
+ }
+ }else {
+ worker.setDoRun(false);
+ synchronized (worker){worker.notify();}
+ }
+ }
+
+ public int getMaxThreads() {
+ return maxThreads;
+ }
+
+ public int getMinThreads() {
+ return minThreads;
+ }
+
+ public void stop() {
+ running = false;
+ synchronized (mutex) {
+ Iterator i = idle.iterator();
+ while ( i.hasNext() ) {
+ WorkerThread worker = (WorkerThread)i.next();
+ returnWorker(worker);
+ i.remove();
+ }
+ }
+ }
+
+ public void setMaxThreads(int maxThreads) {
+ this.maxThreads = maxThreads;
+ }
+
+ public void setMinThreads(int minThreads) {
+ this.minThreads = minThreads;
+ }
+
+ public ThreadCreator getThreadCreator() {
+ return this.creator;
+ }
+
+ public static interface ThreadCreator {
+ public WorkerThread getWorkerThread();
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport;\r
-\r
-import org.apache.catalina.tribes.io.ListenCallback;\r
-\r
-\r
-\r
-\r
-/**\r
- * @author Filip Hanik\r
- * @version $Revision: 366253 $ $Date: 2006-01-05 13:30:42 -0600 (Thu, 05 Jan 2006) $\r
- */\r
-public abstract class WorkerThread extends Thread \r
-{\r
- \r
- public static final int OPTION_DIRECT_BUFFER = ReceiverBase.OPTION_DIRECT_BUFFER;\r
- \r
- private ListenCallback callback;\r
- private ThreadPool pool;\r
- private boolean doRun = true;\r
- private int options;\r
- protected boolean useBufferPool = true;\r
-\r
- public WorkerThread(ListenCallback callback) {\r
- this.callback = callback;\r
- }\r
-\r
- public void setPool(ThreadPool pool) {\r
- this.pool = pool;\r
- }\r
-\r
- public void setOptions(int options) {\r
- this.options = options;\r
- }\r
-\r
- public void setCallback(ListenCallback callback) {\r
- this.callback = callback;\r
- }\r
-\r
- public void setDoRun(boolean doRun) {\r
- this.doRun = doRun;\r
- }\r
-\r
- public ThreadPool getPool() {\r
- return pool;\r
- }\r
-\r
- public int getOptions() {\r
- return options;\r
- }\r
-\r
- public ListenCallback getCallback() {\r
- return callback;\r
- }\r
-\r
- public boolean isDoRun() {\r
- return doRun;\r
- }\r
-\r
- public void close()\r
- {\r
- doRun = false;\r
- notify();\r
- }\r
- \r
- public void setUseBufferPool(boolean usebufpool) {\r
- useBufferPool = usebufpool;\r
- }\r
- \r
- public boolean getUseBufferPool() {\r
- return useBufferPool;\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport;
+
+import org.apache.catalina.tribes.io.ListenCallback;
+
+
+
+
+/**
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+public abstract class WorkerThread extends Thread
+{
+
+ public static final int OPTION_DIRECT_BUFFER = ReceiverBase.OPTION_DIRECT_BUFFER;
+
+ private ListenCallback callback;
+ private ThreadPool pool;
+ private boolean doRun = true;
+ private int options;
+ protected boolean useBufferPool = true;
+
+ public WorkerThread(ListenCallback callback) {
+ this.callback = callback;
+ }
+
+ public void setPool(ThreadPool pool) {
+ this.pool = pool;
+ }
+
+ public void setOptions(int options) {
+ this.options = options;
+ }
+
+ public void setCallback(ListenCallback callback) {
+ this.callback = callback;
+ }
+
+ public void setDoRun(boolean doRun) {
+ this.doRun = doRun;
+ }
+
+ public ThreadPool getPool() {
+ return pool;
+ }
+
+ public int getOptions() {
+ return options;
+ }
+
+ public ListenCallback getCallback() {
+ return callback;
+ }
+
+ public boolean isDoRun() {
+ return doRun;
+ }
+
+ public void close()
+ {
+ doRun = false;
+ notify();
+ }
+
+ public void setUseBufferPool(boolean usebufpool) {
+ useBufferPool = usebufpool;
+ }
+
+ public boolean getUseBufferPool() {
+ return useBufferPool;
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport.bio;\r
-import java.io.IOException;\r
-\r
-import org.apache.catalina.tribes.io.ObjectReader;\r
-import org.apache.catalina.tribes.transport.Constants;\r
-import org.apache.catalina.tribes.transport.WorkerThread;\r
-import java.net.Socket;\r
-import java.io.InputStream;\r
-import org.apache.catalina.tribes.transport.ReceiverBase;\r
-import java.io.OutputStream;\r
-import org.apache.catalina.tribes.io.ListenCallback;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.io.ChannelData;\r
-import org.apache.catalina.tribes.io.BufferPool;\r
-\r
-/**\r
- * A worker thread class which can drain channels and echo-back the input. Each\r
- * instance is constructed with a reference to the owning thread pool object.\r
- * When started, the thread loops forever waiting to be awakened to service the\r
- * channel associated with a SelectionKey object. The worker is tasked by\r
- * calling its serviceChannel() method with a SelectionKey object. The\r
- * serviceChannel() method stores the key reference in the thread object then\r
- * calls notify() to wake it up. When the channel has been drained, the worker\r
- * thread returns itself to its parent pool.\r
- * \r
- * @author Filip Hanik\r
- * \r
- * @version $Revision: 378050 $, $Date: 2006-02-15 12:30:02 -0600 (Wed, 15 Feb 2006) $\r
- */\r
-public class BioReplicationThread extends WorkerThread {\r
-\r
-\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( BioReplicationThread.class );\r
- \r
- protected Socket socket;\r
- protected ObjectReader reader;\r
- \r
- public BioReplicationThread (ListenCallback callback) {\r
- super(callback);\r
- }\r
-\r
- // loop forever waiting for work to do\r
- public synchronized void run()\r
- {\r
- this.notify();\r
- while (isDoRun()) {\r
- try {\r
- // sleep and release object lock\r
- this.wait();\r
- } catch (InterruptedException e) {\r
- if(log.isInfoEnabled())\r
- log.info("TCP worker thread interrupted in cluster",e);\r
- // clear interrupt status\r
- Thread.interrupted();\r
- }\r
- if ( socket == null ) continue;\r
- try {\r
- drainSocket();\r
- } catch ( Exception x ) {\r
- log.error("Unable to service bio socket");\r
- }finally {\r
- try {socket.close();}catch ( Exception ignore){}\r
- try {reader.close();}catch ( Exception ignore){}\r
- reader = null;\r
- socket = null;\r
- }\r
- // done, ready for more, return to pool\r
- if ( getPool() != null ) getPool().returnWorker (this);\r
- else setDoRun(false);\r
- }\r
- }\r
-\r
- \r
- public synchronized void serviceSocket(Socket socket, ObjectReader reader) {\r
- this.socket = socket;\r
- this.reader = reader;\r
- this.notify(); // awaken the thread\r
- }\r
- \r
- protected void execute(ObjectReader reader) throws Exception{\r
- int pkgcnt = reader.count();\r
-\r
- if ( pkgcnt > 0 ) {\r
- ChannelMessage[] msgs = reader.execute();\r
- for ( int i=0; i<msgs.length; i++ ) {\r
- /**\r
- * Use send ack here if you want to ack the request to the remote \r
- * server before completing the request\r
- * This is considered an asynchronized request\r
- */\r
- if (ChannelData.sendAckAsync(msgs[i].getOptions())) sendAck(Constants.ACK_COMMAND);\r
- try {\r
- //process the message\r
- getCallback().messageDataReceived(msgs[i]);\r
- /**\r
- * Use send ack here if you want the request to complete on this\r
- * server before sending the ack to the remote server\r
- * This is considered a synchronized request\r
- */\r
- if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(Constants.ACK_COMMAND);\r
- }catch ( Exception x ) {\r
- if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(Constants.FAIL_ACK_COMMAND);\r
- log.error("Error thrown from messageDataReceived.",x);\r
- }\r
- if ( getUseBufferPool() ) {\r
- BufferPool.getBufferPool().returnBuffer(msgs[i].getMessage());\r
- msgs[i].setMessage(null);\r
- }\r
- } \r
- }\r
-\r
- \r
- }\r
-\r
- /**\r
- * The actual code which drains the channel associated with\r
- * the given key. This method assumes the key has been\r
- * modified prior to invocation to turn off selection\r
- * interest in OP_READ. When this method completes it\r
- * re-enables OP_READ and calls wakeup() on the selector\r
- * so the selector will resume watching this channel.\r
- */\r
- protected void drainSocket () throws Exception {\r
- InputStream in = socket.getInputStream();\r
- // loop while data available, channel is non-blocking\r
- byte[] buf = new byte[1024];\r
- int length = in.read(buf);\r
- while ( length >= 0 ) {\r
- int count = reader.append(buf,0,length,true);\r
- if ( count > 0 ) execute(reader);\r
- length = in.read(buf);\r
- }\r
- }\r
-\r
-\r
-\r
-\r
- /**\r
- * send a reply-acknowledgement (6,2,3)\r
- * @param key\r
- * @param channel\r
- */\r
- protected void sendAck(byte[] command) {\r
- try {\r
- OutputStream out = socket.getOutputStream();\r
- out.write(command);\r
- out.flush();\r
- if (log.isTraceEnabled()) {\r
- log.trace("ACK sent to " + socket.getPort());\r
- }\r
- } catch ( java.io.IOException x ) {\r
- log.warn("Unable to send ACK back through channel, channel disconnected?: "+x.getMessage());\r
- }\r
- }\r
- \r
- public void close() {\r
- setDoRun(false);\r
- try {socket.close();}catch ( Exception ignore){}\r
- try {reader.close();}catch ( Exception ignore){}\r
- reader = null;\r
- socket = null;\r
- super.close();\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport.bio;
+import java.io.IOException;
+
+import org.apache.catalina.tribes.io.ObjectReader;
+import org.apache.catalina.tribes.transport.Constants;
+import org.apache.catalina.tribes.transport.WorkerThread;
+import java.net.Socket;
+import java.io.InputStream;
+import org.apache.catalina.tribes.transport.ReceiverBase;
+import java.io.OutputStream;
+import org.apache.catalina.tribes.io.ListenCallback;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.io.ChannelData;
+import org.apache.catalina.tribes.io.BufferPool;
+
+/**
+ * A worker thread class which can drain channels and echo-back the input. Each
+ * instance is constructed with a reference to the owning thread pool object.
+ * When started, the thread loops forever waiting to be awakened to service the
+ * channel associated with a SelectionKey object. The worker is tasked by
+ * calling its serviceChannel() method with a SelectionKey object. The
+ * serviceChannel() method stores the key reference in the thread object then
+ * calls notify() to wake it up. When the channel has been drained, the worker
+ * thread returns itself to its parent pool.
+ *
+ * @author Filip Hanik
+ *
+ * @version $Revision$, $Date$
+ */
+public class BioReplicationThread extends WorkerThread {
+
+
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( BioReplicationThread.class );
+
+ protected Socket socket;
+ protected ObjectReader reader;
+
+ public BioReplicationThread (ListenCallback callback) {
+ super(callback);
+ }
+
+ // loop forever waiting for work to do
+ public synchronized void run()
+ {
+ this.notify();
+ while (isDoRun()) {
+ try {
+ // sleep and release object lock
+ this.wait();
+ } catch (InterruptedException e) {
+ if(log.isInfoEnabled())
+ log.info("TCP worker thread interrupted in cluster",e);
+ // clear interrupt status
+ Thread.interrupted();
+ }
+ if ( socket == null ) continue;
+ try {
+ drainSocket();
+ } catch ( Exception x ) {
+ log.error("Unable to service bio socket");
+ }finally {
+ try {socket.close();}catch ( Exception ignore){}
+ try {reader.close();}catch ( Exception ignore){}
+ reader = null;
+ socket = null;
+ }
+ // done, ready for more, return to pool
+ if ( getPool() != null ) getPool().returnWorker (this);
+ else setDoRun(false);
+ }
+ }
+
+
+ public synchronized void serviceSocket(Socket socket, ObjectReader reader) {
+ this.socket = socket;
+ this.reader = reader;
+ this.notify(); // awaken the thread
+ }
+
+ protected void execute(ObjectReader reader) throws Exception{
+ int pkgcnt = reader.count();
+
+ if ( pkgcnt > 0 ) {
+ ChannelMessage[] msgs = reader.execute();
+ for ( int i=0; i<msgs.length; i++ ) {
+ /**
+ * Use send ack here if you want to ack the request to the remote
+ * server before completing the request
+ * This is considered an asynchronized request
+ */
+ if (ChannelData.sendAckAsync(msgs[i].getOptions())) sendAck(Constants.ACK_COMMAND);
+ try {
+ //process the message
+ getCallback().messageDataReceived(msgs[i]);
+ /**
+ * Use send ack here if you want the request to complete on this
+ * server before sending the ack to the remote server
+ * This is considered a synchronized request
+ */
+ if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(Constants.ACK_COMMAND);
+ }catch ( Exception x ) {
+ if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(Constants.FAIL_ACK_COMMAND);
+ log.error("Error thrown from messageDataReceived.",x);
+ }
+ if ( getUseBufferPool() ) {
+ BufferPool.getBufferPool().returnBuffer(msgs[i].getMessage());
+ msgs[i].setMessage(null);
+ }
+ }
+ }
+
+
+ }
+
+ /**
+ * The actual code which drains the channel associated with
+ * the given key. This method assumes the key has been
+ * modified prior to invocation to turn off selection
+ * interest in OP_READ. When this method completes it
+ * re-enables OP_READ and calls wakeup() on the selector
+ * so the selector will resume watching this channel.
+ */
+ protected void drainSocket () throws Exception {
+ InputStream in = socket.getInputStream();
+ // loop while data available, channel is non-blocking
+ byte[] buf = new byte[1024];
+ int length = in.read(buf);
+ while ( length >= 0 ) {
+ int count = reader.append(buf,0,length,true);
+ if ( count > 0 ) execute(reader);
+ length = in.read(buf);
+ }
+ }
+
+
+
+
+ /**
+ * send a reply-acknowledgement (6,2,3)
+ * @param key
+ * @param channel
+ */
+ protected void sendAck(byte[] command) {
+ try {
+ OutputStream out = socket.getOutputStream();
+ out.write(command);
+ out.flush();
+ if (log.isTraceEnabled()) {
+ log.trace("ACK sent to " + socket.getPort());
+ }
+ } catch ( java.io.IOException x ) {
+ log.warn("Unable to send ACK back through channel, channel disconnected?: "+x.getMessage());
+ }
+ }
+
+ public void close() {
+ setDoRun(false);
+ try {socket.close();}catch ( Exception ignore){}
+ try {reader.close();}catch ( Exception ignore){}
+ reader = null;
+ socket = null;
+ super.close();
+ }
+}
*
* @author Peter Rossbach
* @author Filip Hanik
- * @version $Revision: 377484 $ $Date: 2006-02-13 15:00:05 -0600 (Mon, 13 Feb 2006) $
+ * @version $Revision$ $Date$
* @since 5.5.16
*/
public class BioSender extends AbstractSender implements DataSender {
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport.bio.util;\r
-\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.ErrorHandler;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-\r
-\r
-\r
-/**\r
- * A fast queue that remover thread lock the adder thread. <br/>Limit the queue\r
- * length when you have strange producer thread problemes.\r
- * \r
- * FIXME add i18n support to log messages\r
- * @author Rainer Jung\r
- * @author Peter Rossbach\r
- * @version $Revision: 345567 $ $Date: 2005-11-18 15:07:23 -0600 (Fri, 18 Nov 2005) $\r
- */\r
-public class FastQueue {\r
-\r
- private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(FastQueue.class);\r
-\r
- /**\r
- * This is the actual queue\r
- */\r
- private SingleRemoveSynchronizedAddLock lock = null;\r
-\r
- /**\r
- * First Object at queue (consumer message)\r
- */\r
- private LinkObject first = null;\r
-\r
- /**\r
- * Last object in queue (producer Object)\r
- */\r
- private LinkObject last = null;\r
-\r
- /**\r
- * Current Queue elements size\r
- */\r
- private int size = 0;\r
-\r
- /**\r
- * check lock to detect strange threadings things\r
- */\r
- private boolean checkLock = false;\r
-\r
- /**\r
- * protocol the thread wait times\r
- */\r
- private boolean timeWait = false;\r
-\r
- private boolean inAdd = false;\r
-\r
- private boolean inRemove = false;\r
-\r
- private boolean inMutex = false;\r
-\r
- /**\r
- * limit the queue legnth ( default is unlimited)\r
- */\r
- private int maxQueueLength = 0;\r
-\r
- /**\r
- * addWaitTimeout for producer\r
- */\r
- private long addWaitTimeout = 10000L;\r
-\r
- \r
- /**\r
- * removeWaitTimeout for consumer\r
- */\r
- private long removeWaitTimeout = 30000L;\r
-\r
- /**\r
- * enabled the queue\r
- */\r
- private boolean enabled = true;\r
-\r
- /**\r
- * max queue size\r
- */\r
- private int maxSize = 0;\r
-\r
- /**\r
- * avg size sample interval\r
- */\r
- private int sampleInterval = 100;\r
-\r
- /**\r
- * Generate Queue SingleRemoveSynchronizedAddLock and set add and wait\r
- * Timeouts\r
- */\r
- public FastQueue() {\r
- lock = new SingleRemoveSynchronizedAddLock();\r
- lock.setAddWaitTimeout(addWaitTimeout);\r
- lock.setRemoveWaitTimeout(removeWaitTimeout);\r
- }\r
-\r
- /**\r
- * get current add wait timeout\r
- * \r
- * @return current wait timeout\r
- */\r
- public long getAddWaitTimeout() {\r
- addWaitTimeout = lock.getAddWaitTimeout();\r
- return addWaitTimeout;\r
- }\r
-\r
- /**\r
- * Set add wait timeout (default 10000 msec)\r
- * \r
- * @param timeout\r
- */\r
- public void setAddWaitTimeout(long timeout) {\r
- addWaitTimeout = timeout;\r
- lock.setAddWaitTimeout(addWaitTimeout);\r
- }\r
-\r
- /**\r
- * get current remove wait timeout\r
- * \r
- * @return The timeout\r
- */\r
- public long getRemoveWaitTimeout() {\r
- removeWaitTimeout = lock.getRemoveWaitTimeout();\r
- return removeWaitTimeout;\r
- }\r
-\r
- /**\r
- * set remove wait timeout ( default 30000 msec)\r
- * \r
- * @param timeout\r
- */\r
- public void setRemoveWaitTimeout(long timeout) {\r
- removeWaitTimeout = timeout;\r
- lock.setRemoveWaitTimeout(removeWaitTimeout);\r
- }\r
-\r
- /**\r
- * get Max Queue length\r
- * \r
- * @see org.apache.catalina.tribes.util.IQueue#getMaxQueueLength()\r
- */\r
- public int getMaxQueueLength() {\r
- return maxQueueLength;\r
- }\r
-\r
- public void setMaxQueueLength(int length) {\r
- maxQueueLength = length;\r
- }\r
-\r
- public boolean isEnabled() {\r
- return enabled;\r
- }\r
-\r
- public void setEnabled(boolean enable) {\r
- enabled = enable;\r
- if (!enabled) {\r
- lock.abortRemove();\r
- last = first = null;\r
- }\r
- }\r
-\r
- /**\r
- * @return Returns the checkLock.\r
- */\r
- public boolean isCheckLock() {\r
- return checkLock;\r
- }\r
-\r
- /**\r
- * @param checkLock The checkLock to set.\r
- */\r
- public void setCheckLock(boolean checkLock) {\r
- this.checkLock = checkLock;\r
- }\r
-\r
- \r
- /**\r
- * @return The max size\r
- */\r
- public int getMaxSize() {\r
- return maxSize;\r
- }\r
-\r
- /**\r
- * @param size\r
- */\r
- public void setMaxSize(int size) {\r
- maxSize = size;\r
- }\r
-\r
- \r
- /**\r
- * unlock queue for next add \r
- */\r
- public void unlockAdd() {\r
- lock.unlockAdd(size > 0 ? true : false);\r
- }\r
-\r
- /**\r
- * unlock queue for next remove \r
- */\r
- public void unlockRemove() {\r
- lock.unlockRemove();\r
- }\r
-\r
- /**\r
- * start queuing\r
- */\r
- public void start() {\r
- setEnabled(true);\r
- }\r
-\r
- /**\r
- * start queuing\r
- */\r
- public void stop() {\r
- setEnabled(false);\r
- }\r
-\r
- public int getSize() {\r
- return size;\r
- }\r
-\r
- public SingleRemoveSynchronizedAddLock getLock() {\r
- return lock;\r
- }\r
-\r
- /**\r
- * Add new data to the queue\r
- * @see org.apache.catalina.tribes.util.IQueue#add(java.lang.String, java.lang.Object)\r
- * FIXME extract some method\r
- */\r
- public boolean add(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {\r
- boolean ok = true;\r
- long time = 0;\r
-\r
- if (!enabled) {\r
- if (log.isInfoEnabled())\r
- log.info("FastQueue.add: queue disabled, add aborted");\r
- return false;\r
- }\r
-\r
- if (timeWait) {\r
- time = System.currentTimeMillis();\r
- }\r
- lock.lockAdd();\r
- try {\r
- if (log.isTraceEnabled()) {\r
- log.trace("FastQueue.add: starting with size " + size);\r
- }\r
- if (checkLock) {\r
- if (inAdd)\r
- log.warn("FastQueue.add: Detected other add");\r
- inAdd = true;\r
- if (inMutex)\r
- log.warn("FastQueue.add: Detected other mutex in add");\r
- inMutex = true;\r
- }\r
-\r
- if ((maxQueueLength > 0) && (size >= maxQueueLength)) {\r
- ok = false;\r
- if (log.isTraceEnabled()) {\r
- log.trace("FastQueue.add: Could not add, since queue is full (" + size + ">=" + maxQueueLength + ")");\r
- }\r
- } else {\r
- LinkObject element = new LinkObject(msg,destination, payload);\r
- if (size == 0) {\r
- first = last = element;\r
- size = 1;\r
- } else {\r
- if (last == null) {\r
- ok = false;\r
- log.error("FastQueue.add: Could not add, since last is null although size is "+ size + " (>0)");\r
- } else {\r
- last.append(element);\r
- last = element;\r
- size++;\r
- }\r
- }\r
- }\r
-\r
- if (first == null) {\r
- log.error("FastQueue.add: first is null, size is " + size + " at end of add");\r
- }\r
- if (last == null) {\r
- log.error("FastQueue.add: last is null, size is " + size+ " at end of add");\r
- }\r
-\r
- if (checkLock) {\r
- if (!inMutex) log.warn("FastQueue.add: Cancelled by other mutex in add");\r
- inMutex = false;\r
- if (!inAdd) log.warn("FastQueue.add: Cancelled by other add");\r
- inAdd = false;\r
- }\r
- if (log.isTraceEnabled()) log.trace("FastQueue.add: add ending with size " + size);\r
-\r
- } finally {\r
- lock.unlockAdd(true);\r
- }\r
- return ok;\r
- }\r
-\r
- /**\r
- * remove the complete queued object list\r
- * @see org.apache.catalina.tribes.util.IQueue#remove()\r
- * FIXME extract some method\r
- */\r
- public LinkObject remove() {\r
- LinkObject element;\r
- boolean gotLock;\r
- long time = 0;\r
-\r
- if (!enabled) {\r
- if (log.isInfoEnabled())\r
- log.info("FastQueue.remove: queue disabled, remove aborted");\r
- return null;\r
- }\r
-\r
- if (timeWait) {\r
- time = System.currentTimeMillis();\r
- }\r
- gotLock = lock.lockRemove();\r
- try {\r
-\r
- if (!gotLock) {\r
- if (enabled) {\r
- if (log.isInfoEnabled())\r
- log.info("FastQueue.remove: Remove aborted although queue enabled");\r
- } else {\r
- if (log.isInfoEnabled())\r
- log.info("FastQueue.remove: queue disabled, remove aborted");\r
- }\r
- return null;\r
- }\r
-\r
- if (log.isTraceEnabled()) {\r
- log.trace("FastQueue.remove: remove starting with size " + size);\r
- }\r
- if (checkLock) {\r
- if (inRemove)\r
- log.warn("FastQueue.remove: Detected other remove");\r
- inRemove = true;\r
- if (inMutex)\r
- log.warn("FastQueue.remove: Detected other mutex in remove");\r
- inMutex = true;\r
- }\r
-\r
- element = first;\r
-\r
- first = last = null;\r
- size = 0;\r
-\r
- if (checkLock) {\r
- if (!inMutex)\r
- log.warn("FastQueue.remove: Cancelled by other mutex in remove");\r
- inMutex = false;\r
- if (!inRemove)\r
- log.warn("FastQueue.remove: Cancelled by other remove");\r
- inRemove = false;\r
- }\r
- if (log.isTraceEnabled()) {\r
- log.trace("FastQueue.remove: remove ending with size " + size);\r
- }\r
-\r
- if (timeWait) {\r
- time = System.currentTimeMillis();\r
- }\r
- } finally {\r
- lock.unlockRemove();\r
- }\r
- return element;\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport.bio.util;
+
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.ErrorHandler;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+
+
+
+/**
+ * A fast queue that remover thread lock the adder thread. <br/>Limit the queue
+ * length when you have strange producer thread problemes.
+ *
+ * FIXME add i18n support to log messages
+ * @author Rainer Jung
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ */
+public class FastQueue {
+
+ private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(FastQueue.class);
+
+ /**
+ * This is the actual queue
+ */
+ private SingleRemoveSynchronizedAddLock lock = null;
+
+ /**
+ * First Object at queue (consumer message)
+ */
+ private LinkObject first = null;
+
+ /**
+ * Last object in queue (producer Object)
+ */
+ private LinkObject last = null;
+
+ /**
+ * Current Queue elements size
+ */
+ private int size = 0;
+
+ /**
+ * check lock to detect strange threadings things
+ */
+ private boolean checkLock = false;
+
+ /**
+ * protocol the thread wait times
+ */
+ private boolean timeWait = false;
+
+ private boolean inAdd = false;
+
+ private boolean inRemove = false;
+
+ private boolean inMutex = false;
+
+ /**
+ * limit the queue legnth ( default is unlimited)
+ */
+ private int maxQueueLength = 0;
+
+ /**
+ * addWaitTimeout for producer
+ */
+ private long addWaitTimeout = 10000L;
+
+
+ /**
+ * removeWaitTimeout for consumer
+ */
+ private long removeWaitTimeout = 30000L;
+
+ /**
+ * enabled the queue
+ */
+ private boolean enabled = true;
+
+ /**
+ * max queue size
+ */
+ private int maxSize = 0;
+
+ /**
+ * avg size sample interval
+ */
+ private int sampleInterval = 100;
+
+ /**
+ * Generate Queue SingleRemoveSynchronizedAddLock and set add and wait
+ * Timeouts
+ */
+ public FastQueue() {
+ lock = new SingleRemoveSynchronizedAddLock();
+ lock.setAddWaitTimeout(addWaitTimeout);
+ lock.setRemoveWaitTimeout(removeWaitTimeout);
+ }
+
+ /**
+ * get current add wait timeout
+ *
+ * @return current wait timeout
+ */
+ public long getAddWaitTimeout() {
+ addWaitTimeout = lock.getAddWaitTimeout();
+ return addWaitTimeout;
+ }
+
+ /**
+ * Set add wait timeout (default 10000 msec)
+ *
+ * @param timeout
+ */
+ public void setAddWaitTimeout(long timeout) {
+ addWaitTimeout = timeout;
+ lock.setAddWaitTimeout(addWaitTimeout);
+ }
+
+ /**
+ * get current remove wait timeout
+ *
+ * @return The timeout
+ */
+ public long getRemoveWaitTimeout() {
+ removeWaitTimeout = lock.getRemoveWaitTimeout();
+ return removeWaitTimeout;
+ }
+
+ /**
+ * set remove wait timeout ( default 30000 msec)
+ *
+ * @param timeout
+ */
+ public void setRemoveWaitTimeout(long timeout) {
+ removeWaitTimeout = timeout;
+ lock.setRemoveWaitTimeout(removeWaitTimeout);
+ }
+
+ /**
+ * get Max Queue length
+ *
+ * @see org.apache.catalina.tribes.util.IQueue#getMaxQueueLength()
+ */
+ public int getMaxQueueLength() {
+ return maxQueueLength;
+ }
+
+ public void setMaxQueueLength(int length) {
+ maxQueueLength = length;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enable) {
+ enabled = enable;
+ if (!enabled) {
+ lock.abortRemove();
+ last = first = null;
+ }
+ }
+
+ /**
+ * @return Returns the checkLock.
+ */
+ public boolean isCheckLock() {
+ return checkLock;
+ }
+
+ /**
+ * @param checkLock The checkLock to set.
+ */
+ public void setCheckLock(boolean checkLock) {
+ this.checkLock = checkLock;
+ }
+
+
+ /**
+ * @return The max size
+ */
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ * @param size
+ */
+ public void setMaxSize(int size) {
+ maxSize = size;
+ }
+
+
+ /**
+ * unlock queue for next add
+ */
+ public void unlockAdd() {
+ lock.unlockAdd(size > 0 ? true : false);
+ }
+
+ /**
+ * unlock queue for next remove
+ */
+ public void unlockRemove() {
+ lock.unlockRemove();
+ }
+
+ /**
+ * start queuing
+ */
+ public void start() {
+ setEnabled(true);
+ }
+
+ /**
+ * start queuing
+ */
+ public void stop() {
+ setEnabled(false);
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public SingleRemoveSynchronizedAddLock getLock() {
+ return lock;
+ }
+
+ /**
+ * Add new data to the queue
+ * @see org.apache.catalina.tribes.util.IQueue#add(java.lang.String, java.lang.Object)
+ * FIXME extract some method
+ */
+ public boolean add(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {
+ boolean ok = true;
+ long time = 0;
+
+ if (!enabled) {
+ if (log.isInfoEnabled())
+ log.info("FastQueue.add: queue disabled, add aborted");
+ return false;
+ }
+
+ if (timeWait) {
+ time = System.currentTimeMillis();
+ }
+ lock.lockAdd();
+ try {
+ if (log.isTraceEnabled()) {
+ log.trace("FastQueue.add: starting with size " + size);
+ }
+ if (checkLock) {
+ if (inAdd)
+ log.warn("FastQueue.add: Detected other add");
+ inAdd = true;
+ if (inMutex)
+ log.warn("FastQueue.add: Detected other mutex in add");
+ inMutex = true;
+ }
+
+ if ((maxQueueLength > 0) && (size >= maxQueueLength)) {
+ ok = false;
+ if (log.isTraceEnabled()) {
+ log.trace("FastQueue.add: Could not add, since queue is full (" + size + ">=" + maxQueueLength + ")");
+ }
+ } else {
+ LinkObject element = new LinkObject(msg,destination, payload);
+ if (size == 0) {
+ first = last = element;
+ size = 1;
+ } else {
+ if (last == null) {
+ ok = false;
+ log.error("FastQueue.add: Could not add, since last is null although size is "+ size + " (>0)");
+ } else {
+ last.append(element);
+ last = element;
+ size++;
+ }
+ }
+ }
+
+ if (first == null) {
+ log.error("FastQueue.add: first is null, size is " + size + " at end of add");
+ }
+ if (last == null) {
+ log.error("FastQueue.add: last is null, size is " + size+ " at end of add");
+ }
+
+ if (checkLock) {
+ if (!inMutex) log.warn("FastQueue.add: Cancelled by other mutex in add");
+ inMutex = false;
+ if (!inAdd) log.warn("FastQueue.add: Cancelled by other add");
+ inAdd = false;
+ }
+ if (log.isTraceEnabled()) log.trace("FastQueue.add: add ending with size " + size);
+
+ } finally {
+ lock.unlockAdd(true);
+ }
+ return ok;
+ }
+
+ /**
+ * remove the complete queued object list
+ * @see org.apache.catalina.tribes.util.IQueue#remove()
+ * FIXME extract some method
+ */
+ public LinkObject remove() {
+ LinkObject element;
+ boolean gotLock;
+ long time = 0;
+
+ if (!enabled) {
+ if (log.isInfoEnabled())
+ log.info("FastQueue.remove: queue disabled, remove aborted");
+ return null;
+ }
+
+ if (timeWait) {
+ time = System.currentTimeMillis();
+ }
+ gotLock = lock.lockRemove();
+ try {
+
+ if (!gotLock) {
+ if (enabled) {
+ if (log.isInfoEnabled())
+ log.info("FastQueue.remove: Remove aborted although queue enabled");
+ } else {
+ if (log.isInfoEnabled())
+ log.info("FastQueue.remove: queue disabled, remove aborted");
+ }
+ return null;
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("FastQueue.remove: remove starting with size " + size);
+ }
+ if (checkLock) {
+ if (inRemove)
+ log.warn("FastQueue.remove: Detected other remove");
+ inRemove = true;
+ if (inMutex)
+ log.warn("FastQueue.remove: Detected other mutex in remove");
+ inMutex = true;
+ }
+
+ element = first;
+
+ first = last = null;
+ size = 0;
+
+ if (checkLock) {
+ if (!inMutex)
+ log.warn("FastQueue.remove: Cancelled by other mutex in remove");
+ inMutex = false;
+ if (!inRemove)
+ log.warn("FastQueue.remove: Cancelled by other remove");
+ inRemove = false;
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("FastQueue.remove: remove ending with size " + size);
+ }
+
+ if (timeWait) {
+ time = System.currentTimeMillis();
+ }
+ } finally {
+ lock.unlockRemove();
+ }
+ return element;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport.bio.util;\r
-\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.ErrorHandler;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.group.InterceptorPayload;\r
-\r
-/**\r
- * The class <b>LinkObject</b> implements an element\r
- * for a linked list, consisting of a general\r
- * data object and a pointer to the next element.\r
- *\r
- * @author Rainer Jung\r
- * @author Peter Rossbach\r
- * @author Filip Hanik\r
- * @version $Revision: 304032 $ $Date: 2005-07-27 10:11:55 -0500 (Wed, 27 Jul 2005) $\r
-\r
- */\r
-\r
-public class LinkObject {\r
-\r
- private ChannelMessage msg;\r
- private LinkObject next;\r
- private byte[] key ;\r
- private Member[] destination;\r
- private InterceptorPayload payload;\r
-\r
- /**\r
- * Construct a new element from the data object.\r
- * Sets the pointer to null.\r
- *\r
- * @param key The key\r
- * @param payload The data object.\r
- */\r
- public LinkObject(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {\r
- this.msg = msg;\r
- this.next = null;\r
- this.key = msg.getUniqueId();\r
- this.payload = payload;\r
- this.destination = destination;\r
- }\r
-\r
- /**\r
- * Set the next element.\r
- * @param next The next element.\r
- */\r
- public void append(LinkObject next) {\r
- this.next = next;\r
- }\r
-\r
- /**\r
- * Get the next element.\r
- * @return The next element.\r
- */\r
- public LinkObject next() {\r
- return next;\r
- }\r
- \r
- public void setNext(LinkObject next) {\r
- this.next = next;\r
- }\r
-\r
- /**\r
- * Get the data object from the element.\r
- * @return The data object from the element.\r
- */\r
- public ChannelMessage data() {\r
- return msg;\r
- }\r
-\r
- /**\r
- * Get the unique message id\r
- * @return the unique message id\r
- */\r
- public byte[] getKey() {\r
- return key;\r
- }\r
-\r
- public ErrorHandler getHandler() {\r
- return payload!=null?payload.getErrorHandler():null;\r
- }\r
-\r
- public InterceptorPayload getPayload() {\r
- return payload;\r
- }\r
-\r
- public Member[] getDestination() {\r
- return destination;\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport.bio.util;
+
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.ErrorHandler;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.InterceptorPayload;
+
+/**
+ * The class <b>LinkObject</b> implements an element
+ * for a linked list, consisting of a general
+ * data object and a pointer to the next element.
+ *
+ * @author Rainer Jung
+ * @author Peter Rossbach
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+
+ */
+
+public class LinkObject {
+
+ private ChannelMessage msg;
+ private LinkObject next;
+ private byte[] key ;
+ private Member[] destination;
+ private InterceptorPayload payload;
+
+ /**
+ * Construct a new element from the data object.
+ * Sets the pointer to null.
+ *
+ * @param key The key
+ * @param payload The data object.
+ */
+ public LinkObject(ChannelMessage msg, Member[] destination, InterceptorPayload payload) {
+ this.msg = msg;
+ this.next = null;
+ this.key = msg.getUniqueId();
+ this.payload = payload;
+ this.destination = destination;
+ }
+
+ /**
+ * Set the next element.
+ * @param next The next element.
+ */
+ public void append(LinkObject next) {
+ this.next = next;
+ }
+
+ /**
+ * Get the next element.
+ * @return The next element.
+ */
+ public LinkObject next() {
+ return next;
+ }
+
+ public void setNext(LinkObject next) {
+ this.next = next;
+ }
+
+ /**
+ * Get the data object from the element.
+ * @return The data object from the element.
+ */
+ public ChannelMessage data() {
+ return msg;
+ }
+
+ /**
+ * Get the unique message id
+ * @return the unique message id
+ */
+ public byte[] getKey() {
+ return key;
+ }
+
+ public ErrorHandler getHandler() {
+ return payload!=null?payload.getErrorHandler():null;
+ }
+
+ public InterceptorPayload getPayload() {
+ return payload;
+ }
+
+ public Member[] getDestination() {
+ return destination;
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport.bio.util;\r
-\r
-/**\r
- * The class <b>SingleRemoveSynchronizedAddLock</b> implement locking for accessing the queue\r
- * by a single remove thread and multiple add threads.\r
- *\r
- * A thread is only allowed to be either the remove or\r
- * an add thread.\r
- *\r
- * The lock can either be owned by the remove thread\r
- * or by a single add thread.\r
- *\r
- * If the remove thread tries to get the lock,\r
- * but the queue is empty, it will block (poll)\r
- * until an add threads adds an entry to the queue and\r
- * releases the lock.\r
- * \r
- * If the remove thread and add threads compete for\r
- * the lock and an add thread releases the lock, then\r
- * the remove thread will get the lock first.\r
- *\r
- * The remove thread removes all entries in the queue\r
- * at once and proceeses them without further\r
- * polling the queue.\r
- *\r
- * The lock is not reentrant, in the sense, that all\r
- * threads must release an owned lock before competing\r
- * for the lock again!\r
- *\r
- * @author Rainer Jung\r
- * @author Peter Rossbach\r
- * @version 1.1\r
- */\r
- \r
-public class SingleRemoveSynchronizedAddLock {\r
- \r
- public SingleRemoveSynchronizedAddLock() {\r
- }\r
- \r
- public SingleRemoveSynchronizedAddLock(boolean dataAvailable) {\r
- this.dataAvailable=dataAvailable;\r
- }\r
- \r
- /**\r
- * Time in milliseconds after which threads\r
- * waiting for an add lock are woken up.\r
- * This is used as a safety measure in case\r
- * thread notification via the unlock methods\r
- * has a bug.\r
- */\r
- private long addWaitTimeout = 10000L;\r
-\r
- /**\r
- * Time in milliseconds after which threads\r
- * waiting for a remove lock are woken up.\r
- * This is used as a safety measure in case\r
- * thread notification via the unlock methods\r
- * has a bug.\r
- */\r
- private long removeWaitTimeout = 30000L;\r
-\r
- /**\r
- * The current remove thread.\r
- * It is set to the remove thread polling for entries.\r
- * It is reset to null when the remove thread\r
- * releases the lock and proceeds processing\r
- * the removed entries.\r
- */\r
- private Thread remover = null;\r
-\r
- /**\r
- * A flag indicating, if an add thread owns the lock.\r
- */\r
- private boolean addLocked = false;\r
-\r
- /**\r
- * A flag indicating, if the remove thread owns the lock.\r
- */\r
- private boolean removeLocked = false;\r
-\r
- /**\r
- * A flag indicating, if the remove thread is allowed\r
- * to wait for the lock. The flag is set to false, when aborting.\r
- */\r
- private boolean removeEnabled = true;\r
-\r
- /**\r
- * A flag indicating, if the remover needs polling.\r
- * It indicates, if the locked object has data available\r
- * to be removed.\r
- */\r
- private boolean dataAvailable = false;\r
-\r
- /**\r
- * @return Value of addWaitTimeout\r
- */\r
- public synchronized long getAddWaitTimeout() {\r
- return addWaitTimeout;\r
- }\r
-\r
- /**\r
- * Set value of addWaitTimeout\r
- */\r
- public synchronized void setAddWaitTimeout(long timeout) {\r
- addWaitTimeout = timeout;\r
- }\r
-\r
- /**\r
- * @return Value of removeWaitTimeout\r
- */\r
- public synchronized long getRemoveWaitTimeout() {\r
- return removeWaitTimeout;\r
- }\r
-\r
- /**\r
- * Set value of removeWaitTimeout\r
- */\r
- public synchronized void setRemoveWaitTimeout(long timeout) {\r
- removeWaitTimeout = timeout;\r
- }\r
-\r
- /**\r
- * Check if the locked object has data available\r
- * i.e. the remover can stop poling and get the lock.\r
- * @return True iff the lock Object has data available.\r
- */\r
- public synchronized boolean isDataAvailable() {\r
- return dataAvailable;\r
- }\r
-\r
- /**\r
- * Check if an add thread owns the lock.\r
- * @return True iff an add thread owns the lock.\r
- */\r
- public synchronized boolean isAddLocked() {\r
- return addLocked;\r
- }\r
-\r
- /**\r
- * Check if the remove thread owns the lock.\r
- * @return True iff the remove thread owns the lock.\r
- */\r
- public synchronized boolean isRemoveLocked() {\r
- return removeLocked;\r
- }\r
-\r
- /**\r
- * Check if the remove thread is polling.\r
- * @return True iff the remove thread is polling.\r
- */\r
- public synchronized boolean isRemovePolling() {\r
- if ( remover != null ) {\r
- return true;\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Acquires the lock by an add thread and sets the add flag.\r
- * If any add thread or the remove thread already acquired the lock\r
- * this add thread will block until the lock is released.\r
- */\r
- public synchronized void lockAdd() {\r
- if ( addLocked || removeLocked ) {\r
- do {\r
- try {\r
- wait(addWaitTimeout);\r
- } catch ( InterruptedException e ) {\r
- Thread.currentThread().interrupted();\r
- }\r
- } while ( addLocked || removeLocked );\r
- }\r
- addLocked=true;\r
- }\r
-\r
- /**\r
- * Acquires the lock by the remove thread and sets the remove flag.\r
- * If any add thread already acquired the lock or the queue is\r
- * empty, the remove thread will block until the lock is released\r
- * and the queue is not empty.\r
- */\r
- public synchronized boolean lockRemove() {\r
- removeLocked=false;\r
- removeEnabled=true;\r
- if ( ( addLocked || ! dataAvailable ) && removeEnabled ) {\r
- remover=Thread.currentThread();\r
- do {\r
- try {\r
- wait(removeWaitTimeout);\r
- } catch ( InterruptedException e ) {\r
- Thread.currentThread().interrupted();\r
- }\r
- } while ( ( addLocked || ! dataAvailable ) && removeEnabled );\r
- remover=null;\r
- }\r
- if ( removeEnabled ) {\r
- removeLocked=true;\r
- } \r
- return removeLocked;\r
- }\r
-\r
- /**\r
- * Releases the lock by an add thread and reset the remove flag.\r
- * If the reader thread is polling, notify it.\r
- */\r
- public synchronized void unlockAdd(boolean dataAvailable) {\r
- addLocked=false;\r
- this.dataAvailable=dataAvailable;\r
- if ( ( remover != null ) && ( dataAvailable || ! removeEnabled ) ) {\r
- remover.interrupt();\r
- } else {\r
- notifyAll();\r
- }\r
- }\r
-\r
- /**\r
- * Releases the lock by the remove thread and reset the add flag.\r
- * Notify all waiting add threads,\r
- * that the lock has been released by the remove thread.\r
- */\r
- public synchronized void unlockRemove() {\r
- removeLocked=false;\r
- dataAvailable=false;\r
- notifyAll();\r
- }\r
-\r
- /**\r
- * Abort any polling remover thread\r
- */\r
- public synchronized void abortRemove() {\r
- removeEnabled=false;\r
- if ( remover != null ) {\r
- remover.interrupt();\r
- }\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport.bio.util;
+
+/**
+ * The class <b>SingleRemoveSynchronizedAddLock</b> implement locking for accessing the queue
+ * by a single remove thread and multiple add threads.
+ *
+ * A thread is only allowed to be either the remove or
+ * an add thread.
+ *
+ * The lock can either be owned by the remove thread
+ * or by a single add thread.
+ *
+ * If the remove thread tries to get the lock,
+ * but the queue is empty, it will block (poll)
+ * until an add threads adds an entry to the queue and
+ * releases the lock.
+ *
+ * If the remove thread and add threads compete for
+ * the lock and an add thread releases the lock, then
+ * the remove thread will get the lock first.
+ *
+ * The remove thread removes all entries in the queue
+ * at once and proceeses them without further
+ * polling the queue.
+ *
+ * The lock is not reentrant, in the sense, that all
+ * threads must release an owned lock before competing
+ * for the lock again!
+ *
+ * @author Rainer Jung
+ * @author Peter Rossbach
+ * @version 1.1
+ */
+
+public class SingleRemoveSynchronizedAddLock {
+
+ public SingleRemoveSynchronizedAddLock() {
+ }
+
+ public SingleRemoveSynchronizedAddLock(boolean dataAvailable) {
+ this.dataAvailable=dataAvailable;
+ }
+
+ /**
+ * Time in milliseconds after which threads
+ * waiting for an add lock are woken up.
+ * This is used as a safety measure in case
+ * thread notification via the unlock methods
+ * has a bug.
+ */
+ private long addWaitTimeout = 10000L;
+
+ /**
+ * Time in milliseconds after which threads
+ * waiting for a remove lock are woken up.
+ * This is used as a safety measure in case
+ * thread notification via the unlock methods
+ * has a bug.
+ */
+ private long removeWaitTimeout = 30000L;
+
+ /**
+ * The current remove thread.
+ * It is set to the remove thread polling for entries.
+ * It is reset to null when the remove thread
+ * releases the lock and proceeds processing
+ * the removed entries.
+ */
+ private Thread remover = null;
+
+ /**
+ * A flag indicating, if an add thread owns the lock.
+ */
+ private boolean addLocked = false;
+
+ /**
+ * A flag indicating, if the remove thread owns the lock.
+ */
+ private boolean removeLocked = false;
+
+ /**
+ * A flag indicating, if the remove thread is allowed
+ * to wait for the lock. The flag is set to false, when aborting.
+ */
+ private boolean removeEnabled = true;
+
+ /**
+ * A flag indicating, if the remover needs polling.
+ * It indicates, if the locked object has data available
+ * to be removed.
+ */
+ private boolean dataAvailable = false;
+
+ /**
+ * @return Value of addWaitTimeout
+ */
+ public synchronized long getAddWaitTimeout() {
+ return addWaitTimeout;
+ }
+
+ /**
+ * Set value of addWaitTimeout
+ */
+ public synchronized void setAddWaitTimeout(long timeout) {
+ addWaitTimeout = timeout;
+ }
+
+ /**
+ * @return Value of removeWaitTimeout
+ */
+ public synchronized long getRemoveWaitTimeout() {
+ return removeWaitTimeout;
+ }
+
+ /**
+ * Set value of removeWaitTimeout
+ */
+ public synchronized void setRemoveWaitTimeout(long timeout) {
+ removeWaitTimeout = timeout;
+ }
+
+ /**
+ * Check if the locked object has data available
+ * i.e. the remover can stop poling and get the lock.
+ * @return True iff the lock Object has data available.
+ */
+ public synchronized boolean isDataAvailable() {
+ return dataAvailable;
+ }
+
+ /**
+ * Check if an add thread owns the lock.
+ * @return True iff an add thread owns the lock.
+ */
+ public synchronized boolean isAddLocked() {
+ return addLocked;
+ }
+
+ /**
+ * Check if the remove thread owns the lock.
+ * @return True iff the remove thread owns the lock.
+ */
+ public synchronized boolean isRemoveLocked() {
+ return removeLocked;
+ }
+
+ /**
+ * Check if the remove thread is polling.
+ * @return True iff the remove thread is polling.
+ */
+ public synchronized boolean isRemovePolling() {
+ if ( remover != null ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Acquires the lock by an add thread and sets the add flag.
+ * If any add thread or the remove thread already acquired the lock
+ * this add thread will block until the lock is released.
+ */
+ public synchronized void lockAdd() {
+ if ( addLocked || removeLocked ) {
+ do {
+ try {
+ wait(addWaitTimeout);
+ } catch ( InterruptedException e ) {
+ Thread.currentThread().interrupted();
+ }
+ } while ( addLocked || removeLocked );
+ }
+ addLocked=true;
+ }
+
+ /**
+ * Acquires the lock by the remove thread and sets the remove flag.
+ * If any add thread already acquired the lock or the queue is
+ * empty, the remove thread will block until the lock is released
+ * and the queue is not empty.
+ */
+ public synchronized boolean lockRemove() {
+ removeLocked=false;
+ removeEnabled=true;
+ if ( ( addLocked || ! dataAvailable ) && removeEnabled ) {
+ remover=Thread.currentThread();
+ do {
+ try {
+ wait(removeWaitTimeout);
+ } catch ( InterruptedException e ) {
+ Thread.currentThread().interrupted();
+ }
+ } while ( ( addLocked || ! dataAvailable ) && removeEnabled );
+ remover=null;
+ }
+ if ( removeEnabled ) {
+ removeLocked=true;
+ }
+ return removeLocked;
+ }
+
+ /**
+ * Releases the lock by an add thread and reset the remove flag.
+ * If the reader thread is polling, notify it.
+ */
+ public synchronized void unlockAdd(boolean dataAvailable) {
+ addLocked=false;
+ this.dataAvailable=dataAvailable;
+ if ( ( remover != null ) && ( dataAvailable || ! removeEnabled ) ) {
+ remover.interrupt();
+ } else {
+ notifyAll();
+ }
+ }
+
+ /**
+ * Releases the lock by the remove thread and reset the add flag.
+ * Notify all waiting add threads,
+ * that the lock has been released by the remove thread.
+ */
+ public synchronized void unlockRemove() {
+ removeLocked=false;
+ dataAvailable=false;
+ notifyAll();
+ }
+
+ /**
+ * Abort any polling remover thread
+ */
+ public synchronized void abortRemove() {
+ removeEnabled=false;
+ if ( remover != null ) {
+ remover.interrupt();
+ }
+ }
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport.nio;\r
-\r
-import java.io.IOException;\r
-import java.net.ServerSocket;\r
-import java.nio.channels.SelectableChannel;\r
-import java.nio.channels.SelectionKey;\r
-import java.nio.channels.Selector;\r
-import java.nio.channels.ServerSocketChannel;\r
-import java.nio.channels.SocketChannel;\r
-import java.util.Iterator;\r
-\r
-import org.apache.catalina.tribes.ChannelReceiver;\r
-import org.apache.catalina.tribes.io.ListenCallback;\r
-import org.apache.catalina.tribes.io.ObjectReader;\r
-import org.apache.catalina.tribes.transport.Constants;\r
-import org.apache.catalina.tribes.transport.ReceiverBase;\r
-import org.apache.catalina.tribes.transport.ThreadPool;\r
-import org.apache.catalina.tribes.transport.WorkerThread;\r
-import org.apache.catalina.tribes.util.StringManager;\r
-import java.util.LinkedList;\r
-import java.util.Set;\r
-import java.nio.channels.CancelledKeyException;\r
-\r
-/**\r
- * @author Filip Hanik\r
- * @version $Revision: 379904 $ $Date: 2006-02-22 15:16:25 -0600 (Wed, 22 Feb 2006) $\r
- */\r
-public class NioReceiver extends ReceiverBase implements Runnable, ChannelReceiver, ListenCallback {\r
-\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(NioReceiver.class);\r
-\r
- /**\r
- * The string manager for this package.\r
- */\r
- protected StringManager sm = StringManager.getManager(Constants.Package);\r
-\r
- /**\r
- * The descriptive information about this implementation.\r
- */\r
- private static final String info = "NioReceiver/1.0";\r
-\r
- private Selector selector = null;\r
- private ServerSocketChannel serverChannel = null;\r
-\r
- protected LinkedList events = new LinkedList();\r
-// private Object interestOpsMutex = new Object();\r
-\r
- public NioReceiver() {\r
- }\r
-\r
- /**\r
- * Return descriptive information about this implementation and the\r
- * corresponding version number, in the format\r
- * <code><description>/<version></code>.\r
- */\r
- public String getInfo() {\r
- return (info);\r
- }\r
-\r
-// public Object getInterestOpsMutex() {\r
-// return interestOpsMutex;\r
-// }\r
-\r
- public void stop() {\r
- this.stopListening();\r
- }\r
-\r
- /**\r
- * start cluster receiver\r
- * @throws Exception\r
- * @see org.apache.catalina.tribes.ClusterReceiver#start()\r
- */\r
- public void start() throws IOException {\r
- try {\r
-// setPool(new ThreadPool(interestOpsMutex, getMaxThreads(),getMinThreads(),this));\r
- setPool(new ThreadPool(getMaxThreads(),getMinThreads(),this));\r
- } catch (Exception x) {\r
- log.fatal("ThreadPool can initilzed. Listener not started", x);\r
- if ( x instanceof IOException ) throw (IOException)x;\r
- else throw new IOException(x.getMessage());\r
- }\r
- try {\r
- getBind();\r
- bind();\r
- Thread t = new Thread(this, "NioReceiver");\r
- t.setDaemon(true);\r
- t.start();\r
- } catch (Exception x) {\r
- log.fatal("Unable to start cluster receiver", x);\r
- if ( x instanceof IOException ) throw (IOException)x;\r
- else throw new IOException(x.getMessage());\r
- }\r
- }\r
- \r
- public WorkerThread getWorkerThread() {\r
- NioReplicationThread thread = new NioReplicationThread(this,this);\r
- thread.setUseBufferPool(this.getUseBufferPool());\r
- thread.setRxBufSize(getRxBufSize());\r
- thread.setOptions(getWorkerThreadOptions());\r
- return thread;\r
- }\r
- \r
- \r
- \r
- protected void bind() throws IOException {\r
- // allocate an unbound server socket channel\r
- serverChannel = ServerSocketChannel.open();\r
- // Get the associated ServerSocket to bind it with\r
- ServerSocket serverSocket = serverChannel.socket();\r
- // create a new Selector for use below\r
- selector = Selector.open();\r
- // set the port the server channel will listen to\r
- //serverSocket.bind(new InetSocketAddress(getBind(), getTcpListenPort()));\r
- bind(serverSocket,getTcpListenPort(),getAutoBind());\r
- // set non-blocking mode for the listening socket\r
- serverChannel.configureBlocking(false);\r
- // register the ServerSocketChannel with the Selector\r
- serverChannel.register(selector, SelectionKey.OP_ACCEPT);\r
- \r
- }\r
- \r
- public void addEvent(Runnable event) {\r
- if ( selector != null ) {\r
- synchronized (events) {\r
- events.add(event);\r
- }\r
- if ( log.isTraceEnabled() ) log.trace("Adding event to selector:"+event);\r
- selector.wakeup();\r
- }\r
- }\r
-\r
- public void events() {\r
- if ( events.size() == 0 ) return;\r
- synchronized (events) {\r
- Runnable r = null;\r
- while ( (events.size() > 0) && (r = (Runnable)events.removeFirst()) != null ) {\r
- try {\r
- if ( log.isTraceEnabled() ) log.trace("Processing event in selector:"+r);\r
- r.run();\r
- } catch ( Exception x ) {\r
- log.error("",x);\r
- }\r
- }\r
- events.clear();\r
- }\r
- }\r
- \r
- public static void cancelledKey(SelectionKey key) {\r
- ObjectReader reader = (ObjectReader)key.attachment();\r
- if ( reader != null ) {\r
- reader.setCancelled(true);\r
- reader.finish();\r
- }\r
- key.cancel(); \r
- key.attach(null);\r
- try { ((SocketChannel)key.channel()).socket().close(); } catch (IOException e) { if (log.isDebugEnabled()) log.debug("", e); }\r
- try { key.channel().close(); } catch (IOException e) { if (log.isDebugEnabled()) log.debug("", e); }\r
- \r
- }\r
- \r
- protected void socketTimeouts() {\r
- //timeout\r
- Set keys = selector.keys();\r
- long now = System.currentTimeMillis();\r
- for (Iterator iter = keys.iterator(); iter.hasNext(); ) {\r
- SelectionKey key = (SelectionKey) iter.next();\r
- try {\r
-// if (key.interestOps() == SelectionKey.OP_READ) {\r
-// //only timeout sockets that we are waiting for a read from\r
-// ObjectReader ka = (ObjectReader) key.attachment();\r
-// long delta = now - ka.getLastAccess();\r
-// if (delta > (long) getTimeout()) {\r
-// cancelledKey(key);\r
-// }\r
-// }\r
-// else\r
- if ( key.interestOps() == 0 ) {\r
- //check for keys that didn't make it in.\r
- ObjectReader ka = (ObjectReader) key.attachment();\r
- if ( ka != null ) {\r
- long delta = now - ka.getLastAccess();\r
- if (delta > (long) getTimeout() && (!ka.isAccessed())) {\r
- log.warn("Channel key is registered, but has had no interest ops for the last "+getTimeout()+" ms. (cancelled:"+ka.isCancelled()+"):"+key+" last access:"+new java.sql.Timestamp(ka.getLastAccess()));\r
-// System.out.println("Interest:"+key.interestOps());\r
-// System.out.println("Ready Ops:"+key.readyOps());\r
-// System.out.println("Valid:"+key.isValid());\r
- ka.setLastAccess(now);\r
- //key.interestOps(SelectionKey.OP_READ);\r
- }//end if\r
- } else {\r
- cancelledKey(key);\r
- }//end if\r
- }//end if\r
- }catch ( CancelledKeyException ckx ) {\r
- cancelledKey(key);\r
- }\r
- }\r
- }\r
-\r
-\r
- /**\r
- * get data from channel and store in byte array\r
- * send it to cluster\r
- * @throws IOException\r
- * @throws java.nio.channels.ClosedChannelException\r
- */\r
- protected void listen() throws Exception {\r
- if (doListen()) {\r
- log.warn("ServerSocketChannel already started");\r
- return;\r
- }\r
- \r
- setListen(true);\r
-\r
- while (doListen() && selector != null) {\r
- // this may block for a long time, upon return the\r
- // selected set contains keys of the ready channels\r
- try {\r
- events();\r
- socketTimeouts();\r
- int n = selector.select(getTcpSelectorTimeout());\r
- if (n == 0) {\r
- //there is a good chance that we got here\r
- //because the TcpReplicationThread called\r
- //selector wakeup().\r
- //if that happens, we must ensure that that\r
- //thread has enough time to call interestOps\r
-// synchronized (interestOpsMutex) {\r
- //if we got the lock, means there are no\r
- //keys trying to register for the\r
- //interestOps method\r
-// }\r
- continue; // nothing to do\r
- }\r
- // get an iterator over the set of selected keys\r
- Iterator it = selector.selectedKeys().iterator();\r
- // look at each key in the selected set\r
- while (it.hasNext()) {\r
- SelectionKey key = (SelectionKey) it.next();\r
- // Is a new connection coming in?\r
- if (key.isAcceptable()) {\r
- ServerSocketChannel server = (ServerSocketChannel) key.channel();\r
- SocketChannel channel = server.accept();\r
- channel.socket().setReceiveBufferSize(getRxBufSize());\r
- channel.socket().setSendBufferSize(getTxBufSize());\r
- channel.socket().setTcpNoDelay(getTcpNoDelay());\r
- channel.socket().setKeepAlive(getSoKeepAlive());\r
- channel.socket().setOOBInline(getOoBInline());\r
- channel.socket().setReuseAddress(getSoReuseAddress());\r
- channel.socket().setSoLinger(getSoLingerOn(),getSoLingerTime());\r
- channel.socket().setTrafficClass(getSoTrafficClass());\r
- channel.socket().setSoTimeout(getTimeout());\r
- Object attach = new ObjectReader(channel);\r
- registerChannel(selector,\r
- channel,\r
- SelectionKey.OP_READ,\r
- attach);\r
- }\r
- // is there data to read on this channel?\r
- if (key.isReadable()) {\r
- readDataFromSocket(key);\r
- } else {\r
- key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));\r
- }\r
-\r
- // remove key from selected set, it's been handled\r
- it.remove();\r
- }\r
- } catch (java.nio.channels.ClosedSelectorException cse) {\r
- // ignore is normal at shutdown or stop listen socket\r
- } catch (java.nio.channels.CancelledKeyException nx) {\r
- log.warn("Replication client disconnected, error when polling key. Ignoring client.");\r
- } catch (Throwable x) {\r
- try {\r
- log.error("Unable to process request in NioReceiver", x);\r
- }catch ( Throwable tx ) {\r
- //in case an out of memory error, will affect the logging framework as well\r
- tx.printStackTrace();\r
- }\r
- }\r
-\r
- }\r
- serverChannel.close();\r
- if (selector != null)\r
- selector.close();\r
- }\r
-\r
- \r
-\r
- /**\r
- * Close Selector.\r
- *\r
- * @see org.apache.catalina.tribes.transport.ClusterReceiverBase#stopListening()\r
- */\r
- protected void stopListening() {\r
- // Bugzilla 37529: http://issues.apache.org/bugzilla/show_bug.cgi?id=37529\r
- setListen(false);\r
- if (selector != null) {\r
- try {\r
- for (int i = 0; i < getMaxThreads(); i++) {\r
- selector.wakeup();\r
- }\r
- selector.close();\r
- } catch (Exception x) {\r
- log.error("Unable to close cluster receiver selector.", x);\r
- } finally {\r
- selector = null;\r
- }\r
- }\r
- }\r
-\r
- // ----------------------------------------------------------\r
-\r
- /**\r
- * Register the given channel with the given selector for\r
- * the given operations of interest\r
- */\r
- protected void registerChannel(Selector selector,\r
- SelectableChannel channel,\r
- int ops,\r
- Object attach) throws Exception {\r
- if (channel == null)return; // could happen\r
- // set the new channel non-blocking\r
- channel.configureBlocking(false);\r
- // register it with the selector\r
- channel.register(selector, ops, attach);\r
- }\r
-\r
- /**\r
- * Start thread and listen\r
- */\r
- public void run() {\r
- try {\r
- listen();\r
- } catch (Exception x) {\r
- log.error("Unable to run replication listener.", x);\r
- }\r
- }\r
-\r
- // ----------------------------------------------------------\r
-\r
- /**\r
- * Sample data handler method for a channel with data ready to read.\r
- * @param key A SelectionKey object associated with a channel\r
- * determined by the selector to be ready for reading. If the\r
- * channel returns an EOF condition, it is closed here, which\r
- * automatically invalidates the associated key. The selector\r
- * will then de-register the channel on the next select call.\r
- */\r
- protected void readDataFromSocket(SelectionKey key) throws Exception {\r
- NioReplicationThread worker = (NioReplicationThread) getPool().getWorker();\r
- if (worker == null) {\r
- // No threads available, do nothing, the selection\r
- // loop will keep calling this method until a\r
- // thread becomes available, the thread pool itself has a waiting mechanism\r
- // so we will not wait here.\r
- if (log.isDebugEnabled())\r
- log.debug("No TcpReplicationThread available");\r
- } else {\r
- // invoking this wakes up the worker thread then returns\r
- worker.serviceChannel(key);\r
- }\r
- }\r
-\r
-\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport.nio;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+
+import org.apache.catalina.tribes.ChannelReceiver;
+import org.apache.catalina.tribes.io.ListenCallback;
+import org.apache.catalina.tribes.io.ObjectReader;
+import org.apache.catalina.tribes.transport.Constants;
+import org.apache.catalina.tribes.transport.ReceiverBase;
+import org.apache.catalina.tribes.transport.ThreadPool;
+import org.apache.catalina.tribes.transport.WorkerThread;
+import org.apache.catalina.tribes.util.StringManager;
+import java.util.LinkedList;
+import java.util.Set;
+import java.nio.channels.CancelledKeyException;
+
+/**
+ * @author Filip Hanik
+ * @version $Revision$ $Date$
+ */
+public class NioReceiver extends ReceiverBase implements Runnable, ChannelReceiver, ListenCallback {
+
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(NioReceiver.class);
+
+ /**
+ * The string manager for this package.
+ */
+ protected StringManager sm = StringManager.getManager(Constants.Package);
+
+ /**
+ * The descriptive information about this implementation.
+ */
+ private static final String info = "NioReceiver/1.0";
+
+ private Selector selector = null;
+ private ServerSocketChannel serverChannel = null;
+
+ protected LinkedList events = new LinkedList();
+// private Object interestOpsMutex = new Object();
+
+ public NioReceiver() {
+ }
+
+ /**
+ * Return descriptive information about this implementation and the
+ * corresponding version number, in the format
+ * <code><description>/<version></code>.
+ */
+ public String getInfo() {
+ return (info);
+ }
+
+// public Object getInterestOpsMutex() {
+// return interestOpsMutex;
+// }
+
+ public void stop() {
+ this.stopListening();
+ }
+
+ /**
+ * start cluster receiver
+ * @throws Exception
+ * @see org.apache.catalina.tribes.ClusterReceiver#start()
+ */
+ public void start() throws IOException {
+ try {
+// setPool(new ThreadPool(interestOpsMutex, getMaxThreads(),getMinThreads(),this));
+ setPool(new ThreadPool(getMaxThreads(),getMinThreads(),this));
+ } catch (Exception x) {
+ log.fatal("ThreadPool can initilzed. Listener not started", x);
+ if ( x instanceof IOException ) throw (IOException)x;
+ else throw new IOException(x.getMessage());
+ }
+ try {
+ getBind();
+ bind();
+ Thread t = new Thread(this, "NioReceiver");
+ t.setDaemon(true);
+ t.start();
+ } catch (Exception x) {
+ log.fatal("Unable to start cluster receiver", x);
+ if ( x instanceof IOException ) throw (IOException)x;
+ else throw new IOException(x.getMessage());
+ }
+ }
+
+ public WorkerThread getWorkerThread() {
+ NioReplicationThread thread = new NioReplicationThread(this,this);
+ thread.setUseBufferPool(this.getUseBufferPool());
+ thread.setRxBufSize(getRxBufSize());
+ thread.setOptions(getWorkerThreadOptions());
+ return thread;
+ }
+
+
+
+ protected void bind() throws IOException {
+ // allocate an unbound server socket channel
+ serverChannel = ServerSocketChannel.open();
+ // Get the associated ServerSocket to bind it with
+ ServerSocket serverSocket = serverChannel.socket();
+ // create a new Selector for use below
+ selector = Selector.open();
+ // set the port the server channel will listen to
+ //serverSocket.bind(new InetSocketAddress(getBind(), getTcpListenPort()));
+ bind(serverSocket,getTcpListenPort(),getAutoBind());
+ // set non-blocking mode for the listening socket
+ serverChannel.configureBlocking(false);
+ // register the ServerSocketChannel with the Selector
+ serverChannel.register(selector, SelectionKey.OP_ACCEPT);
+
+ }
+
+ public void addEvent(Runnable event) {
+ if ( selector != null ) {
+ synchronized (events) {
+ events.add(event);
+ }
+ if ( log.isTraceEnabled() ) log.trace("Adding event to selector:"+event);
+ selector.wakeup();
+ }
+ }
+
+ public void events() {
+ if ( events.size() == 0 ) return;
+ synchronized (events) {
+ Runnable r = null;
+ while ( (events.size() > 0) && (r = (Runnable)events.removeFirst()) != null ) {
+ try {
+ if ( log.isTraceEnabled() ) log.trace("Processing event in selector:"+r);
+ r.run();
+ } catch ( Exception x ) {
+ log.error("",x);
+ }
+ }
+ events.clear();
+ }
+ }
+
+ public static void cancelledKey(SelectionKey key) {
+ ObjectReader reader = (ObjectReader)key.attachment();
+ if ( reader != null ) {
+ reader.setCancelled(true);
+ reader.finish();
+ }
+ key.cancel();
+ key.attach(null);
+ try { ((SocketChannel)key.channel()).socket().close(); } catch (IOException e) { if (log.isDebugEnabled()) log.debug("", e); }
+ try { key.channel().close(); } catch (IOException e) { if (log.isDebugEnabled()) log.debug("", e); }
+
+ }
+
+ protected void socketTimeouts() {
+ //timeout
+ Set keys = selector.keys();
+ long now = System.currentTimeMillis();
+ for (Iterator iter = keys.iterator(); iter.hasNext(); ) {
+ SelectionKey key = (SelectionKey) iter.next();
+ try {
+// if (key.interestOps() == SelectionKey.OP_READ) {
+// //only timeout sockets that we are waiting for a read from
+// ObjectReader ka = (ObjectReader) key.attachment();
+// long delta = now - ka.getLastAccess();
+// if (delta > (long) getTimeout()) {
+// cancelledKey(key);
+// }
+// }
+// else
+ if ( key.interestOps() == 0 ) {
+ //check for keys that didn't make it in.
+ ObjectReader ka = (ObjectReader) key.attachment();
+ if ( ka != null ) {
+ long delta = now - ka.getLastAccess();
+ if (delta > (long) getTimeout() && (!ka.isAccessed())) {
+ log.warn("Channel key is registered, but has had no interest ops for the last "+getTimeout()+" ms. (cancelled:"+ka.isCancelled()+"):"+key+" last access:"+new java.sql.Timestamp(ka.getLastAccess()));
+// System.out.println("Interest:"+key.interestOps());
+// System.out.println("Ready Ops:"+key.readyOps());
+// System.out.println("Valid:"+key.isValid());
+ ka.setLastAccess(now);
+ //key.interestOps(SelectionKey.OP_READ);
+ }//end if
+ } else {
+ cancelledKey(key);
+ }//end if
+ }//end if
+ }catch ( CancelledKeyException ckx ) {
+ cancelledKey(key);
+ }
+ }
+ }
+
+
+ /**
+ * get data from channel and store in byte array
+ * send it to cluster
+ * @throws IOException
+ * @throws java.nio.channels.ClosedChannelException
+ */
+ protected void listen() throws Exception {
+ if (doListen()) {
+ log.warn("ServerSocketChannel already started");
+ return;
+ }
+
+ setListen(true);
+
+ while (doListen() && selector != null) {
+ // this may block for a long time, upon return the
+ // selected set contains keys of the ready channels
+ try {
+ events();
+ socketTimeouts();
+ int n = selector.select(getTcpSelectorTimeout());
+ if (n == 0) {
+ //there is a good chance that we got here
+ //because the TcpReplicationThread called
+ //selector wakeup().
+ //if that happens, we must ensure that that
+ //thread has enough time to call interestOps
+// synchronized (interestOpsMutex) {
+ //if we got the lock, means there are no
+ //keys trying to register for the
+ //interestOps method
+// }
+ continue; // nothing to do
+ }
+ // get an iterator over the set of selected keys
+ Iterator it = selector.selectedKeys().iterator();
+ // look at each key in the selected set
+ while (it.hasNext()) {
+ SelectionKey key = (SelectionKey) it.next();
+ // Is a new connection coming in?
+ if (key.isAcceptable()) {
+ ServerSocketChannel server = (ServerSocketChannel) key.channel();
+ SocketChannel channel = server.accept();
+ channel.socket().setReceiveBufferSize(getRxBufSize());
+ channel.socket().setSendBufferSize(getTxBufSize());
+ channel.socket().setTcpNoDelay(getTcpNoDelay());
+ channel.socket().setKeepAlive(getSoKeepAlive());
+ channel.socket().setOOBInline(getOoBInline());
+ channel.socket().setReuseAddress(getSoReuseAddress());
+ channel.socket().setSoLinger(getSoLingerOn(),getSoLingerTime());
+ channel.socket().setTrafficClass(getSoTrafficClass());
+ channel.socket().setSoTimeout(getTimeout());
+ Object attach = new ObjectReader(channel);
+ registerChannel(selector,
+ channel,
+ SelectionKey.OP_READ,
+ attach);
+ }
+ // is there data to read on this channel?
+ if (key.isReadable()) {
+ readDataFromSocket(key);
+ } else {
+ key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
+ }
+
+ // remove key from selected set, it's been handled
+ it.remove();
+ }
+ } catch (java.nio.channels.ClosedSelectorException cse) {
+ // ignore is normal at shutdown or stop listen socket
+ } catch (java.nio.channels.CancelledKeyException nx) {
+ log.warn("Replication client disconnected, error when polling key. Ignoring client.");
+ } catch (Throwable x) {
+ try {
+ log.error("Unable to process request in NioReceiver", x);
+ }catch ( Throwable tx ) {
+ //in case an out of memory error, will affect the logging framework as well
+ tx.printStackTrace();
+ }
+ }
+
+ }
+ serverChannel.close();
+ if (selector != null)
+ selector.close();
+ }
+
+
+
+ /**
+ * Close Selector.
+ *
+ * @see org.apache.catalina.tribes.transport.ClusterReceiverBase#stopListening()
+ */
+ protected void stopListening() {
+ // Bugzilla 37529: http://issues.apache.org/bugzilla/show_bug.cgi?id=37529
+ setListen(false);
+ if (selector != null) {
+ try {
+ for (int i = 0; i < getMaxThreads(); i++) {
+ selector.wakeup();
+ }
+ selector.close();
+ } catch (Exception x) {
+ log.error("Unable to close cluster receiver selector.", x);
+ } finally {
+ selector = null;
+ }
+ }
+ }
+
+ // ----------------------------------------------------------
+
+ /**
+ * Register the given channel with the given selector for
+ * the given operations of interest
+ */
+ protected void registerChannel(Selector selector,
+ SelectableChannel channel,
+ int ops,
+ Object attach) throws Exception {
+ if (channel == null)return; // could happen
+ // set the new channel non-blocking
+ channel.configureBlocking(false);
+ // register it with the selector
+ channel.register(selector, ops, attach);
+ }
+
+ /**
+ * Start thread and listen
+ */
+ public void run() {
+ try {
+ listen();
+ } catch (Exception x) {
+ log.error("Unable to run replication listener.", x);
+ }
+ }
+
+ // ----------------------------------------------------------
+
+ /**
+ * Sample data handler method for a channel with data ready to read.
+ * @param key A SelectionKey object associated with a channel
+ * determined by the selector to be ready for reading. If the
+ * channel returns an EOF condition, it is closed here, which
+ * automatically invalidates the associated key. The selector
+ * will then de-register the channel on the next select call.
+ */
+ protected void readDataFromSocket(SelectionKey key) throws Exception {
+ NioReplicationThread worker = (NioReplicationThread) getPool().getWorker();
+ if (worker == null) {
+ // No threads available, do nothing, the selection
+ // loop will keep calling this method until a
+ // thread becomes available, the thread pool itself has a waiting mechanism
+ // so we will not wait here.
+ if (log.isDebugEnabled())
+ log.debug("No TcpReplicationThread available");
+ } else {
+ // invoking this wakes up the worker thread then returns
+ worker.serviceChannel(key);
+ }
+ }
+
+
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport.nio;\r
-import java.io.IOException;\r
-import java.nio.ByteBuffer;\r
-import java.nio.channels.SelectionKey;\r
-import java.nio.channels.SocketChannel;\r
-\r
-import org.apache.catalina.tribes.io.ObjectReader;\r
-import org.apache.catalina.tribes.transport.Constants;\r
-import org.apache.catalina.tribes.transport.WorkerThread;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.io.ListenCallback;\r
-import org.apache.catalina.tribes.io.ChannelData;\r
-import org.apache.catalina.tribes.io.BufferPool;\r
-import java.nio.channels.CancelledKeyException;\r
-import org.apache.catalina.tribes.UniqueId;\r
-import org.apache.catalina.tribes.RemoteProcessException;\r
-import org.apache.catalina.tribes.util.Logs;\r
-\r
-/**\r
- * A worker thread class which can drain channels and echo-back the input. Each\r
- * instance is constructed with a reference to the owning thread pool object.\r
- * When started, the thread loops forever waiting to be awakened to service the\r
- * channel associated with a SelectionKey object. The worker is tasked by\r
- * calling its serviceChannel() method with a SelectionKey object. The\r
- * serviceChannel() method stores the key reference in the thread object then\r
- * calls notify() to wake it up. When the channel has been drained, the worker\r
- * thread returns itself to its parent pool.\r
- * \r
- * @author Filip Hanik\r
- * \r
- * @version $Revision: 378050 $, $Date: 2006-02-15 12:30:02 -0600 (Wed, 15 Feb 2006) $\r
- */\r
-public class NioReplicationThread extends WorkerThread {\r
- \r
- private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( NioReplicationThread.class );\r
- \r
- private ByteBuffer buffer = null;\r
- private SelectionKey key;\r
- private int rxBufSize;\r
- private NioReceiver receiver;\r
- public NioReplicationThread (ListenCallback callback, NioReceiver receiver)\r
- {\r
- super(callback);\r
- this.receiver = receiver;\r
- }\r
-\r
- // loop forever waiting for work to do\r
- public synchronized void run() { \r
- this.notify();\r
- if ( (getOptions() & OPTION_DIRECT_BUFFER) == OPTION_DIRECT_BUFFER ) {\r
- buffer = ByteBuffer.allocateDirect(getRxBufSize());\r
- }else {\r
- buffer = ByteBuffer.allocate (getRxBufSize());\r
- }\r
- while (isDoRun()) {\r
- try {\r
- // sleep and release object lock\r
- this.wait();\r
- } catch (InterruptedException e) {\r
- if(log.isInfoEnabled()) log.info("TCP worker thread interrupted in cluster",e);\r
- // clear interrupt status\r
- Thread.interrupted();\r
- }\r
- if (key == null) {\r
- continue; // just in case\r
- }\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Servicing key:"+key);\r
-\r
- try {\r
- ObjectReader reader = (ObjectReader)key.attachment();\r
- if ( reader == null ) {\r
- if ( log.isTraceEnabled() ) \r
- log.trace("No object reader, cancelling:"+key);\r
- cancelKey(key);\r
- } else {\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Draining channel:"+key);\r
-\r
- drainChannel(key, reader);\r
- }\r
- } catch (Exception e) {\r
- //this is common, since the sockets on the other\r
- //end expire after a certain time.\r
- if ( e instanceof CancelledKeyException ) {\r
- //do nothing\r
- } else if ( e instanceof IOException ) {\r
- //dont spew out stack traces for IO exceptions unless debug is enabled.\r
- if (log.isDebugEnabled()) log.debug ("IOException in replication worker, unable to drain channel. Probable cause: Keep alive socket closed["+e.getMessage()+"].", e);\r
- else log.warn ("IOException in replication worker, unable to drain channel. Probable cause: Keep alive socket closed["+e.getMessage()+"].");\r
- } else if ( log.isErrorEnabled() ) {\r
- //this is a real error, log it.\r
- log.error("Exception caught in TcpReplicationThread.drainChannel.",e);\r
- } \r
- cancelKey(key);\r
- } finally {\r
- \r
- }\r
- key = null;\r
- // done, ready for more, return to pool\r
- getPool().returnWorker (this);\r
- }\r
- }\r
-\r
- /**\r
- * Called to initiate a unit of work by this worker thread\r
- * on the provided SelectionKey object. This method is\r
- * synchronized, as is the run() method, so only one key\r
- * can be serviced at a given time.\r
- * Before waking the worker thread, and before returning\r
- * to the main selection loop, this key's interest set is\r
- * updated to remove OP_READ. This will cause the selector\r
- * to ignore read-readiness for this channel while the\r
- * worker thread is servicing it.\r
- */\r
- public synchronized void serviceChannel (SelectionKey key) {\r
- if ( log.isTraceEnabled() ) \r
- log.trace("About to service key:"+key);\r
- ObjectReader reader = (ObjectReader)key.attachment();\r
- if ( reader != null ) reader.setLastAccess(System.currentTimeMillis());\r
- this.key = key;\r
- key.interestOps (key.interestOps() & (~SelectionKey.OP_READ));\r
- key.interestOps (key.interestOps() & (~SelectionKey.OP_WRITE));\r
- this.notify(); // awaken the thread\r
- }\r
-\r
- /**\r
- * The actual code which drains the channel associated with\r
- * the given key. This method assumes the key has been\r
- * modified prior to invocation to turn off selection\r
- * interest in OP_READ. When this method completes it\r
- * re-enables OP_READ and calls wakeup() on the selector\r
- * so the selector will resume watching this channel.\r
- */\r
- protected void drainChannel (final SelectionKey key, ObjectReader reader) throws Exception {\r
- reader.setLastAccess(System.currentTimeMillis());\r
- reader.access();\r
- SocketChannel channel = (SocketChannel) key.channel();\r
- int count;\r
- buffer.clear(); // make buffer empty\r
-\r
- // loop while data available, channel is non-blocking\r
- while ((count = channel.read (buffer)) > 0) {\r
- buffer.flip(); // make buffer readable\r
- if ( buffer.hasArray() ) \r
- reader.append(buffer.array(),0,count,false);\r
- else \r
- reader.append(buffer,count,false);\r
- buffer.clear(); // make buffer empty\r
- //do we have at least one package?\r
- if ( reader.hasPackage() ) break;\r
- }\r
-\r
- int pkgcnt = reader.count();\r
- \r
- if (count < 0 && pkgcnt == 0 ) {\r
- //end of stream, and no more packages to process\r
- remoteEof(key);\r
- return;\r
- }\r
- \r
- ChannelMessage[] msgs = pkgcnt == 0? ChannelData.EMPTY_DATA_ARRAY : reader.execute();\r
- \r
- registerForRead(key,reader);//register to read new data, before we send it off to avoid dead locks\r
- \r
- for ( int i=0; i<msgs.length; i++ ) {\r
- /**\r
- * Use send ack here if you want to ack the request to the remote \r
- * server before completing the request\r
- * This is considered an asynchronized request\r
- */\r
- if (ChannelData.sendAckAsync(msgs[i].getOptions())) sendAck(key,channel,Constants.ACK_COMMAND);\r
- try {\r
- if ( Logs.MESSAGES.isTraceEnabled() ) {\r
- try {\r
- Logs.MESSAGES.trace("NioReplicationThread - Received msg:" + new UniqueId(msgs[i].getUniqueId()) + " at " + new java.sql.Timestamp(System.currentTimeMillis()));\r
- }catch ( Throwable t ) {}\r
- }\r
- //process the message\r
- getCallback().messageDataReceived(msgs[i]);\r
- /**\r
- * Use send ack here if you want the request to complete on this \r
- * server before sending the ack to the remote server\r
- * This is considered a synchronized request\r
- */\r
- if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.ACK_COMMAND);\r
- }catch ( RemoteProcessException e ) {\r
- if ( log.isDebugEnabled() ) log.error("Processing of cluster message failed.",e);\r
- if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.FAIL_ACK_COMMAND);\r
- }catch ( Exception e ) {\r
- log.error("Processing of cluster message failed.",e);\r
- if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.FAIL_ACK_COMMAND);\r
- }\r
- if ( getUseBufferPool() ) {\r
- BufferPool.getBufferPool().returnBuffer(msgs[i].getMessage());\r
- msgs[i].setMessage(null);\r
- }\r
- } \r
- \r
- if (count < 0) {\r
- remoteEof(key);\r
- return;\r
- }\r
- }\r
-\r
- private void remoteEof(SelectionKey key) {\r
- // close channel on EOF, invalidates the key\r
- if ( log.isDebugEnabled() ) log.debug("Channel closed on the remote end, disconnecting");\r
- cancelKey(key);\r
- }\r
-\r
- protected void registerForRead(final SelectionKey key, ObjectReader reader) {\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Adding key for read event:"+key);\r
- reader.finish();\r
- //register our OP_READ interest\r
- Runnable r = new Runnable() {\r
- public void run() {\r
- try {\r
- if (key.isValid()) {\r
- // cycle the selector so this key is active again\r
- key.selector().wakeup();\r
- // resume interest in OP_READ, OP_WRITE\r
- int resumeOps = key.interestOps() | SelectionKey.OP_READ;\r
- key.interestOps(resumeOps);\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Registering key for read:"+key);\r
- }\r
- } catch (CancelledKeyException ckx ) {\r
- NioReceiver.cancelledKey(key);\r
- if ( log.isTraceEnabled() ) \r
- log.trace("CKX Cancelling key:"+key);\r
-\r
- } catch (Exception x) {\r
- log.error("Error registering key for read:"+key,x);\r
- }\r
- }\r
- };\r
- receiver.addEvent(r);\r
- }\r
-\r
- private void cancelKey(final SelectionKey key) {\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Adding key for cancel event:"+key);\r
-\r
- ObjectReader reader = (ObjectReader)key.attachment();\r
- if ( reader != null ) {\r
- reader.setCancelled(true);\r
- reader.finish();\r
- }\r
- Runnable cx = new Runnable() {\r
- public void run() {\r
- if ( log.isTraceEnabled() ) \r
- log.trace("Cancelling key:"+key);\r
-\r
- NioReceiver.cancelledKey(key);\r
- }\r
- };\r
- receiver.addEvent(cx);\r
- }\r
- \r
- \r
-\r
-\r
-\r
- /**\r
- * send a reply-acknowledgement (6,2,3)\r
- * @param key\r
- * @param channel\r
- */\r
- protected void sendAck(SelectionKey key, SocketChannel channel, byte[] command) {\r
- \r
- try {\r
- ByteBuffer buf = ByteBuffer.wrap(command);\r
- int total = 0;\r
- while ( total < command.length ) {\r
- total += channel.write(buf);\r
- }\r
- if (log.isTraceEnabled()) {\r
- log.trace("ACK sent to " + channel.socket().getPort());\r
- }\r
- } catch ( java.io.IOException x ) {\r
- log.warn("Unable to send ACK back through channel, channel disconnected?: "+x.getMessage());\r
- }\r
- }\r
-\r
- public void setRxBufSize(int rxBufSize) {\r
- this.rxBufSize = rxBufSize;\r
- }\r
-\r
- public int getRxBufSize() {\r
- return rxBufSize;\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.tribes.transport.nio;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+
+import org.apache.catalina.tribes.io.ObjectReader;
+import org.apache.catalina.tribes.transport.Constants;
+import org.apache.catalina.tribes.transport.WorkerThread;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.io.ListenCallback;
+import org.apache.catalina.tribes.io.ChannelData;
+import org.apache.catalina.tribes.io.BufferPool;
+import java.nio.channels.CancelledKeyException;
+import org.apache.catalina.tribes.UniqueId;
+import org.apache.catalina.tribes.RemoteProcessException;
+import org.apache.catalina.tribes.util.Logs;
+
+/**
+ * A worker thread class which can drain channels and echo-back the input. Each
+ * instance is constructed with a reference to the owning thread pool object.
+ * When started, the thread loops forever waiting to be awakened to service the
+ * channel associated with a SelectionKey object. The worker is tasked by
+ * calling its serviceChannel() method with a SelectionKey object. The
+ * serviceChannel() method stores the key reference in the thread object then
+ * calls notify() to wake it up. When the channel has been drained, the worker
+ * thread returns itself to its parent pool.
+ *
+ * @author Filip Hanik
+ *
+ * @version $Revision$, $Date$
+ */
+public class NioReplicationThread extends WorkerThread {
+
+ private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog( NioReplicationThread.class );
+
+ private ByteBuffer buffer = null;
+ private SelectionKey key;
+ private int rxBufSize;
+ private NioReceiver receiver;
+ public NioReplicationThread (ListenCallback callback, NioReceiver receiver)
+ {
+ super(callback);
+ this.receiver = receiver;
+ }
+
+ // loop forever waiting for work to do
+ public synchronized void run() {
+ this.notify();
+ if ( (getOptions() & OPTION_DIRECT_BUFFER) == OPTION_DIRECT_BUFFER ) {
+ buffer = ByteBuffer.allocateDirect(getRxBufSize());
+ }else {
+ buffer = ByteBuffer.allocate (getRxBufSize());
+ }
+ while (isDoRun()) {
+ try {
+ // sleep and release object lock
+ this.wait();
+ } catch (InterruptedException e) {
+ if(log.isInfoEnabled()) log.info("TCP worker thread interrupted in cluster",e);
+ // clear interrupt status
+ Thread.interrupted();
+ }
+ if (key == null) {
+ continue; // just in case
+ }
+ if ( log.isTraceEnabled() )
+ log.trace("Servicing key:"+key);
+
+ try {
+ ObjectReader reader = (ObjectReader)key.attachment();
+ if ( reader == null ) {
+ if ( log.isTraceEnabled() )
+ log.trace("No object reader, cancelling:"+key);
+ cancelKey(key);
+ } else {
+ if ( log.isTraceEnabled() )
+ log.trace("Draining channel:"+key);
+
+ drainChannel(key, reader);
+ }
+ } catch (Exception e) {
+ //this is common, since the sockets on the other
+ //end expire after a certain time.
+ if ( e instanceof CancelledKeyException ) {
+ //do nothing
+ } else if ( e instanceof IOException ) {
+ //dont spew out stack traces for IO exceptions unless debug is enabled.
+ if (log.isDebugEnabled()) log.debug ("IOException in replication worker, unable to drain channel. Probable cause: Keep alive socket closed["+e.getMessage()+"].", e);
+ else log.warn ("IOException in replication worker, unable to drain channel. Probable cause: Keep alive socket closed["+e.getMessage()+"].");
+ } else if ( log.isErrorEnabled() ) {
+ //this is a real error, log it.
+ log.error("Exception caught in TcpReplicationThread.drainChannel.",e);
+ }
+ cancelKey(key);
+ } finally {
+
+ }
+ key = null;
+ // done, ready for more, return to pool
+ getPool().returnWorker (this);
+ }
+ }
+
+ /**
+ * Called to initiate a unit of work by this worker thread
+ * on the provided SelectionKey object. This method is
+ * synchronized, as is the run() method, so only one key
+ * can be serviced at a given time.
+ * Before waking the worker thread, and before returning
+ * to the main selection loop, this key's interest set is
+ * updated to remove OP_READ. This will cause the selector
+ * to ignore read-readiness for this channel while the
+ * worker thread is servicing it.
+ */
+ public synchronized void serviceChannel (SelectionKey key) {
+ if ( log.isTraceEnabled() )
+ log.trace("About to service key:"+key);
+ ObjectReader reader = (ObjectReader)key.attachment();
+ if ( reader != null ) reader.setLastAccess(System.currentTimeMillis());
+ this.key = key;
+ key.interestOps (key.interestOps() & (~SelectionKey.OP_READ));
+ key.interestOps (key.interestOps() & (~SelectionKey.OP_WRITE));
+ this.notify(); // awaken the thread
+ }
+
+ /**
+ * The actual code which drains the channel associated with
+ * the given key. This method assumes the key has been
+ * modified prior to invocation to turn off selection
+ * interest in OP_READ. When this method completes it
+ * re-enables OP_READ and calls wakeup() on the selector
+ * so the selector will resume watching this channel.
+ */
+ protected void drainChannel (final SelectionKey key, ObjectReader reader) throws Exception {
+ reader.setLastAccess(System.currentTimeMillis());
+ reader.access();
+ SocketChannel channel = (SocketChannel) key.channel();
+ int count;
+ buffer.clear(); // make buffer empty
+
+ // loop while data available, channel is non-blocking
+ while ((count = channel.read (buffer)) > 0) {
+ buffer.flip(); // make buffer readable
+ if ( buffer.hasArray() )
+ reader.append(buffer.array(),0,count,false);
+ else
+ reader.append(buffer,count,false);
+ buffer.clear(); // make buffer empty
+ //do we have at least one package?
+ if ( reader.hasPackage() ) break;
+ }
+
+ int pkgcnt = reader.count();
+
+ if (count < 0 && pkgcnt == 0 ) {
+ //end of stream, and no more packages to process
+ remoteEof(key);
+ return;
+ }
+
+ ChannelMessage[] msgs = pkgcnt == 0? ChannelData.EMPTY_DATA_ARRAY : reader.execute();
+
+ registerForRead(key,reader);//register to read new data, before we send it off to avoid dead locks
+
+ for ( int i=0; i<msgs.length; i++ ) {
+ /**
+ * Use send ack here if you want to ack the request to the remote
+ * server before completing the request
+ * This is considered an asynchronized request
+ */
+ if (ChannelData.sendAckAsync(msgs[i].getOptions())) sendAck(key,channel,Constants.ACK_COMMAND);
+ try {
+ if ( Logs.MESSAGES.isTraceEnabled() ) {
+ try {
+ Logs.MESSAGES.trace("NioReplicationThread - Received msg:" + new UniqueId(msgs[i].getUniqueId()) + " at " + new java.sql.Timestamp(System.currentTimeMillis()));
+ }catch ( Throwable t ) {}
+ }
+ //process the message
+ getCallback().messageDataReceived(msgs[i]);
+ /**
+ * Use send ack here if you want the request to complete on this
+ * server before sending the ack to the remote server
+ * This is considered a synchronized request
+ */
+ if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.ACK_COMMAND);
+ }catch ( RemoteProcessException e ) {
+ if ( log.isDebugEnabled() ) log.error("Processing of cluster message failed.",e);
+ if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.FAIL_ACK_COMMAND);
+ }catch ( Exception e ) {
+ log.error("Processing of cluster message failed.",e);
+ if (ChannelData.sendAckSync(msgs[i].getOptions())) sendAck(key,channel,Constants.FAIL_ACK_COMMAND);
+ }
+ if ( getUseBufferPool() ) {
+ BufferPool.getBufferPool().returnBuffer(msgs[i].getMessage());
+ msgs[i].setMessage(null);
+ }
+ }
+
+ if (count < 0) {
+ remoteEof(key);
+ return;
+ }
+ }
+
+ private void remoteEof(SelectionKey key) {
+ // close channel on EOF, invalidates the key
+ if ( log.isDebugEnabled() ) log.debug("Channel closed on the remote end, disconnecting");
+ cancelKey(key);
+ }
+
+ protected void registerForRead(final SelectionKey key, ObjectReader reader) {
+ if ( log.isTraceEnabled() )
+ log.trace("Adding key for read event:"+key);
+ reader.finish();
+ //register our OP_READ interest
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ if (key.isValid()) {
+ // cycle the selector so this key is active again
+ key.selector().wakeup();
+ // resume interest in OP_READ, OP_WRITE
+ int resumeOps = key.interestOps() | SelectionKey.OP_READ;
+ key.interestOps(resumeOps);
+ if ( log.isTraceEnabled() )
+ log.trace("Registering key for read:"+key);
+ }
+ } catch (CancelledKeyException ckx ) {
+ NioReceiver.cancelledKey(key);
+ if ( log.isTraceEnabled() )
+ log.trace("CKX Cancelling key:"+key);
+
+ } catch (Exception x) {
+ log.error("Error registering key for read:"+key,x);
+ }
+ }
+ };
+ receiver.addEvent(r);
+ }
+
+ private void cancelKey(final SelectionKey key) {
+ if ( log.isTraceEnabled() )
+ log.trace("Adding key for cancel event:"+key);
+
+ ObjectReader reader = (ObjectReader)key.attachment();
+ if ( reader != null ) {
+ reader.setCancelled(true);
+ reader.finish();
+ }
+ Runnable cx = new Runnable() {
+ public void run() {
+ if ( log.isTraceEnabled() )
+ log.trace("Cancelling key:"+key);
+
+ NioReceiver.cancelledKey(key);
+ }
+ };
+ receiver.addEvent(cx);
+ }
+
+
+
+
+
+ /**
+ * send a reply-acknowledgement (6,2,3)
+ * @param key
+ * @param channel
+ */
+ protected void sendAck(SelectionKey key, SocketChannel channel, byte[] command) {
+
+ try {
+ ByteBuffer buf = ByteBuffer.wrap(command);
+ int total = 0;
+ while ( total < command.length ) {
+ total += channel.write(buf);
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("ACK sent to " + channel.socket().getPort());
+ }
+ } catch ( java.io.IOException x ) {
+ log.warn("Unable to send ACK back through channel, channel disconnected?: "+x.getMessage());
+ }
+ }
+
+ public void setRxBufSize(int rxBufSize) {
+ this.rxBufSize = rxBufSize;
+ }
+
+ public int getRxBufSize() {
+ return rxBufSize;
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.apache.catalina.tribes.transport.nio;\r
-\r
-import java.io.IOException;\r
-import java.net.InetSocketAddress;\r
-import java.net.UnknownHostException;\r
-import java.nio.ByteBuffer;\r
-import java.nio.channels.SelectionKey;\r
-import java.nio.channels.Selector;\r
-import java.nio.channels.SocketChannel;\r
-import java.util.Arrays;\r
-\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-import org.apache.catalina.tribes.transport.AbstractSender;\r
-import org.apache.catalina.tribes.transport.DataSender;\r
-import org.apache.catalina.tribes.RemoteProcessException;\r
-import java.io.EOFException;\r
-\r
-/**\r
- * This class is NOT thread safe and should never be used with more than one thread at a time\r
- * \r
- * This is a state machine, handled by the process method\r
- * States are:\r
- * - NOT_CONNECTED -> connect() -> CONNECTED\r
- * - CONNECTED -> setMessage() -> READY TO WRITE\r
- * - READY_TO_WRITE -> write() -> READY TO WRITE | READY TO READ\r
- * - READY_TO_READ -> read() -> READY_TO_READ | TRANSFER_COMPLETE\r
- * - TRANSFER_COMPLETE -> CONNECTED\r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class NioSender extends AbstractSender implements DataSender{\r
-\r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(NioSender.class);\r
-\r
- \r
- \r
- protected Selector selector; \r
- protected SocketChannel socketChannel;\r
-\r
- /*\r
- * STATE VARIABLES *\r
- */\r
- protected ByteBuffer readbuf = null;\r
- protected ByteBuffer writebuf = null;\r
- protected byte[] current = null;\r
- protected XByteBuffer ackbuf = new XByteBuffer(128,true);\r
- protected int remaining = 0;\r
- protected boolean complete;\r
- \r
- protected boolean connecting = false;\r
- \r
- public NioSender() {\r
- super();\r
- \r
- }\r
- \r
- /**\r
- * State machine to send data\r
- * @param key SelectionKey\r
- * @return boolean\r
- * @throws IOException\r
- */\r
- public boolean process(SelectionKey key, boolean waitForAck) throws IOException {\r
- int ops = key.readyOps();\r
- key.interestOps(key.interestOps() & ~ops);\r
- //in case disconnect has been called\r
- if ((!isConnected()) && (!connecting)) throw new IOException("Sender has been disconnected, can't selection key.");\r
- if ( !key.isValid() ) throw new IOException("Key is not valid, it must have been cancelled.");\r
- if ( key.isConnectable() ) {\r
- if ( socketChannel.finishConnect() ) {\r
- //we connected, register ourselves for writing\r
- setConnected(true);\r
- connecting = false;\r
- setRequestCount(0);\r
- setConnectTime(System.currentTimeMillis());\r
- socketChannel.socket().setSendBufferSize(getTxBufSize());\r
- socketChannel.socket().setReceiveBufferSize(getRxBufSize());\r
- socketChannel.socket().setSoTimeout((int)getTimeout());\r
- socketChannel.socket().setSoLinger(false,0);\r
- socketChannel.socket().setTcpNoDelay(getTcpNoDelay());\r
- socketChannel.socket().setKeepAlive(getSoKeepAlive());\r
- socketChannel.socket().setReuseAddress(getSoReuseAddress());\r
- socketChannel.socket().setOOBInline(getOoBInline());\r
- socketChannel.socket().setSoLinger(getSoLingerOn(),getSoLingerTime());\r
- socketChannel.socket().setTrafficClass(getSoTrafficClass());\r
- if ( current != null ) key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);\r
- return false;\r
- } else { \r
- //wait for the connection to finish\r
- key.interestOps(key.interestOps() | SelectionKey.OP_CONNECT);\r
- return false;\r
- }//end if\r
- } else if ( key.isWritable() ) {\r
- boolean writecomplete = write(key);\r
- if ( writecomplete ) {\r
- //we are completed, should we read an ack?\r
- if ( waitForAck ) {\r
- //register to read the ack\r
- key.interestOps(key.interestOps() | SelectionKey.OP_READ);\r
- } else {\r
- //if not, we are ready, setMessage will reregister us for another write interest\r
- //do a health check, we have no way of verify a disconnected\r
- //socket since we don't register for OP_READ on waitForAck=false\r
- read(key);//this causes overhead\r
- setRequestCount(getRequestCount()+1);\r
- return true;\r
- }\r
- } else {\r
- //we are not complete, lets write some more\r
- key.interestOps(key.interestOps()|SelectionKey.OP_WRITE);\r
- }//end if\r
- } else if ( key.isReadable() ) {\r
- boolean readcomplete = read(key);\r
- if ( readcomplete ) {\r
- setRequestCount(getRequestCount()+1);\r
- return true;\r
- } else {\r
- key.interestOps(key.interestOps() | SelectionKey.OP_READ);\r
- }//end if\r
- } else {\r
- //unknown state, should never happen\r
- log.warn("Data is in unknown state. readyOps="+ops);\r
- throw new IOException("Data is in unknown state. readyOps="+ops);\r
- }//end if\r
- return false;\r
- }\r
- \r
- \r
-\r
- protected boolean read(SelectionKey key) throws IOException {\r
- //if there is no message here, we are done\r
- if ( current == null ) return true;\r
- int read = socketChannel.read(readbuf);\r
- //end of stream\r
- if ( read == -1 ) throw new IOException("Unable to receive an ack message. EOF on socket channel has been reached.");\r
- //no data read\r
- else if ( read == 0 ) return false;\r
- readbuf.flip();\r
- ackbuf.append(readbuf,read);\r
- readbuf.clear();\r
- if (ackbuf.doesPackageExist() ) {\r
- byte[] ackcmd = ackbuf.extractDataPackage(true).getBytes();\r
- boolean ack = Arrays.equals(ackcmd,org.apache.catalina.tribes.transport.Constants.ACK_DATA);\r
- boolean fack = Arrays.equals(ackcmd,org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA);\r
- if ( fack && getThrowOnFailedAck() ) throw new RemoteProcessException("Received a failed ack:org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA");\r
- return ack || fack;\r
- } else {\r
- return false;\r
- }\r
- }\r
-\r
- \r
- protected boolean write(SelectionKey key) throws IOException {\r
- if ( (!isConnected()) || (this.socketChannel==null)) {\r
- throw new IOException("NioSender is not connected, this should not occur.");\r
- }\r
- if ( current != null ) {\r
- if ( remaining > 0 ) {\r
- //weve written everything, or we are starting a new package\r
- //protect against buffer overwrite\r
- int byteswritten = socketChannel.write(writebuf);\r
- if (byteswritten == -1 ) throw new EOFException();\r
- remaining -= byteswritten;\r
- //if the entire message was written from the buffer\r
- //reset the position counter\r
- if ( remaining < 0 ) {\r
- remaining = 0;\r
- }\r
- }\r
- return (remaining==0);\r
- }\r
- //no message to send, we can consider that complete\r
- return true;\r
- }\r
-\r
- /**\r
- * connect - blocking in this operation\r
- *\r
- * @throws IOException\r
- * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method\r
- */\r
- public synchronized void connect() throws IOException {\r
- if ( connecting ) return;\r
- connecting = true;\r
- if ( isConnected() ) throw new IOException("NioSender is already in connected state.");\r
- if ( readbuf == null ) {\r
- readbuf = getReadBuffer();\r
- } else {\r
- readbuf.clear();\r
- }\r
- if ( writebuf == null ) {\r
- writebuf = getWriteBuffer();\r
- } else {\r
- writebuf.clear();\r
- }\r
- \r
- InetSocketAddress addr = new InetSocketAddress(getAddress(),getPort());\r
- if ( socketChannel != null ) throw new IOException("Socket channel has already been established. Connection might be in progress.");\r
- socketChannel = SocketChannel.open();\r
- socketChannel.configureBlocking(false);\r
- socketChannel.connect(addr);\r
- socketChannel.register(getSelector(),SelectionKey.OP_CONNECT,this);\r
- }\r
- \r
-\r
- /**\r
- * disconnect\r
- *\r
- * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method\r
- */\r
- public void disconnect() {\r
- try {\r
- connecting = false;\r
- setConnected(false);\r
- if ( socketChannel != null ) {\r
- try {\r
- try {socketChannel.socket().close();}catch ( Exception x){}\r
- //error free close, all the way\r
- //try {socket.shutdownOutput();}catch ( Exception x){}\r
- //try {socket.shutdownInput();}catch ( Exception x){}\r
- //try {socket.close();}catch ( Exception x){}\r
- try {socketChannel.close();}catch ( Exception x){}\r
- }finally {\r
- socketChannel = null;\r
- }\r
- }\r
- } catch ( Exception x ) {\r
- log.error("Unable to disconnect NioSender. msg="+x.getMessage());\r
- if ( log.isDebugEnabled() ) log.debug("Unable to disconnect NioSender. msg="+x.getMessage(),x);\r
- } finally {\r
- }\r
-\r
- }\r
- \r
- public void reset() {\r
- if ( isConnected() && readbuf == null) {\r
- readbuf = getReadBuffer();\r
- }\r
- if ( readbuf != null ) readbuf.clear();\r
- if ( writebuf != null ) writebuf.clear();\r
- current = null;\r
- ackbuf.clear();\r
- remaining = 0;\r
- complete = false;\r
- setAttempt(0);\r
- setRequestCount(0);\r
- setConnectTime(-1);\r
- }\r
-\r
- private ByteBuffer getReadBuffer() { \r
- return getBuffer(getRxBufSize());\r
- }\r
- \r
- private ByteBuffer getWriteBuffer() {\r
- return getBuffer(getTxBufSize());\r
- }\r
-\r
- private ByteBuffer getBuffer(int size) {\r
- return (getDirectBuffer()?ByteBuffer.allocateDirect(size):ByteBuffer.allocate(size));\r
- }\r
- \r
- /**\r
- * sendMessage\r
- *\r
- * @param data ChannelMessage\r
- * @throws IOException\r
- * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method\r
- */\r
- public synchronized void setMessage(byte[] data) throws IOException {\r
- setMessage(data,0,data.length);\r
- }\r
-\r
- public synchronized void setMessage(byte[] data,int offset, int length) throws IOException {\r
- if ( data != null ) {\r
- current = data;\r
- remaining = length;\r
- ackbuf.clear();\r
- if ( writebuf != null ) writebuf.clear();\r
- else writebuf = getBuffer(length);\r
- if ( writebuf.capacity() < length ) writebuf = getBuffer(length);\r
- writebuf.put(data,offset,length);\r
- //writebuf.rewind();\r
- //set the limit so that we don't write non wanted data\r
- //writebuf.limit(length);\r
- writebuf.flip();\r
- if (isConnected()) {\r
- socketChannel.register(getSelector(), SelectionKey.OP_WRITE, this);\r
- }\r
- } \r
- }\r
- \r
- public byte[] getMessage() {\r
- return current;\r
- }\r
-\r
-\r
-\r
- public boolean isComplete() {\r
- return complete;\r
- }\r
-\r
- public Selector getSelector() {\r
- return selector;\r
- }\r
-\r
- public void setSelector(Selector selector) {\r
- this.selector = selector;\r
- }\r
-\r
-\r
- public void setComplete(boolean complete) {\r
- this.complete = complete;\r
- }\r
-}
\ No newline at end of file
+/*
+ * 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.apache.catalina.tribes.transport.nio;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.catalina.tribes.transport.AbstractSender;
+import org.apache.catalina.tribes.transport.DataSender;
+import org.apache.catalina.tribes.RemoteProcessException;
+import java.io.EOFException;
+
+/**
+ * This class is NOT thread safe and should never be used with more than one thread at a time
+ *
+ * This is a state machine, handled by the process method
+ * States are:
+ * - NOT_CONNECTED -> connect() -> CONNECTED
+ * - CONNECTED -> setMessage() -> READY TO WRITE
+ * - READY_TO_WRITE -> write() -> READY TO WRITE | READY TO READ
+ * - READY_TO_READ -> read() -> READY_TO_READ | TRANSFER_COMPLETE
+ * - TRANSFER_COMPLETE -> CONNECTED
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class NioSender extends AbstractSender implements DataSender{
+
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(NioSender.class);
+
+
+
+ protected Selector selector;
+ protected SocketChannel socketChannel;
+
+ /*
+ * STATE VARIABLES *
+ */
+ protected ByteBuffer readbuf = null;
+ protected ByteBuffer writebuf = null;
+ protected byte[] current = null;
+ protected XByteBuffer ackbuf = new XByteBuffer(128,true);
+ protected int remaining = 0;
+ protected boolean complete;
+
+ protected boolean connecting = false;
+
+ public NioSender() {
+ super();
+
+ }
+
+ /**
+ * State machine to send data
+ * @param key SelectionKey
+ * @return boolean
+ * @throws IOException
+ */
+ public boolean process(SelectionKey key, boolean waitForAck) throws IOException {
+ int ops = key.readyOps();
+ key.interestOps(key.interestOps() & ~ops);
+ //in case disconnect has been called
+ if ((!isConnected()) && (!connecting)) throw new IOException("Sender has been disconnected, can't selection key.");
+ if ( !key.isValid() ) throw new IOException("Key is not valid, it must have been cancelled.");
+ if ( key.isConnectable() ) {
+ if ( socketChannel.finishConnect() ) {
+ //we connected, register ourselves for writing
+ setConnected(true);
+ connecting = false;
+ setRequestCount(0);
+ setConnectTime(System.currentTimeMillis());
+ socketChannel.socket().setSendBufferSize(getTxBufSize());
+ socketChannel.socket().setReceiveBufferSize(getRxBufSize());
+ socketChannel.socket().setSoTimeout((int)getTimeout());
+ socketChannel.socket().setSoLinger(false,0);
+ socketChannel.socket().setTcpNoDelay(getTcpNoDelay());
+ socketChannel.socket().setKeepAlive(getSoKeepAlive());
+ socketChannel.socket().setReuseAddress(getSoReuseAddress());
+ socketChannel.socket().setOOBInline(getOoBInline());
+ socketChannel.socket().setSoLinger(getSoLingerOn(),getSoLingerTime());
+ socketChannel.socket().setTrafficClass(getSoTrafficClass());
+ if ( current != null ) key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
+ return false;
+ } else {
+ //wait for the connection to finish
+ key.interestOps(key.interestOps() | SelectionKey.OP_CONNECT);
+ return false;
+ }//end if
+ } else if ( key.isWritable() ) {
+ boolean writecomplete = write(key);
+ if ( writecomplete ) {
+ //we are completed, should we read an ack?
+ if ( waitForAck ) {
+ //register to read the ack
+ key.interestOps(key.interestOps() | SelectionKey.OP_READ);
+ } else {
+ //if not, we are ready, setMessage will reregister us for another write interest
+ //do a health check, we have no way of verify a disconnected
+ //socket since we don't register for OP_READ on waitForAck=false
+ read(key);//this causes overhead
+ setRequestCount(getRequestCount()+1);
+ return true;
+ }
+ } else {
+ //we are not complete, lets write some more
+ key.interestOps(key.interestOps()|SelectionKey.OP_WRITE);
+ }//end if
+ } else if ( key.isReadable() ) {
+ boolean readcomplete = read(key);
+ if ( readcomplete ) {
+ setRequestCount(getRequestCount()+1);
+ return true;
+ } else {
+ key.interestOps(key.interestOps() | SelectionKey.OP_READ);
+ }//end if
+ } else {
+ //unknown state, should never happen
+ log.warn("Data is in unknown state. readyOps="+ops);
+ throw new IOException("Data is in unknown state. readyOps="+ops);
+ }//end if
+ return false;
+ }
+
+
+
+ protected boolean read(SelectionKey key) throws IOException {
+ //if there is no message here, we are done
+ if ( current == null ) return true;
+ int read = socketChannel.read(readbuf);
+ //end of stream
+ if ( read == -1 ) throw new IOException("Unable to receive an ack message. EOF on socket channel has been reached.");
+ //no data read
+ else if ( read == 0 ) return false;
+ readbuf.flip();
+ ackbuf.append(readbuf,read);
+ readbuf.clear();
+ if (ackbuf.doesPackageExist() ) {
+ byte[] ackcmd = ackbuf.extractDataPackage(true).getBytes();
+ boolean ack = Arrays.equals(ackcmd,org.apache.catalina.tribes.transport.Constants.ACK_DATA);
+ boolean fack = Arrays.equals(ackcmd,org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA);
+ if ( fack && getThrowOnFailedAck() ) throw new RemoteProcessException("Received a failed ack:org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA");
+ return ack || fack;
+ } else {
+ return false;
+ }
+ }
+
+
+ protected boolean write(SelectionKey key) throws IOException {
+ if ( (!isConnected()) || (this.socketChannel==null)) {
+ throw new IOException("NioSender is not connected, this should not occur.");
+ }
+ if ( current != null ) {
+ if ( remaining > 0 ) {
+ //weve written everything, or we are starting a new package
+ //protect against buffer overwrite
+ int byteswritten = socketChannel.write(writebuf);
+ if (byteswritten == -1 ) throw new EOFException();
+ remaining -= byteswritten;
+ //if the entire message was written from the buffer
+ //reset the position counter
+ if ( remaining < 0 ) {
+ remaining = 0;
+ }
+ }
+ return (remaining==0);
+ }
+ //no message to send, we can consider that complete
+ return true;
+ }
+
+ /**
+ * connect - blocking in this operation
+ *
+ * @throws IOException
+ * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method
+ */
+ public synchronized void connect() throws IOException {
+ if ( connecting ) return;
+ connecting = true;
+ if ( isConnected() ) throw new IOException("NioSender is already in connected state.");
+ if ( readbuf == null ) {
+ readbuf = getReadBuffer();
+ } else {
+ readbuf.clear();
+ }
+ if ( writebuf == null ) {
+ writebuf = getWriteBuffer();
+ } else {
+ writebuf.clear();
+ }
+
+ InetSocketAddress addr = new InetSocketAddress(getAddress(),getPort());
+ if ( socketChannel != null ) throw new IOException("Socket channel has already been established. Connection might be in progress.");
+ socketChannel = SocketChannel.open();
+ socketChannel.configureBlocking(false);
+ socketChannel.connect(addr);
+ socketChannel.register(getSelector(),SelectionKey.OP_CONNECT,this);
+ }
+
+
+ /**
+ * disconnect
+ *
+ * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method
+ */
+ public void disconnect() {
+ try {
+ connecting = false;
+ setConnected(false);
+ if ( socketChannel != null ) {
+ try {
+ try {socketChannel.socket().close();}catch ( Exception x){}
+ //error free close, all the way
+ //try {socket.shutdownOutput();}catch ( Exception x){}
+ //try {socket.shutdownInput();}catch ( Exception x){}
+ //try {socket.close();}catch ( Exception x){}
+ try {socketChannel.close();}catch ( Exception x){}
+ }finally {
+ socketChannel = null;
+ }
+ }
+ } catch ( Exception x ) {
+ log.error("Unable to disconnect NioSender. msg="+x.getMessage());
+ if ( log.isDebugEnabled() ) log.debug("Unable to disconnect NioSender. msg="+x.getMessage(),x);
+ } finally {
+ }
+
+ }
+
+ public void reset() {
+ if ( isConnected() && readbuf == null) {
+ readbuf = getReadBuffer();
+ }
+ if ( readbuf != null ) readbuf.clear();
+ if ( writebuf != null ) writebuf.clear();
+ current = null;
+ ackbuf.clear();
+ remaining = 0;
+ complete = false;
+ setAttempt(0);
+ setRequestCount(0);
+ setConnectTime(-1);
+ }
+
+ private ByteBuffer getReadBuffer() {
+ return getBuffer(getRxBufSize());
+ }
+
+ private ByteBuffer getWriteBuffer() {
+ return getBuffer(getTxBufSize());
+ }
+
+ private ByteBuffer getBuffer(int size) {
+ return (getDirectBuffer()?ByteBuffer.allocateDirect(size):ByteBuffer.allocate(size));
+ }
+
+ /**
+ * sendMessage
+ *
+ * @param data ChannelMessage
+ * @throws IOException
+ * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method
+ */
+ public synchronized void setMessage(byte[] data) throws IOException {
+ setMessage(data,0,data.length);
+ }
+
+ public synchronized void setMessage(byte[] data,int offset, int length) throws IOException {
+ if ( data != null ) {
+ current = data;
+ remaining = length;
+ ackbuf.clear();
+ if ( writebuf != null ) writebuf.clear();
+ else writebuf = getBuffer(length);
+ if ( writebuf.capacity() < length ) writebuf = getBuffer(length);
+ writebuf.put(data,offset,length);
+ //writebuf.rewind();
+ //set the limit so that we don't write non wanted data
+ //writebuf.limit(length);
+ writebuf.flip();
+ if (isConnected()) {
+ socketChannel.register(getSelector(), SelectionKey.OP_WRITE, this);
+ }
+ }
+ }
+
+ public byte[] getMessage() {
+ return current;
+ }
+
+
+
+ public boolean isComplete() {
+ return complete;
+ }
+
+ public Selector getSelector() {
+ return selector;
+ }
+
+ public void setSelector(Selector selector) {
+ this.selector = selector;
+ }
+
+
+ public void setComplete(boolean complete) {
+ this.complete = complete;
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.transport.nio;\r
-\r
-\r
-import java.io.IOException;\r
-import java.nio.channels.SelectionKey;\r
-import java.nio.channels.Selector;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.Map;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.io.ChannelData;\r
-import org.apache.catalina.tribes.io.XByteBuffer;\r
-import org.apache.catalina.tribes.transport.MultiPointSender;\r
-import org.apache.catalina.tribes.transport.SenderState;\r
-import org.apache.catalina.tribes.transport.AbstractSender;\r
-import java.net.UnknownHostException;\r
-import org.apache.catalina.tribes.Channel;\r
-import org.apache.catalina.tribes.group.RpcChannel;\r
-import org.apache.catalina.tribes.util.Logs;\r
-import org.apache.catalina.tribes.UniqueId;\r
-\r
-/**\r
- * <p>Title: </p>\r
- *\r
- * <p>Description: </p>\r
- *\r
- * <p>Copyright: Copyright (c) 2005</p>\r
- *\r
- * <p>Company: </p>\r
- *\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-public class ParallelNioSender extends AbstractSender implements MultiPointSender {\r
- \r
- protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ParallelNioSender.class);\r
- protected long selectTimeout = 5000; //default 5 seconds, same as send timeout\r
- protected Selector selector;\r
- protected HashMap nioSenders = new HashMap();\r
-\r
- public ParallelNioSender() throws IOException {\r
- selector = Selector.open();\r
- setConnected(true);\r
- }\r
- \r
- \r
- public synchronized void sendMessage(Member[] destination, ChannelMessage msg) throws ChannelException {\r
- long start = System.currentTimeMillis();\r
- byte[] data = XByteBuffer.createDataPackage((ChannelData)msg);\r
- NioSender[] senders = setupForSend(destination);\r
- connect(senders);\r
- setData(senders,data);\r
- \r
- int remaining = senders.length;\r
- ChannelException cx = null;\r
- try {\r
- //loop until complete, an error happens, or we timeout\r
- long delta = System.currentTimeMillis() - start;\r
- boolean waitForAck = (Channel.SEND_OPTIONS_USE_ACK & msg.getOptions()) == Channel.SEND_OPTIONS_USE_ACK;\r
- while ( (remaining>0) && (delta<getTimeout()) ) {\r
- try {\r
- remaining -= doLoop(selectTimeout, getMaxRetryAttempts(),waitForAck,msg);\r
- } catch (Exception x ) {\r
- if ( cx == null ) {\r
- if ( x instanceof ChannelException ) cx = (ChannelException)x;\r
- else cx = new ChannelException("Parallel NIO send failed.", x);\r
- } else {\r
- if (x instanceof ChannelException) cx.addFaultyMember( ( (ChannelException) x).getFaultyMembers());\r
- }\r
- }\r
- //bail out if all remaining senders are failing\r
- if ( cx != null && cx.getFaultyMembers().length == remaining ) throw cx;\r
- delta = System.currentTimeMillis() - start;\r
- }\r
- if ( remaining > 0 ) {\r
- //timeout has occured\r
- cx = new ChannelException("Operation has timed out("+getTimeout()+" ms.).");\r
- for (int i=0; i<senders.length; i++ ) {\r
- if (!senders[i].isComplete() ) cx.addFaultyMember(senders[i].getDestination(),null);\r
- }\r
- throw cx;\r
- }\r
- } catch (Exception x ) {\r
- try { this.disconnect(); } catch (Exception ignore) {}\r
- if ( x instanceof ChannelException ) throw (ChannelException)x;\r
- else throw new ChannelException(x);\r
- }\r
- \r
- }\r
- \r
- private int doLoop(long selectTimeOut, int maxAttempts, boolean waitForAck, ChannelMessage msg) throws IOException, ChannelException {\r
- int completed = 0;\r
- int selectedKeys = selector.select(selectTimeOut);\r
- \r
- if (selectedKeys == 0) {\r
- return 0;\r
- }\r
- \r
- Iterator it = selector.selectedKeys().iterator();\r
- while (it.hasNext()) {\r
- SelectionKey sk = (SelectionKey) it.next();\r
- it.remove();\r
- int readyOps = sk.readyOps();\r
- sk.interestOps(sk.interestOps() & ~readyOps);\r
- NioSender sender = (NioSender) sk.attachment();\r
- try {\r
- if (sender.process(sk,waitForAck)) {\r
- completed++;\r
- sender.setComplete(true);\r
- if ( Logs.MESSAGES.isTraceEnabled() ) {\r
- Logs.MESSAGES.trace("ParallelNioSender - Sent msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " to "+sender.getDestination().getName());\r
- }\r
- SenderState.getSenderState(sender.getDestination()).setReady();\r
- }//end if\r
- } catch (Exception x) {\r
- SenderState state = SenderState.getSenderState(sender.getDestination());\r
- int attempt = sender.getAttempt()+1;\r
- boolean retry = (sender.getAttempt() <= maxAttempts && maxAttempts>0);\r
- synchronized (state) {\r
- \r
- //sk.cancel();\r
- if (state.isSuspect()) state.setFailing();\r
- if (state.isReady()) {\r
- state.setSuspect();\r
- if ( retry ) \r
- log.warn("Member send is failing for:" + sender.getDestination().getName() +" ; Setting to suspect and retrying.");\r
- else \r
- log.warn("Member send is failing for:" + sender.getDestination().getName() +" ; Setting to suspect.", x);\r
- } \r
- }\r
- if ( !isConnected() ) {\r
- log.warn("Not retrying send for:" + sender.getDestination().getName() + "; Sender is disconnected.");\r
- ChannelException cx = new ChannelException("Send failed, and sender is disconnected. Not retrying.",x);\r
- cx.addFaultyMember(sender.getDestination(),x);\r
- throw cx;\r
- }\r
- \r
- byte[] data = sender.getMessage();\r
- if ( retry ) {\r
- try { \r
- sender.disconnect(); \r
- sender.connect();\r
- sender.setAttempt(attempt);\r
- sender.setMessage(data);\r
- }catch ( Exception ignore){\r
- state.setFailing();\r
- }\r
- } else {\r
- ChannelException cx = new ChannelException("Send failed, attempt:"+sender.getAttempt()+" max:"+maxAttempts,x);\r
- cx.addFaultyMember(sender.getDestination(),x);\r
- throw cx;\r
- }//end if\r
- }\r
- }\r
- return completed;\r
-\r
- }\r
- \r
- private void connect(NioSender[] senders) throws ChannelException {\r
- ChannelException x = null;\r
- for (int i=0; i<senders.length; i++ ) {\r
- try {\r
- if (!senders[i].isConnected()) senders[i].connect();\r
- }catch ( IOException io ) {\r
- if ( x==null ) x = new ChannelException(io);\r
- x.addFaultyMember(senders[i].getDestination(),io);\r
- }\r
- }\r
- if ( x != null ) throw x;\r
- }\r
- \r
- private void setData(NioSender[] senders, byte[] data) throws ChannelException {\r
- ChannelException x = null;\r
- for (int i=0; i<senders.length; i++ ) {\r
- try {\r
- senders[i].setMessage(data);\r
- }catch ( IOException io ) {\r
- if ( x==null ) x = new ChannelException(io);\r
- x.addFaultyMember(senders[i].getDestination(),io);\r
- }\r
- }\r
- if ( x != null ) throw x;\r
- }\r
- \r
- \r
- private NioSender[] setupForSend(Member[] destination) throws ChannelException {\r
- ChannelException cx = null;\r
- NioSender[] result = new NioSender[destination.length];\r
- for ( int i=0; i<destination.length; i++ ) {\r
- NioSender sender = (NioSender)nioSenders.get(destination[i]);\r
- try {\r
-\r
- if (sender == null) {\r
- sender = new NioSender();\r
- sender.transferProperties(this, sender);\r
- nioSenders.put(destination[i], sender);\r
- }\r
- if (sender != null) {\r
- sender.reset();\r
- sender.setDestination(destination[i]);\r
- sender.setSelector(selector);\r
- result[i] = sender;\r
- }\r
- }catch ( UnknownHostException x ) {\r
- if (cx == null) cx = new ChannelException("Unable to setup NioSender.", x);\r
- cx.addFaultyMember(destination[i], x);\r
- }\r
- }\r
- if ( cx != null ) throw cx;\r
- else return result;\r
- }\r
- \r
- public void connect() {\r
- //do nothing, we connect on demand\r
- setConnected(true);\r
- }\r
- \r
- \r
- private synchronized void close() throws ChannelException {\r
- ChannelException x = null;\r
- Object[] members = nioSenders.keySet().toArray();\r
- for (int i=0; i<members.length; i++ ) {\r
- Member mbr = (Member)members[i];\r
- try {\r
- NioSender sender = (NioSender)nioSenders.get(mbr);\r
- sender.disconnect();\r
- }catch ( Exception e ) {\r
- if ( x == null ) x = new ChannelException(e);\r
- x.addFaultyMember(mbr,e);\r
- }\r
- nioSenders.remove(mbr);\r
- }\r
- if ( x != null ) throw x;\r
- }\r
- \r
- public void memberAdded(Member member) {\r
- \r
- }\r
- \r
- public void memberDisappeared(Member member) {\r
- //disconnect senders\r
- NioSender sender = (NioSender)nioSenders.remove(member);\r
- if ( sender != null ) sender.disconnect();\r
- }\r
-\r
- \r
- public synchronized void disconnect() {\r
- setConnected(false);\r
- try {close(); }catch (Exception x){}\r
- \r
- }\r
- \r
- public void finalize() {\r
- try {disconnect(); }catch ( Exception ignore){}\r
- }\r
-\r
- public boolean keepalive() {\r
- //throw new UnsupportedOperationException("Method ParallelNioSender.checkKeepAlive() not implemented");\r
- boolean result = false;\r
- for ( Iterator i = nioSenders.entrySet().iterator(); i.hasNext(); ) {\r
- Map.Entry entry = (Map.Entry)i.next();\r
- NioSender sender = (NioSender)entry.getValue();\r
- if ( sender.keepalive() ) {\r
- nioSenders.remove(entry.getKey());\r
- }\r
- }\r
- return result;\r
- }\r
-\r
+/*
+ * 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.apache.catalina.tribes.transport.nio;
+
+
+import java.io.IOException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.io.ChannelData;
+import org.apache.catalina.tribes.io.XByteBuffer;
+import org.apache.catalina.tribes.transport.MultiPointSender;
+import org.apache.catalina.tribes.transport.SenderState;
+import org.apache.catalina.tribes.transport.AbstractSender;
+import java.net.UnknownHostException;
+import org.apache.catalina.tribes.Channel;
+import org.apache.catalina.tribes.group.RpcChannel;
+import org.apache.catalina.tribes.util.Logs;
+import org.apache.catalina.tribes.UniqueId;
+
+/**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2005</p>
+ *
+ * <p>Company: </p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public class ParallelNioSender extends AbstractSender implements MultiPointSender {
+
+ protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(ParallelNioSender.class);
+ protected long selectTimeout = 5000; //default 5 seconds, same as send timeout
+ protected Selector selector;
+ protected HashMap nioSenders = new HashMap();
+
+ public ParallelNioSender() throws IOException {
+ selector = Selector.open();
+ setConnected(true);
+ }
+
+
+ public synchronized void sendMessage(Member[] destination, ChannelMessage msg) throws ChannelException {
+ long start = System.currentTimeMillis();
+ byte[] data = XByteBuffer.createDataPackage((ChannelData)msg);
+ NioSender[] senders = setupForSend(destination);
+ connect(senders);
+ setData(senders,data);
+
+ int remaining = senders.length;
+ ChannelException cx = null;
+ try {
+ //loop until complete, an error happens, or we timeout
+ long delta = System.currentTimeMillis() - start;
+ boolean waitForAck = (Channel.SEND_OPTIONS_USE_ACK & msg.getOptions()) == Channel.SEND_OPTIONS_USE_ACK;
+ while ( (remaining>0) && (delta<getTimeout()) ) {
+ try {
+ remaining -= doLoop(selectTimeout, getMaxRetryAttempts(),waitForAck,msg);
+ } catch (Exception x ) {
+ if ( cx == null ) {
+ if ( x instanceof ChannelException ) cx = (ChannelException)x;
+ else cx = new ChannelException("Parallel NIO send failed.", x);
+ } else {
+ if (x instanceof ChannelException) cx.addFaultyMember( ( (ChannelException) x).getFaultyMembers());
+ }
+ }
+ //bail out if all remaining senders are failing
+ if ( cx != null && cx.getFaultyMembers().length == remaining ) throw cx;
+ delta = System.currentTimeMillis() - start;
+ }
+ if ( remaining > 0 ) {
+ //timeout has occured
+ cx = new ChannelException("Operation has timed out("+getTimeout()+" ms.).");
+ for (int i=0; i<senders.length; i++ ) {
+ if (!senders[i].isComplete() ) cx.addFaultyMember(senders[i].getDestination(),null);
+ }
+ throw cx;
+ }
+ } catch (Exception x ) {
+ try { this.disconnect(); } catch (Exception ignore) {}
+ if ( x instanceof ChannelException ) throw (ChannelException)x;
+ else throw new ChannelException(x);
+ }
+
+ }
+
+ private int doLoop(long selectTimeOut, int maxAttempts, boolean waitForAck, ChannelMessage msg) throws IOException, ChannelException {
+ int completed = 0;
+ int selectedKeys = selector.select(selectTimeOut);
+
+ if (selectedKeys == 0) {
+ return 0;
+ }
+
+ Iterator it = selector.selectedKeys().iterator();
+ while (it.hasNext()) {
+ SelectionKey sk = (SelectionKey) it.next();
+ it.remove();
+ int readyOps = sk.readyOps();
+ sk.interestOps(sk.interestOps() & ~readyOps);
+ NioSender sender = (NioSender) sk.attachment();
+ try {
+ if (sender.process(sk,waitForAck)) {
+ completed++;
+ sender.setComplete(true);
+ if ( Logs.MESSAGES.isTraceEnabled() ) {
+ Logs.MESSAGES.trace("ParallelNioSender - Sent msg:" + new UniqueId(msg.getUniqueId()) + " at " +new java.sql.Timestamp(System.currentTimeMillis())+ " to "+sender.getDestination().getName());
+ }
+ SenderState.getSenderState(sender.getDestination()).setReady();
+ }//end if
+ } catch (Exception x) {
+ SenderState state = SenderState.getSenderState(sender.getDestination());
+ int attempt = sender.getAttempt()+1;
+ boolean retry = (sender.getAttempt() <= maxAttempts && maxAttempts>0);
+ synchronized (state) {
+
+ //sk.cancel();
+ if (state.isSuspect()) state.setFailing();
+ if (state.isReady()) {
+ state.setSuspect();
+ if ( retry )
+ log.warn("Member send is failing for:" + sender.getDestination().getName() +" ; Setting to suspect and retrying.");
+ else
+ log.warn("Member send is failing for:" + sender.getDestination().getName() +" ; Setting to suspect.", x);
+ }
+ }
+ if ( !isConnected() ) {
+ log.warn("Not retrying send for:" + sender.getDestination().getName() + "; Sender is disconnected.");
+ ChannelException cx = new ChannelException("Send failed, and sender is disconnected. Not retrying.",x);
+ cx.addFaultyMember(sender.getDestination(),x);
+ throw cx;
+ }
+
+ byte[] data = sender.getMessage();
+ if ( retry ) {
+ try {
+ sender.disconnect();
+ sender.connect();
+ sender.setAttempt(attempt);
+ sender.setMessage(data);
+ }catch ( Exception ignore){
+ state.setFailing();
+ }
+ } else {
+ ChannelException cx = new ChannelException("Send failed, attempt:"+sender.getAttempt()+" max:"+maxAttempts,x);
+ cx.addFaultyMember(sender.getDestination(),x);
+ throw cx;
+ }//end if
+ }
+ }
+ return completed;
+
+ }
+
+ private void connect(NioSender[] senders) throws ChannelException {
+ ChannelException x = null;
+ for (int i=0; i<senders.length; i++ ) {
+ try {
+ if (!senders[i].isConnected()) senders[i].connect();
+ }catch ( IOException io ) {
+ if ( x==null ) x = new ChannelException(io);
+ x.addFaultyMember(senders[i].getDestination(),io);
+ }
+ }
+ if ( x != null ) throw x;
+ }
+
+ private void setData(NioSender[] senders, byte[] data) throws ChannelException {
+ ChannelException x = null;
+ for (int i=0; i<senders.length; i++ ) {
+ try {
+ senders[i].setMessage(data);
+ }catch ( IOException io ) {
+ if ( x==null ) x = new ChannelException(io);
+ x.addFaultyMember(senders[i].getDestination(),io);
+ }
+ }
+ if ( x != null ) throw x;
+ }
+
+
+ private NioSender[] setupForSend(Member[] destination) throws ChannelException {
+ ChannelException cx = null;
+ NioSender[] result = new NioSender[destination.length];
+ for ( int i=0; i<destination.length; i++ ) {
+ NioSender sender = (NioSender)nioSenders.get(destination[i]);
+ try {
+
+ if (sender == null) {
+ sender = new NioSender();
+ sender.transferProperties(this, sender);
+ nioSenders.put(destination[i], sender);
+ }
+ if (sender != null) {
+ sender.reset();
+ sender.setDestination(destination[i]);
+ sender.setSelector(selector);
+ result[i] = sender;
+ }
+ }catch ( UnknownHostException x ) {
+ if (cx == null) cx = new ChannelException("Unable to setup NioSender.", x);
+ cx.addFaultyMember(destination[i], x);
+ }
+ }
+ if ( cx != null ) throw cx;
+ else return result;
+ }
+
+ public void connect() {
+ //do nothing, we connect on demand
+ setConnected(true);
+ }
+
+
+ private synchronized void close() throws ChannelException {
+ ChannelException x = null;
+ Object[] members = nioSenders.keySet().toArray();
+ for (int i=0; i<members.length; i++ ) {
+ Member mbr = (Member)members[i];
+ try {
+ NioSender sender = (NioSender)nioSenders.get(mbr);
+ sender.disconnect();
+ }catch ( Exception e ) {
+ if ( x == null ) x = new ChannelException(e);
+ x.addFaultyMember(mbr,e);
+ }
+ nioSenders.remove(mbr);
+ }
+ if ( x != null ) throw x;
+ }
+
+ public void memberAdded(Member member) {
+
+ }
+
+ public void memberDisappeared(Member member) {
+ //disconnect senders
+ NioSender sender = (NioSender)nioSenders.remove(member);
+ if ( sender != null ) sender.disconnect();
+ }
+
+
+ public synchronized void disconnect() {
+ setConnected(false);
+ try {close(); }catch (Exception x){}
+
+ }
+
+ public void finalize() {
+ try {disconnect(); }catch ( Exception ignore){}
+ }
+
+ public boolean keepalive() {
+ //throw new UnsupportedOperationException("Method ParallelNioSender.checkKeepAlive() not implemented");
+ boolean result = false;
+ for ( Iterator i = nioSenders.entrySet().iterator(); i.hasNext(); ) {
+ Map.Entry entry = (Map.Entry)i.next();
+ NioSender sender = (NioSender)entry.getValue();
+ if ( sender.keepalive() ) {
+ nioSenders.remove(entry.getKey());
+ }
+ }
+ return result;
+ }
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.transport.nio;\r
-\r
-import java.io.IOException;\r
-\r
-import org.apache.catalina.tribes.ChannelException;\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.transport.DataSender;\r
-import org.apache.catalina.tribes.transport.MultiPointSender;\r
-import org.apache.catalina.tribes.transport.PooledSender;\r
-\r
-/**\r
- * <p>Title: </p>\r
- *\r
- * <p>Description: </p>\r
- *\r
- * <p>Copyright: Copyright (c) 2005</p>\r
- *\r
- * <p>Company: </p>\r
- *\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-public class PooledParallelSender extends PooledSender implements MultiPointSender {\r
- protected boolean connected = true;\r
- public PooledParallelSender() {\r
- super();\r
- }\r
- \r
- public void sendMessage(Member[] destination, ChannelMessage message) throws ChannelException {\r
- if ( !connected ) throw new ChannelException("Sender not connected.");\r
- ParallelNioSender sender = (ParallelNioSender)getSender();\r
- try {\r
- sender.sendMessage(destination, message);\r
- sender.keepalive();\r
- }finally {\r
- if ( !connected ) disconnect();\r
- returnSender(sender);\r
- }\r
- }\r
-\r
- public DataSender getNewDataSender() {\r
- try {\r
- ParallelNioSender sender = new ParallelNioSender();\r
- sender.transferProperties(this,sender);\r
- return sender;\r
- } catch ( IOException x ) {\r
- throw new RuntimeException("Unable to open NIO selector.",x);\r
- }\r
- }\r
- \r
- public synchronized void disconnect() {\r
- this.connected = false;\r
- super.disconnect();\r
- }\r
-\r
- public synchronized void connect() throws IOException {\r
- this.connected = true;\r
- super.connect();\r
- }\r
-\r
- public void memberAdded(Member member) {\r
- \r
- }\r
- \r
- public void memberDisappeared(Member member) {\r
- //disconnect senders\r
- } \r
+/*
+ * 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.apache.catalina.tribes.transport.nio;
+
+import java.io.IOException;
+
+import org.apache.catalina.tribes.ChannelException;
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.transport.DataSender;
+import org.apache.catalina.tribes.transport.MultiPointSender;
+import org.apache.catalina.tribes.transport.PooledSender;
+
+/**
+ * <p>Title: </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2005</p>
+ *
+ * <p>Company: </p>
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public class PooledParallelSender extends PooledSender implements MultiPointSender {
+ protected boolean connected = true;
+ public PooledParallelSender() {
+ super();
+ }
+
+ public void sendMessage(Member[] destination, ChannelMessage message) throws ChannelException {
+ if ( !connected ) throw new ChannelException("Sender not connected.");
+ ParallelNioSender sender = (ParallelNioSender)getSender();
+ try {
+ sender.sendMessage(destination, message);
+ sender.keepalive();
+ }finally {
+ if ( !connected ) disconnect();
+ returnSender(sender);
+ }
+ }
+
+ public DataSender getNewDataSender() {
+ try {
+ ParallelNioSender sender = new ParallelNioSender();
+ sender.transferProperties(this,sender);
+ return sender;
+ } catch ( IOException x ) {
+ throw new RuntimeException("Unable to open NIO selector.",x);
+ }
+ }
+
+ public synchronized void disconnect() {
+ this.connected = false;
+ super.disconnect();
+ }
+
+ public synchronized void connect() throws IOException {
+ this.connected = true;
+ super.connect();
+ }
+
+ public void memberAdded(Member member) {
+
+ }
+
+ public void memberDisappeared(Member member) {
+ //disconnect senders
+ }
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.util;\r
-\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-\r
-import org.apache.catalina.tribes.ChannelMessage;\r
-import org.apache.catalina.tribes.Member;\r
-import org.apache.catalina.tribes.UniqueId;\r
-import org.apache.catalina.tribes.group.AbsoluteOrder;\r
-import org.apache.catalina.tribes.membership.MemberImpl;\r
-import org.apache.catalina.tribes.membership.Membership;\r
-import java.io.UnsupportedEncodingException;\r
-import org.apache.juli.logging.Log;\r
-import org.apache.juli.logging.LogFactory;\r
-import java.util.StringTokenizer;\r
-\r
-/**\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class Arrays {\r
- protected static Log log = LogFactory.getLog(Arrays.class);\r
- \r
- public static boolean contains(byte[] source, int srcoffset, byte[] key, int keyoffset, int length) {\r
- if ( srcoffset < 0 || srcoffset >= source.length) throw new ArrayIndexOutOfBoundsException("srcoffset is out of bounds.");\r
- if ( keyoffset < 0 || keyoffset >= key.length) throw new ArrayIndexOutOfBoundsException("keyoffset is out of bounds.");\r
- if ( length > (key.length-keyoffset) ) throw new ArrayIndexOutOfBoundsException("not enough data elements in the key, length is out of bounds.");\r
- //we don't have enough data to validate it\r
- if ( length > (source.length-srcoffset) ) return false;\r
- boolean match = true;\r
- int pos = keyoffset;\r
- for ( int i=srcoffset; match && i<length; i++ ) {\r
- match = (source[i] == key[pos++]);\r
- }\r
- return match;\r
- }\r
- \r
- public static String toString(byte[] data) {\r
- return toString(data,0,data!=null?data.length:0);\r
- }\r
-\r
- public static String toString(byte[] data, int offset, int length) {\r
- StringBuffer buf = new StringBuffer("{");\r
- if ( data != null && length > 0 ) {\r
- buf.append(data[offset++]);\r
- for (int i = offset; i < length; i++) {\r
- buf.append(", ").append(data[i]);\r
- }\r
- }\r
- buf.append("}");\r
- return buf.toString();\r
- }\r
- \r
- public static String toString(Object[] data) {\r
- return toString(data,0,data!=null?data.length:0);\r
- }\r
- \r
- public static String toString(Object[] data, int offset, int length) {\r
- StringBuffer buf = new StringBuffer("{");\r
- if ( data != null && length > 0 ) {\r
- buf.append(data[offset++]);\r
- for (int i = offset; i < length; i++) {\r
- buf.append(", ").append(data[i]);\r
- }\r
- }\r
- buf.append("}");\r
- return buf.toString();\r
- }\r
- \r
- public static String toNameString(Member[] data) {\r
- return toNameString(data,0,data!=null?data.length:0);\r
- }\r
- \r
- public static String toNameString(Member[] data, int offset, int length) {\r
- StringBuffer buf = new StringBuffer("{");\r
- if ( data != null && length > 0 ) {\r
- buf.append(data[offset++].getName());\r
- for (int i = offset; i < length; i++) {\r
- buf.append(", ").append(data[i].getName());\r
- }\r
- }\r
- buf.append("}");\r
- return buf.toString();\r
- }\r
-\r
- public static int add(int[] data) {\r
- int result = 0;\r
- for (int i=0;i<data.length; i++ ) result += data[i];\r
- return result;\r
- }\r
- \r
- public static UniqueId getUniqudId(ChannelMessage msg) {\r
- return new UniqueId(msg.getUniqueId());\r
- }\r
-\r
- public static UniqueId getUniqudId(byte[] data) {\r
- return new UniqueId(data);\r
- }\r
- \r
- public static boolean equals(byte[] o1, byte[] o2) {\r
- return java.util.Arrays.equals(o1,o2);\r
- }\r
-\r
- public static boolean equals(Object[] o1, Object[] o2) {\r
- boolean result = o1.length == o2.length;\r
- if ( result ) for (int i=0; i<o1.length && result; i++ ) result = o1[i].equals(o2[i]);\r
- return result;\r
- }\r
- \r
- public static boolean sameMembers(Member[] m1, Member[] m2) {\r
- AbsoluteOrder.absoluteOrder(m1);\r
- AbsoluteOrder.absoluteOrder(m2);\r
- return equals(m1,m2);\r
- }\r
- \r
- public static Member[] merge(Member[] m1, Member[] m2) {\r
- AbsoluteOrder.absoluteOrder(m1);\r
- AbsoluteOrder.absoluteOrder(m2);\r
- ArrayList list = new ArrayList(java.util.Arrays.asList(m1));\r
- for (int i=0; i<m2.length; i++) if ( !list.contains(m2[i]) ) list.add(m2[i]);\r
- Member[] result = new Member[list.size()];\r
- list.toArray(result);\r
- AbsoluteOrder.absoluteOrder(result);\r
- return result;\r
- }\r
- \r
- public static void fill(Membership mbrship, Member[] m) {\r
- for (int i=0; i<m.length; i++ ) mbrship.addMember((MemberImpl)m[i]);\r
- }\r
- \r
- public static Member[] diff(Membership complete, Membership local, MemberImpl ignore) {\r
- ArrayList result = new ArrayList();\r
- MemberImpl[] comp = complete.getMembers();\r
- for ( int i=0; i<comp.length; i++ ) {\r
- if ( ignore!=null && ignore.equals(comp[i]) ) continue;\r
- if ( local.getMember(comp[i]) == null ) result.add(comp[i]);\r
- }\r
- return (MemberImpl[])result.toArray(new MemberImpl[result.size()]);\r
- }\r
- \r
- public static Member[] remove(Member[] all, Member remove) {\r
- return extract(all,new Member[] {remove});\r
- }\r
- \r
- public static Member[] extract(Member[] all, Member[] remove) {\r
- List alist = java.util.Arrays.asList(all);\r
- ArrayList list = new ArrayList(alist);\r
- for (int i=0; i<remove.length; i++ ) list.remove(remove[i]);\r
- return (Member[])list.toArray(new Member[list.size()]);\r
- }\r
- \r
- public static int indexOf(Member member, Member[] members) {\r
- int result = -1;\r
- for (int i=0; (result==-1) && (i<members.length); i++ ) \r
- if ( member.equals(members[i]) ) result = i;\r
- return result;\r
- }\r
- \r
- public static int nextIndex(Member member, Member[] members) {\r
- int idx = indexOf(member,members)+1;\r
- if (idx >= members.length ) idx = ((members.length>0)?0:-1);\r
- \r
-//System.out.println("Next index:"+idx);\r
-//System.out.println("Member:"+member.getName());\r
-//System.out.println("Members:"+toNameString(members));\r
- return idx;\r
- }\r
- \r
- public static int hashCode(byte a[]) {\r
- if (a == null)\r
- return 0;\r
-\r
- int result = 1;\r
- for (int i=0; i<a.length; i++) {\r
- byte element = a[i];\r
- result = 31 * result + element;\r
- }\r
- return result;\r
- }\r
- \r
- public static byte[] fromString(String value) { \r
- if ( value == null ) return null;\r
- if ( !value.startsWith("{") ) throw new RuntimeException("byte arrays must be represented as {1,3,4,5,6}");\r
- StringTokenizer t = new StringTokenizer(value,"{,}",false);\r
- byte[] result = new byte[t.countTokens()];\r
- for (int i=0; i<result.length; i++ ) result[i] = Byte.parseByte(t.nextToken());\r
- return result;\r
- }\r
-\r
- \r
- \r
- public static byte[] convert(String s) {\r
- try {\r
- return s.getBytes("ISO-8859-1");\r
- }catch (UnsupportedEncodingException ux ) {\r
- log.error("Unable to convert ["+s+"] into a byte[] using ISO-8859-1 encoding, falling back to default encoding.");\r
- return s.getBytes();\r
- }\r
- }\r
-\r
-\r
- \r
- \r
- \r
+/*
+ * 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.apache.catalina.tribes.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.catalina.tribes.ChannelMessage;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.UniqueId;
+import org.apache.catalina.tribes.group.AbsoluteOrder;
+import org.apache.catalina.tribes.membership.MemberImpl;
+import org.apache.catalina.tribes.membership.Membership;
+import java.io.UnsupportedEncodingException;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import java.util.StringTokenizer;
+
+/**
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class Arrays {
+ protected static Log log = LogFactory.getLog(Arrays.class);
+
+ public static boolean contains(byte[] source, int srcoffset, byte[] key, int keyoffset, int length) {
+ if ( srcoffset < 0 || srcoffset >= source.length) throw new ArrayIndexOutOfBoundsException("srcoffset is out of bounds.");
+ if ( keyoffset < 0 || keyoffset >= key.length) throw new ArrayIndexOutOfBoundsException("keyoffset is out of bounds.");
+ if ( length > (key.length-keyoffset) ) throw new ArrayIndexOutOfBoundsException("not enough data elements in the key, length is out of bounds.");
+ //we don't have enough data to validate it
+ if ( length > (source.length-srcoffset) ) return false;
+ boolean match = true;
+ int pos = keyoffset;
+ for ( int i=srcoffset; match && i<length; i++ ) {
+ match = (source[i] == key[pos++]);
+ }
+ return match;
+ }
+
+ public static String toString(byte[] data) {
+ return toString(data,0,data!=null?data.length:0);
+ }
+
+ public static String toString(byte[] data, int offset, int length) {
+ StringBuffer buf = new StringBuffer("{");
+ if ( data != null && length > 0 ) {
+ buf.append(data[offset++]);
+ for (int i = offset; i < length; i++) {
+ buf.append(", ").append(data[i]);
+ }
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ public static String toString(Object[] data) {
+ return toString(data,0,data!=null?data.length:0);
+ }
+
+ public static String toString(Object[] data, int offset, int length) {
+ StringBuffer buf = new StringBuffer("{");
+ if ( data != null && length > 0 ) {
+ buf.append(data[offset++]);
+ for (int i = offset; i < length; i++) {
+ buf.append(", ").append(data[i]);
+ }
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ public static String toNameString(Member[] data) {
+ return toNameString(data,0,data!=null?data.length:0);
+ }
+
+ public static String toNameString(Member[] data, int offset, int length) {
+ StringBuffer buf = new StringBuffer("{");
+ if ( data != null && length > 0 ) {
+ buf.append(data[offset++].getName());
+ for (int i = offset; i < length; i++) {
+ buf.append(", ").append(data[i].getName());
+ }
+ }
+ buf.append("}");
+ return buf.toString();
+ }
+
+ public static int add(int[] data) {
+ int result = 0;
+ for (int i=0;i<data.length; i++ ) result += data[i];
+ return result;
+ }
+
+ public static UniqueId getUniqudId(ChannelMessage msg) {
+ return new UniqueId(msg.getUniqueId());
+ }
+
+ public static UniqueId getUniqudId(byte[] data) {
+ return new UniqueId(data);
+ }
+
+ public static boolean equals(byte[] o1, byte[] o2) {
+ return java.util.Arrays.equals(o1,o2);
+ }
+
+ public static boolean equals(Object[] o1, Object[] o2) {
+ boolean result = o1.length == o2.length;
+ if ( result ) for (int i=0; i<o1.length && result; i++ ) result = o1[i].equals(o2[i]);
+ return result;
+ }
+
+ public static boolean sameMembers(Member[] m1, Member[] m2) {
+ AbsoluteOrder.absoluteOrder(m1);
+ AbsoluteOrder.absoluteOrder(m2);
+ return equals(m1,m2);
+ }
+
+ public static Member[] merge(Member[] m1, Member[] m2) {
+ AbsoluteOrder.absoluteOrder(m1);
+ AbsoluteOrder.absoluteOrder(m2);
+ ArrayList list = new ArrayList(java.util.Arrays.asList(m1));
+ for (int i=0; i<m2.length; i++) if ( !list.contains(m2[i]) ) list.add(m2[i]);
+ Member[] result = new Member[list.size()];
+ list.toArray(result);
+ AbsoluteOrder.absoluteOrder(result);
+ return result;
+ }
+
+ public static void fill(Membership mbrship, Member[] m) {
+ for (int i=0; i<m.length; i++ ) mbrship.addMember((MemberImpl)m[i]);
+ }
+
+ public static Member[] diff(Membership complete, Membership local, MemberImpl ignore) {
+ ArrayList result = new ArrayList();
+ MemberImpl[] comp = complete.getMembers();
+ for ( int i=0; i<comp.length; i++ ) {
+ if ( ignore!=null && ignore.equals(comp[i]) ) continue;
+ if ( local.getMember(comp[i]) == null ) result.add(comp[i]);
+ }
+ return (MemberImpl[])result.toArray(new MemberImpl[result.size()]);
+ }
+
+ public static Member[] remove(Member[] all, Member remove) {
+ return extract(all,new Member[] {remove});
+ }
+
+ public static Member[] extract(Member[] all, Member[] remove) {
+ List alist = java.util.Arrays.asList(all);
+ ArrayList list = new ArrayList(alist);
+ for (int i=0; i<remove.length; i++ ) list.remove(remove[i]);
+ return (Member[])list.toArray(new Member[list.size()]);
+ }
+
+ public static int indexOf(Member member, Member[] members) {
+ int result = -1;
+ for (int i=0; (result==-1) && (i<members.length); i++ )
+ if ( member.equals(members[i]) ) result = i;
+ return result;
+ }
+
+ public static int nextIndex(Member member, Member[] members) {
+ int idx = indexOf(member,members)+1;
+ if (idx >= members.length ) idx = ((members.length>0)?0:-1);
+
+//System.out.println("Next index:"+idx);
+//System.out.println("Member:"+member.getName());
+//System.out.println("Members:"+toNameString(members));
+ return idx;
+ }
+
+ public static int hashCode(byte a[]) {
+ if (a == null)
+ return 0;
+
+ int result = 1;
+ for (int i=0; i<a.length; i++) {
+ byte element = a[i];
+ result = 31 * result + element;
+ }
+ return result;
+ }
+
+ public static byte[] fromString(String value) {
+ if ( value == null ) return null;
+ if ( !value.startsWith("{") ) throw new RuntimeException("byte arrays must be represented as {1,3,4,5,6}");
+ StringTokenizer t = new StringTokenizer(value,"{,}",false);
+ byte[] result = new byte[t.countTokens()];
+ for (int i=0; i<result.length; i++ ) result[i] = Byte.parseByte(t.nextToken());
+ return result;
+ }
+
+
+
+ public static byte[] convert(String s) {
+ try {
+ return s.getBytes("ISO-8859-1");
+ }catch (UnsupportedEncodingException ux ) {
+ log.error("Unable to convert ["+s+"] into a byte[] using ISO-8859-1 encoding, falling back to default encoding.");
+ return s.getBytes();
+ }
+ }
+
+
+
+
+
}
\ No newline at end of file
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.util;\r
-\r
-import org.apache.juli.logging.Log;\r
-import org.apache.juli.logging.LogFactory;\r
-/**\r
- * \r
- * Simple class that holds references to global loggers\r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class Logs {\r
- public static Log MESSAGES = LogFactory.getLog( "org.apache.catalina.tribes.MESSAGES" );\r
-}\r
+/*
+ * 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.apache.catalina.tribes.util;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+/**
+ *
+ * Simple class that holds references to global loggers
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class Logs {
+ public static Log MESSAGES = LogFactory.getLog( "org.apache.catalina.tribes.MESSAGES" );
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.catalina.tribes.util;\r
-\r
-import java.text.MessageFormat;\r
-import java.util.Hashtable;\r
-import java.util.Locale;\r
-import java.util.MissingResourceException;\r
-import java.util.ResourceBundle;\r
-import java.net.URLClassLoader;\r
-\r
-/**\r
- * An internationalization / localization helper class which reduces\r
- * the bother of handling ResourceBundles and takes care of the\r
- * common cases of message formating which otherwise require the\r
- * creation of Object arrays and such.\r
- *\r
- * <p>The StringManager operates on a package basis. One StringManager\r
- * per package can be created and accessed via the getManager method\r
- * call.\r
- *\r
- * <p>The StringManager will look for a ResourceBundle named by\r
- * the package name given plus the suffix of "LocalStrings". In\r
- * practice, this means that the localized information will be contained\r
- * in a LocalStrings.properties file located in the package\r
- * directory of the classpath.\r
- *\r
- * <p>Please see the documentation for java.util.ResourceBundle for\r
- * more information.\r
- *\r
- * @author James Duncan Davidson [duncan@eng.sun.com]\r
- * @author James Todd [gonzo@eng.sun.com]\r
- */\r
-\r
-public class StringManager {\r
-\r
- /**\r
- * The ResourceBundle for this StringManager.\r
- */\r
-\r
- private ResourceBundle bundle;\r
-\r
- private static org.apache.juli.logging.Log log=\r
- org.apache.juli.logging.LogFactory.getLog( StringManager.class );\r
-\r
- /**\r
- * Creates a new StringManager for a given package. This is a\r
- * private method and all access to it is arbitrated by the\r
- * static getManager method call so that only one StringManager\r
- * per package will be created.\r
- *\r
- * @param packageName Name of package to create StringManager for.\r
- */\r
-\r
- private StringManager(String packageName) {\r
- String bundleName = packageName + ".LocalStrings";\r
- try {\r
- bundle = ResourceBundle.getBundle(bundleName);\r
- return;\r
- } catch( MissingResourceException ex ) {\r
- // Try from the current loader ( that's the case for trusted apps )\r
- ClassLoader cl=Thread.currentThread().getContextClassLoader();\r
- if( cl != null ) {\r
- try {\r
- bundle=ResourceBundle.getBundle(bundleName, Locale.getDefault(), cl);\r
- return;\r
- } catch(MissingResourceException ex2) {\r
- }\r
- }\r
- if( cl==null )\r
- cl=this.getClass().getClassLoader();\r
-\r
- if (log.isDebugEnabled())\r
- log.debug("Can't find resource " + bundleName +\r
- " " + cl);\r
- if( cl instanceof URLClassLoader ) {\r
- if (log.isDebugEnabled()) \r
- log.debug( ((URLClassLoader)cl).getURLs());\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Get a string from the underlying resource bundle.\r
- *\r
- * @param key The resource name\r
- */\r
- public String getString(String key) {\r
- return MessageFormat.format(getStringInternal(key), (Object [])null);\r
- }\r
-\r
-\r
- protected String getStringInternal(String key) {\r
- if (key == null) {\r
- String msg = "key is null";\r
-\r
- throw new NullPointerException(msg);\r
- }\r
-\r
- String str = null;\r
-\r
- if( bundle==null )\r
- return key;\r
- try {\r
- str = bundle.getString(key);\r
- } catch (MissingResourceException mre) {\r
- str = "Cannot find message associated with key '" + key + "'";\r
- }\r
-\r
- return str;\r
- }\r
-\r
- /**\r
- * Get a string from the underlying resource bundle and format\r
- * it with the given set of arguments.\r
- *\r
- * @param key The resource name\r
- * @param args Formatting directives\r
- */\r
-\r
- public String getString(String key, Object[] args) {\r
- String iString = null;\r
- String value = getStringInternal(key);\r
-\r
- // this check for the runtime exception is some pre 1.1.6\r
- // VM's don't do an automatic toString() on the passed in\r
- // objects and barf out\r
-\r
- try {\r
- // ensure the arguments are not null so pre 1.2 VM's don't barf\r
- Object nonNullArgs[] = args;\r
- for (int i=0; i<args.length; i++) {\r
- if (args[i] == null) {\r
- if (nonNullArgs==args) nonNullArgs=(Object[])args.clone();\r
- nonNullArgs[i] = "null";\r
- }\r
- }\r
-\r
- iString = MessageFormat.format(value, nonNullArgs);\r
- } catch (IllegalArgumentException iae) {\r
- StringBuffer buf = new StringBuffer();\r
- buf.append(value);\r
- for (int i = 0; i < args.length; i++) {\r
- buf.append(" arg[" + i + "]=" + args[i]);\r
- }\r
- iString = buf.toString();\r
- }\r
- return iString;\r
- }\r
-\r
- /**\r
- * Get a string from the underlying resource bundle and format it\r
- * with the given object argument. This argument can of course be\r
- * a String object.\r
- *\r
- * @param key The resource name\r
- * @param arg Formatting directive\r
- */\r
-\r
- public String getString(String key, Object arg) {\r
- Object[] args = new Object[] {arg};\r
- return getString(key, args);\r
- }\r
-\r
- /**\r
- * Get a string from the underlying resource bundle and format it\r
- * with the given object arguments. These arguments can of course\r
- * be String objects.\r
- *\r
- * @param key The resource name\r
- * @param arg1 Formatting directive\r
- * @param arg2 Formatting directive\r
- */\r
-\r
- public String getString(String key, Object arg1, Object arg2) {\r
- Object[] args = new Object[] {arg1, arg2};\r
- return getString(key, args);\r
- }\r
-\r
- /**\r
- * Get a string from the underlying resource bundle and format it\r
- * with the given object arguments. These arguments can of course\r
- * be String objects.\r
- *\r
- * @param key The resource name\r
- * @param arg1 Formatting directive\r
- * @param arg2 Formatting directive\r
- * @param arg3 Formatting directive\r
- */\r
-\r
- public String getString(String key, Object arg1, Object arg2,\r
- Object arg3) {\r
- Object[] args = new Object[] {arg1, arg2, arg3};\r
- return getString(key, args);\r
- }\r
-\r
- /**\r
- * Get a string from the underlying resource bundle and format it\r
- * with the given object arguments. These arguments can of course\r
- * be String objects.\r
- *\r
- * @param key The resource name\r
- * @param arg1 Formatting directive\r
- * @param arg2 Formatting directive\r
- * @param arg3 Formatting directive\r
- * @param arg4 Formatting directive\r
- */\r
-\r
- public String getString(String key, Object arg1, Object arg2,\r
- Object arg3, Object arg4) {\r
- Object[] args = new Object[] {arg1, arg2, arg3, arg4};\r
- return getString(key, args);\r
- }\r
- // --------------------------------------------------------------\r
- // STATIC SUPPORT METHODS\r
- // --------------------------------------------------------------\r
-\r
- private static Hashtable managers = new Hashtable();\r
-\r
- /**\r
- * Get the StringManager for a particular package. If a manager for\r
- * a package already exists, it will be reused, else a new\r
- * StringManager will be created and returned.\r
- *\r
- * @param packageName The package name\r
- */\r
-\r
- public synchronized static StringManager getManager(String packageName) {\r
- StringManager mgr = (StringManager)managers.get(packageName);\r
-\r
- if (mgr == null) {\r
- mgr = new StringManager(packageName);\r
- managers.put(packageName, mgr);\r
- }\r
- return mgr;\r
- }\r
-}\r
+/*
+ * 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.apache.catalina.tribes.util;
+
+import java.text.MessageFormat;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.net.URLClassLoader;
+
+/**
+ * An internationalization / localization helper class which reduces
+ * the bother of handling ResourceBundles and takes care of the
+ * common cases of message formating which otherwise require the
+ * creation of Object arrays and such.
+ *
+ * <p>The StringManager operates on a package basis. One StringManager
+ * per package can be created and accessed via the getManager method
+ * call.
+ *
+ * <p>The StringManager will look for a ResourceBundle named by
+ * the package name given plus the suffix of "LocalStrings". In
+ * practice, this means that the localized information will be contained
+ * in a LocalStrings.properties file located in the package
+ * directory of the classpath.
+ *
+ * <p>Please see the documentation for java.util.ResourceBundle for
+ * more information.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ */
+
+public class StringManager {
+
+ /**
+ * The ResourceBundle for this StringManager.
+ */
+
+ private ResourceBundle bundle;
+
+ private static org.apache.juli.logging.Log log=
+ org.apache.juli.logging.LogFactory.getLog( StringManager.class );
+
+ /**
+ * Creates a new StringManager for a given package. This is a
+ * private method and all access to it is arbitrated by the
+ * static getManager method call so that only one StringManager
+ * per package will be created.
+ *
+ * @param packageName Name of package to create StringManager for.
+ */
+
+ private StringManager(String packageName) {
+ String bundleName = packageName + ".LocalStrings";
+ try {
+ bundle = ResourceBundle.getBundle(bundleName);
+ return;
+ } catch( MissingResourceException ex ) {
+ // Try from the current loader ( that's the case for trusted apps )
+ ClassLoader cl=Thread.currentThread().getContextClassLoader();
+ if( cl != null ) {
+ try {
+ bundle=ResourceBundle.getBundle(bundleName, Locale.getDefault(), cl);
+ return;
+ } catch(MissingResourceException ex2) {
+ }
+ }
+ if( cl==null )
+ cl=this.getClass().getClassLoader();
+
+ if (log.isDebugEnabled())
+ log.debug("Can't find resource " + bundleName +
+ " " + cl);
+ if( cl instanceof URLClassLoader ) {
+ if (log.isDebugEnabled())
+ log.debug( ((URLClassLoader)cl).getURLs());
+ }
+ }
+ }
+
+ /**
+ * Get a string from the underlying resource bundle.
+ *
+ * @param key The resource name
+ */
+ public String getString(String key) {
+ return MessageFormat.format(getStringInternal(key), (Object [])null);
+ }
+
+
+ protected String getStringInternal(String key) {
+ if (key == null) {
+ String msg = "key is null";
+
+ throw new NullPointerException(msg);
+ }
+
+ String str = null;
+
+ if( bundle==null )
+ return key;
+ try {
+ str = bundle.getString(key);
+ } catch (MissingResourceException mre) {
+ str = "Cannot find message associated with key '" + key + "'";
+ }
+
+ return str;
+ }
+
+ /**
+ * Get a string from the underlying resource bundle and format
+ * it with the given set of arguments.
+ *
+ * @param key The resource name
+ * @param args Formatting directives
+ */
+
+ public String getString(String key, Object[] args) {
+ String iString = null;
+ String value = getStringInternal(key);
+
+ // this check for the runtime exception is some pre 1.1.6
+ // VM's don't do an automatic toString() on the passed in
+ // objects and barf out
+
+ try {
+ // ensure the arguments are not null so pre 1.2 VM's don't barf
+ Object nonNullArgs[] = args;
+ for (int i=0; i<args.length; i++) {
+ if (args[i] == null) {
+ if (nonNullArgs==args) nonNullArgs=(Object[])args.clone();
+ nonNullArgs[i] = "null";
+ }
+ }
+
+ iString = MessageFormat.format(value, nonNullArgs);
+ } catch (IllegalArgumentException iae) {
+ StringBuffer buf = new StringBuffer();
+ buf.append(value);
+ for (int i = 0; i < args.length; i++) {
+ buf.append(" arg[" + i + "]=" + args[i]);
+ }
+ iString = buf.toString();
+ }
+ return iString;
+ }
+
+ /**
+ * Get a string from the underlying resource bundle and format it
+ * with the given object argument. This argument can of course be
+ * a String object.
+ *
+ * @param key The resource name
+ * @param arg Formatting directive
+ */
+
+ public String getString(String key, Object arg) {
+ Object[] args = new Object[] {arg};
+ return getString(key, args);
+ }
+
+ /**
+ * Get a string from the underlying resource bundle and format it
+ * with the given object arguments. These arguments can of course
+ * be String objects.
+ *
+ * @param key The resource name
+ * @param arg1 Formatting directive
+ * @param arg2 Formatting directive
+ */
+
+ public String getString(String key, Object arg1, Object arg2) {
+ Object[] args = new Object[] {arg1, arg2};
+ return getString(key, args);
+ }
+
+ /**
+ * Get a string from the underlying resource bundle and format it
+ * with the given object arguments. These arguments can of course
+ * be String objects.
+ *
+ * @param key The resource name
+ * @param arg1 Formatting directive
+ * @param arg2 Formatting directive
+ * @param arg3 Formatting directive
+ */
+
+ public String getString(String key, Object arg1, Object arg2,
+ Object arg3) {
+ Object[] args = new Object[] {arg1, arg2, arg3};
+ return getString(key, args);
+ }
+
+ /**
+ * Get a string from the underlying resource bundle and format it
+ * with the given object arguments. These arguments can of course
+ * be String objects.
+ *
+ * @param key The resource name
+ * @param arg1 Formatting directive
+ * @param arg2 Formatting directive
+ * @param arg3 Formatting directive
+ * @param arg4 Formatting directive
+ */
+
+ public String getString(String key, Object arg1, Object arg2,
+ Object arg3, Object arg4) {
+ Object[] args = new Object[] {arg1, arg2, arg3, arg4};
+ return getString(key, args);
+ }
+ // --------------------------------------------------------------
+ // STATIC SUPPORT METHODS
+ // --------------------------------------------------------------
+
+ private static Hashtable managers = new Hashtable();
+
+ /**
+ * Get the StringManager for a particular package. If a manager for
+ * a package already exists, it will be reused, else a new
+ * StringManager will be created and returned.
+ *
+ * @param packageName The package name
+ */
+
+ public synchronized static StringManager getManager(String packageName) {
+ StringManager mgr = (StringManager)managers.get(packageName);
+
+ if (mgr == null) {
+ mgr = new StringManager(packageName);
+ managers.put(packageName, mgr);
+ }
+ return mgr;
+ }
+}
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.apache.catalina.tribes.util;\r
-\r
-import java.security.SecureRandom;\r
-import java.util.Random;\r
-\r
-/**\r
- * simple generation of a UUID \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class UUIDGenerator {\r
- public static final int UUID_LENGTH = 16;\r
- public static final int UUID_VERSION = 4;\r
- public static final int BYTES_PER_INT = 4;\r
- public static final int BITS_PER_BYTE = 8;\r
- \r
- protected static SecureRandom secrand = null;\r
- protected static Random rand = new Random(System.currentTimeMillis());\r
- static {\r
- secrand = new SecureRandom();\r
- secrand.setSeed(rand.nextLong());\r
- }\r
- \r
- public static byte[] randomUUID(boolean secure) {\r
- byte[] result = new byte[UUID_LENGTH];\r
- return randomUUID(secure,result,0);\r
- }\r
-\r
- public static byte[] randomUUID(boolean secure, byte[] into, int offset) {\r
- if ( (offset+UUID_LENGTH)>into.length )\r
- throw new ArrayIndexOutOfBoundsException("Unable to fit "+UUID_LENGTH+" bytes into the array. length:"+into.length+" required length:"+(offset+UUID_LENGTH));\r
- Random r = (secure&&(secrand!=null))?secrand:rand;\r
- nextBytes(into,offset,UUID_LENGTH,r);\r
- into[6+offset] &= 0x0F;\r
- into[6+offset] |= (UUID_VERSION << 4);\r
- into[8+offset] &= 0x3F; //0011 1111\r
- into[8+offset] |= 0x80; //1000 0000\r
- return into;\r
- }\r
- \r
- /**\r
- * Same as java.util.Random.nextBytes except this one we dont have to allocate a new byte array\r
- * @param into byte[]\r
- * @param offset int\r
- * @param length int\r
- * @param r Random\r
- */\r
- public static void nextBytes(byte[] into, int offset, int length, Random r) {\r
- int numRequested = length;\r
- int numGot = 0, rnd = 0;\r
- while (true) {\r
- for (int i = 0; i < BYTES_PER_INT; i++) {\r
- if (numGot == numRequested) return;\r
- rnd = (i == 0 ? r.nextInt() : rnd >> BITS_PER_BYTE);\r
- into[offset+numGot] = (byte) rnd;\r
- numGot++;\r
- }\r
- }\r
- }\r
-\r
+/*
+ * 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.apache.catalina.tribes.util;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * simple generation of a UUID
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class UUIDGenerator {
+ public static final int UUID_LENGTH = 16;
+ public static final int UUID_VERSION = 4;
+ public static final int BYTES_PER_INT = 4;
+ public static final int BITS_PER_BYTE = 8;
+
+ protected static SecureRandom secrand = null;
+ protected static Random rand = new Random(System.currentTimeMillis());
+ static {
+ secrand = new SecureRandom();
+ secrand.setSeed(rand.nextLong());
+ }
+
+ public static byte[] randomUUID(boolean secure) {
+ byte[] result = new byte[UUID_LENGTH];
+ return randomUUID(secure,result,0);
+ }
+
+ public static byte[] randomUUID(boolean secure, byte[] into, int offset) {
+ if ( (offset+UUID_LENGTH)>into.length )
+ throw new ArrayIndexOutOfBoundsException("Unable to fit "+UUID_LENGTH+" bytes into the array. length:"+into.length+" required length:"+(offset+UUID_LENGTH));
+ Random r = (secure&&(secrand!=null))?secrand:rand;
+ nextBytes(into,offset,UUID_LENGTH,r);
+ into[6+offset] &= 0x0F;
+ into[6+offset] |= (UUID_VERSION << 4);
+ into[8+offset] &= 0x3F; //0011 1111
+ into[8+offset] |= 0x80; //1000 0000
+ return into;
+ }
+
+ /**
+ * Same as java.util.Random.nextBytes except this one we dont have to allocate a new byte array
+ * @param into byte[]
+ * @param offset int
+ * @param length int
+ * @param r Random
+ */
+ public static void nextBytes(byte[] into, int offset, int length, Random r) {
+ int numRequested = length;
+ int numGot = 0, rnd = 0;
+ while (true) {
+ for (int i = 0; i < BYTES_PER_INT; i++) {
+ if (numGot == numRequested) return;
+ rnd = (i == 0 ? r.nextInt() : rnd >> BITS_PER_BYTE);
+ into[offset+numGot] = (byte) rnd;
+ numGot++;
+ }
+ }
+ }
+
}
\ No newline at end of file
* <p>Convenience base class for {@link Group} implementations.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* <p>Convenience base class for {@link Role} implementations.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* <p>Convenience base class for {@link User} implementations.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
*
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* {@link MemoryUserDatabase} implementation of {@link UserDatabase}.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* {@link MemoryUserDatabase} implementation of {@link UserDatabase}.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* {@link MemoryUserDatabase} implementation of {@link UserDatabase}.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* and uses a specified XML file for its persistent storage.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 304047 $ $Date: 2005-08-04 15:12:16 +0200 (jeu., 04 août 2005) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* </ul>
*
* @author Craig R. McClanahan
- * @version $Revision: 304046 $ $Date: 2005-08-04 15:06:56 +0200 (jeu., 04 août 2005) $
+ * @version $Revision$ $Date$
* @since 4.1
*/
* Internet Message Bodies. Reference 1996
*
* @author Jeffrey Rodriguez
- * @version $Id: Base64.java 303133 2004-08-29 16:46:15Z yoavs $
+ * @version $Id$
*/
public final class Base64
{
* your own version for a particular web application.
*
* @author Craig R. McClanahan
- * @version $Date: 2005-01-05 11:00:45 +0100 (mer., 05 janv. 2005) $ $Version$
+ * @version $Date$ $Version$
*/
public class CharsetMapper {
*
* @author Craig R. McClanahan
* @author Bip Thelin
- * @version $Revision: 304082 $, $Date: 2005-09-08 17:41:11 +0200 (jeu., 08 sept. 2005) $
+ * @version $Revision$, $Date$
*/
public final class CustomObjectInputStream
*
* @author Fabien Carrion
* @author Remy Maucherat
- * @version $Revision: 303236 $, $Date: 2006-03-09 16:46:52 -0600 (Thu, 09 Mar 2006) $
+ * @version $Revision$, $Date$
*/
public class DefaultAnnotationProcessor implements AnnotationProcessor {
* Constructors are provided to easliy create such wrappers.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class Enumerator implements Enumeration {
* @author Craig McClanahan
* @author Justyna Horwat
* @author Greg Murray
- * @version $Revision: 360278 $ $Date: 2005-12-31 14:26:41 +0100 (sam., 31 déc. 2005) $
+ * @version $Revision$ $Date$
*/
public final class Extension {
*
* @author Greg Murray
* @author Justyna Horwat
- * @version $Revision: 360278 $ $Date: 2005-12-31 14:26:41 +0100 (sam., 31 déc. 2005) $
+ * @version $Revision$ $Date$
*
*/
public final class ExtensionValidator {
* registered InstanceListeners.
*
* @author Craig R. McClanahan
- * @version $Id: InstanceSupport.java 303133 2004-08-29 16:46:15Z yoavs $
+ * @version $Id$
*/
public final class InstanceSupport {
* registered LifecycleListeners.
*
* @author Craig R. McClanahan
- * @version $Id: LifecycleSupport.java 302726 2004-02-27 14:59:07Z yoavs $
+ * @version $Id$
*/
public final class LifecycleSupport {
* of the digest.
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class MD5Encoder {
* </TR>
* </TABLE>
*
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
* @author TAMURA Kent <kent@trl.ibm.co.jp>
*/
public class MIME2Java {
*
* @author Greg Murray
* @author Justyna Horwat
- * @version $Revision: 360278 $ $Date: 2005-12-31 14:26:41 +0100 (sam., 31 déc. 2005) $
+ * @version $Revision$ $Date$
*
*/
public class ManifestResource {
* <code>ParmaeterMap</code> instance is not locked.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class ParameterMap extends HashMap {
*
* @author Craig R. McClanahan
* @author Tim Tye
- * @version $Revision: 302905 $ $Date: 2004-05-26 18:41:54 +0200 (mer., 26 mai 2004) $
+ * @version $Revision$ $Date$
*/
public final class RequestUtil {
* is not locked.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class ResourceSet extends HashSet {
* when integrating Tomcat.
*
* @author Craig R. McClanahan
- * @version $Revision: 303276 $ $Date: 2004-09-24 18:41:12 +0200 (ven., 24 sept. 2004) $
+ * @version $Revision$ $Date$
*/
public class ServerInfo {
*
* @author Bip Thelin
* @author Dan Sandberg
- * @version $Revision: 303133 $, $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$, $Date$
*/
public class Strftime {
protected static Properties translate;
* method with the appropriate saved offset values.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class StringParser {
* package someplace.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class URL implements Serializable {
*
* @author Craig R. McClanahan
* @author Jason Brittain
- * @version $Revision: 303826 $ $Date: 2005-03-31 12:31:54 +0200 (jeu., 31 mars 2005) $
+ * @version $Revision$ $Date$
*/
public class AccessLogValve
* <p>This Valve should be attached to a Context.</p>
*
* @author Remy Maucherat
- * @version $Revision: 386404 $ $Date: 2006-03-16 18:50:37 +0100 (jeu., 16 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class CometConnectionManagerValve
* @author <a href="mailto:nicolaken@supereva.it">Nicola Ken Barozzi</a> Aisa
* @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
* @author Yoav Shapira
- * @version $Revision: 304023 $ $Date: 2005-07-26 14:45:22 +0200 (mar., 26 juil. 2005) $
+ * @version $Revision$ $Date$
*/
public class ErrorReportValve
*
*
* @author Tim Funk
- * @version $Revision: 303601 $ $Date: 2004-12-24 00:58:07 +0100 (ven., 24 déc. 2004) $
+ * @version $Revision$ $Date$
*/
public class ExtendedAccessLogValve
* @author Craig R. McClanahan
* @author Jason Brittain
* @author Remy Maucherat
- * @version $Revision: 304032 $ $Date: 2005-07-27 17:11:55 +0200 (mer., 27 juil. 2005) $
+ * @version $Revision$ $Date$
*/
public final class FastCommonAccessLogValve
* <b>USAGE CONSTRAINT</b>: To work correctly it requires a PersistentManager.
*
* @author Jean-Frederic Clere
- * @version $Revision: 303826 $ $Date: 2005-03-31 12:31:54 +0200 (jeu., 31 mars 2005) $
+ * @version $Revision$ $Date$
*/
public class PersistentValve
* based on the string representation of the remote client's IP address.
*
* @author Craig R. McClanahan
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public final class RemoteAddrValve
* based on the remote client's host name.
*
* @author Craig R. McClanahan
- * @version $Revision: 304096 $ $Date: 2005-09-20 23:02:12 +0200 (mar., 20 sept. 2005) $
+ * @version $Revision$ $Date$
*/
public final class RemoteHostValve
* of the logging you wish to perform.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public class RequestDumperValve
* of the filtering you wish to perform.
*
* @author Craig R. McClanahan
- * @version $Revision: 303161 $ $Date: 2004-09-01 12:10:10 +0200 (mer., 01 sept. 2004) $
+ * @version $Revision$ $Date$
*/
public abstract class RequestFilterValve
* of the concurrency control you wish to perform.</p>
*
* @author Remy Maucherat
- * @version $Revision: 386404 $ $Date: 2006-03-16 18:50:37 +0100 (jeu., 16 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class SemaphoreValve
* management and lifecycle support.
*
* @author Craig R. McClanahan
- * @version $Revision: 304023 $ $Date: 2005-07-26 14:45:22 +0200 (mar., 26 juil. 2005) $
+ * @version $Revision$ $Date$
*/
public abstract class ValveBase
-# $Id: LocalStrings.properties 301021 2005-08-01 10:00:59Z remm $
+# $Id$
# language
-# $Id: LocalStrings.properties 301004 2005-07-29 10:23:56Z remm $
+# $Id$
# language
-# $Id: LocalStrings_es.properties 299220 2004-02-24 08:50:56Z hgomez $
+# $Id$
# language es
-# $Id: LocalStrings_fr.properties 299220 2004-02-24 08:50:56Z hgomez $
+# $Id$
# language fr
-# $Id: LocalStrings_ja.properties 299758 2004-08-30 17:29:47Z amyroh $
+# $Id$
# language ja
* @see javax.el.ExpressionFactory
*
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public class ExpressionFactoryImpl extends ExpressionFactory {
* @see javax.el.MethodExpression
*
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: jhook $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class MethodExpressionImpl extends MethodExpression implements
Externalizable {
* @see javax.el.ValueExpression
*
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class ValueExpressionImpl extends ValueExpression implements
Externalizable {
/**
* A helper class of Arithmetic defined by the EL Specification
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public abstract class ELArithmetic {
* A helper class that implements the EL Specification
*
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public class ELSupport {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: jhook $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class ExpressionBuilder implements NodeVisitor {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public class FunctionMapperFactory extends FunctionMapper {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public class FunctionMapperImpl extends FunctionMapper implements
Externalizable {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public class ArithmeticNode extends SimpleNode {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstAnd extends BooleanNode {
public AstAnd(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstBracketSuffix extends SimpleNode {
public AstBracketSuffix(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstChoice extends SimpleNode {
public AstChoice(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstCompositeExpression extends SimpleNode {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstDeferredExpression extends SimpleNode {
public AstDeferredExpression(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstDiv extends ArithmeticNode {
public AstDiv(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstDotSuffix extends SimpleNode {
public AstDotSuffix(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstDynamicExpression extends SimpleNode {
public AstDynamicExpression(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstEmpty extends SimpleNode {
public AstEmpty(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstEqual extends BooleanNode {
public AstEqual(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstFalse extends BooleanNode {
public AstFalse(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstFloatingPoint extends SimpleNode {
public AstFloatingPoint(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstFunction extends SimpleNode {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstGreaterThan extends BooleanNode {
public AstGreaterThan(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstGreaterThanEqual extends BooleanNode {
public AstGreaterThanEqual(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: jhook $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstIdentifier extends SimpleNode {
public AstIdentifier(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstInteger extends SimpleNode {
public AstInteger(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstLessThan extends BooleanNode {
public AstLessThan(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstLessThanEqual extends BooleanNode {
public AstLessThanEqual(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstLiteralExpression extends SimpleNode {
public AstLiteralExpression(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstMinus extends ArithmeticNode {
public AstMinus(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstMod extends ArithmeticNode {
public AstMod(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstMult extends ArithmeticNode {
public AstMult(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstNegative extends SimpleNode {
public AstNegative(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstNot extends SimpleNode {
public AstNot(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstNotEqual extends BooleanNode {
public AstNotEqual(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstNull extends SimpleNode {
public AstNull(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstOr extends BooleanNode {
public AstOr(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstPlus extends ArithmeticNode {
public AstPlus(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstString extends SimpleNode {
public AstString(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstTrue extends BooleanNode {
public AstTrue(int id) {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class AstValue extends SimpleNode {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public class BooleanNode extends SimpleNode {
/**
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/*\r
- Author: Jacob Hookom\r
- Email: jacob at hookom.net\r
-*/\r
-\r
-/* == Option Declaration == */\r
-options\r
-{\r
- STATIC=false;\r
- NODE_PREFIX="Ast";\r
- VISITOR_EXCEPTION="javax.el.ELException";\r
- VISITOR=false;\r
- MULTI=true;\r
- NODE_DEFAULT_VOID=true;\r
- JAVA_UNICODE_ESCAPE=false;\r
- UNICODE_INPUT=true;\r
- BUILD_NODE_FILES=true;\r
-}\r
-\r
-/* == Parser Declaration == */\r
-PARSER_BEGIN( ELParser )\r
-package org.apache.el.parser;\r
-import java.io.StringReader;\r
-import javax.el.ELException;\r
-public class ELParser\r
-{\r
- public static Node parse(String ref) throws ELException\r
- {\r
- try {\r
- return (new ELParser(new StringReader(ref))).CompositeExpression();\r
- } catch (ParseException pe) {\r
- throw new ELException(pe.getMessage());\r
- }\r
- }\r
-}\r
-PARSER_END( ELParser )\r
-\r
-/*\r
- * CompositeExpression\r
- * Allow most flexible parsing, restrict by examining\r
- * type of returned node\r
- */\r
-AstCompositeExpression CompositeExpression() #CompositeExpression : {}\r
-{\r
- (DeferredExpression() | DynamicExpression() | LiteralExpression())* <EOF> { return jjtThis; }\r
-}\r
-\r
-/*\r
- * LiteralExpression\r
- * Non-EL Expression blocks\r
- */\r
-void LiteralExpression() #LiteralExpression : { Token t = null; }\r
-{\r
- t=<LITERAL_EXPRESSION> { jjtThis.setImage(t.image); }\r
-}\r
-\r
-/*\r
- * DeferredExpression\r
- * #{..} Expressions\r
- */\r
-void DeferredExpression() #DeferredExpression : {}\r
-{\r
- <START_DEFERRED_EXPRESSION> Expression() <END_EXPRESSION> \r
-}\r
-\r
-/*\r
- * DynamicExpression\r
- * ${..} Expressions\r
- */\r
-void DynamicExpression() #DynamicExpression : {}\r
-{\r
- <START_DYNAMIC_EXPRESSION> Expression() <END_EXPRESSION> \r
-}\r
-\r
-/*\r
- * Expression\r
- * EL Expression Language Root, goes to Choice\r
- */\r
-void Expression() : {}\r
-{\r
- Choice()\r
-}\r
-\r
-/*\r
- * Choice\r
- * For Choice markup a ? b : c, then Or\r
- */\r
-void Choice() : {}\r
-{\r
- Or() (<QUESTIONMARK> Or() <COLON> Choice() #Choice(3))*\r
-}\r
-\r
-/*\r
- * Or\r
- * For 'or' '||', then And\r
- */\r
-void Or() : {}\r
-{\r
- And() ((<OR0>|<OR1>) And() #Or(2))*\r
-}\r
-\r
-/*\r
- * And\r
- * For 'and' '&&', then Equality\r
- */\r
-void And() : {}\r
-{\r
- Equality() ((<AND0>|<AND1>) Equality() #And(2))*\r
-}\r
-\r
-/*\r
- * Equality\r
- * For '==' 'eq' '!=' 'ne', then Compare\r
- */\r
-void Equality() : {}\r
-{\r
- Compare()\r
- (\r
- ((<EQ0>|<EQ1>) Compare() #Equal(2))\r
- |\r
- ((<NE0>|<NE1>) Compare() #NotEqual(2))\r
- )*\r
-}\r
-\r
-/*\r
- * Compare\r
- * For a bunch of them, then Math\r
- */\r
-void Compare() : {}\r
-{\r
- Math()\r
- (\r
- ((<LT0>|<LT1>) Math() #LessThan(2))\r
- |\r
- ((<GT0>|<GT1>) Math() #GreaterThan(2))\r
- |\r
- ((<LE0>|<LE1>) Math() #LessThanEqual(2))\r
- |\r
- ((<GE0>|<GE1>) Math() #GreaterThanEqual(2))\r
- )*\r
-}\r
-\r
-/*\r
- * Math\r
- * For '+' '-', then Multiplication\r
- */\r
-void Math() : {}\r
-{\r
- Multiplication()\r
- (\r
- (<PLUS> Multiplication() #Plus(2))\r
- |\r
- (<MINUS> Multiplication() #Minus(2))\r
- )*\r
-}\r
-\r
-/*\r
- * Multiplication\r
- * For a bunch of them, then Unary\r
- */\r
-void Multiplication() : {}\r
-{\r
- Unary()\r
- (\r
- (<MULT> Unary() #Mult(2))\r
- |\r
- ((<DIV0>|<DIV1>) Unary() #Div(2))\r
- |\r
- ((<MOD0>|<MOD1>) Unary() #Mod(2)) \r
- )* \r
-}\r
-\r
-/*\r
- * Unary\r
- * For '-' '!' 'not' 'empty', then Value\r
- */\r
-void Unary() : {}\r
-{\r
- <MINUS> Unary() #Negative \r
- |\r
- (<NOT0>|<NOT1>) Unary() #Not \r
- |\r
- <EMPTY> Unary() #Empty\r
- | \r
- Value()\r
-}\r
-\r
-/*\r
- * Value\r
- * Defines Prefix plus zero or more Suffixes\r
- */\r
-void Value() : {}\r
-{\r
- (ValuePrefix() (ValueSuffix())*) #Value(>1)\r
-}\r
-\r
-/*\r
- * ValuePrefix\r
- * For Literals, Variables, and Functions\r
- */\r
-void ValuePrefix() : {}\r
-{\r
- Literal()\r
- | NonLiteral()\r
-}\r
-\r
-/*\r
- * ValueSuffix\r
- * Either dot or bracket notation\r
- */\r
-void ValueSuffix() : {}\r
-{\r
- DotSuffix() | BracketSuffix()\r
-}\r
-\r
-/*\r
- * DotSuffix\r
- * Dot Property\r
- */\r
-void DotSuffix() #DotSuffix : { Token t = null; }\r
-{\r
- <DOT> t=<IDENTIFIER> { jjtThis.setImage(t.image); }\r
-}\r
-\r
-/*\r
- * BracketSuffix\r
- * Sub Expression Suffix\r
- */\r
-void BracketSuffix() #BracketSuffix : {}\r
-{\r
- <LBRACK> Expression() <RBRACK>\r
-}\r
-\r
-/*\r
- * NonLiteral\r
- * For Grouped Operations, Identifiers, and Functions\r
- */\r
-void NonLiteral() : {}\r
-{\r
- <LPAREN> Expression() <RPAREN>\r
- | LOOKAHEAD(3) Function()\r
- | Identifier()\r
-}\r
-\r
-/*\r
- * Identifier\r
- * Java Language Identifier\r
- */\r
-void Identifier() #Identifier : { Token t = null; }\r
-{\r
- t=<IDENTIFIER> { jjtThis.setImage(t.image); }\r
-}\r
-\r
-/*\r
- * Function\r
- * Namespace:Name(a,b,c)\r
- */\r
-void Function() #Function :\r
-{\r
- Token t0 = null;\r
- Token t1 = null;\r
-}\r
-{\r
- (t0=<NAMESPACE>)? t1=<IDENTIFIER>\r
- {\r
- if (t0 != null) {\r
- jjtThis.setPrefix(t0.image.substring(0, t0.image.length() - 1));\r
- jjtThis.setLocalName(t1.image);\r
- } else {\r
- jjtThis.setLocalName(t1.image);\r
- }\r
- }\r
- <LPAREN> (Expression() (<COMMA> Expression())*)? <RPAREN>\r
-}\r
-\r
-/*\r
- * Literal\r
- * Reserved Keywords\r
- */\r
-void Literal() : {}\r
-{\r
- Boolean()\r
- | FloatingPoint()\r
- | Integer()\r
- | String()\r
- | Null()\r
-}\r
-\r
-/*\r
- * Boolean\r
- * For 'true' 'false'\r
- */\r
-void Boolean() : {}\r
-{\r
- <TRUE> #True\r
- | <FALSE> #False\r
-}\r
-\r
-/*\r
- * FloatinPoint\r
- * For Decimal and Floating Point Literals\r
- */\r
-void FloatingPoint() #FloatingPoint : { Token t = null; }\r
-{\r
- t=<FLOATING_POINT_LITERAL> { jjtThis.setImage(t.image); }\r
-}\r
-\r
-/*\r
- * Integer\r
- * For Simple Numeric Literals\r
- */\r
-void Integer() #Integer : { Token t = null; }\r
-{\r
- t=<INTEGER_LITERAL> { jjtThis.setImage(t.image); }\r
-}\r
-\r
-/*\r
- * String\r
- * For Quoted Literals\r
- */\r
-void String() #String : { Token t = null; }\r
-{\r
- t=<STRING_LITERAL> { jjtThis.setImage(t.image); }\r
-}\r
-\r
-/*\r
- * Null\r
- * For 'null'\r
- */\r
-void Null() #Null : {}\r
-{\r
- <NULL>\r
-}\r
-\r
-\r
-/* ==================================================================================== */\r
-<DEFAULT> TOKEN :\r
-{\r
- < LITERAL_EXPRESSION:\r
- ((~["\\", "$", "#"])\r
- | ("\\" ("\\" | "$" | "#"))\r
- | ("$" ~["{", "$"])\r
- | ("#" ~["{", "#"])\r
- )+\r
- | "$"\r
- | "#"\r
- >\r
-|\r
- < START_DYNAMIC_EXPRESSION: "${" > : IN_EXPRESSION\r
-|\r
- < START_DEFERRED_EXPRESSION: "#{" > : IN_EXPRESSION\r
-}\r
-\r
-<DEFAULT> SKIP : { "\\" }\r
-\r
-<IN_EXPRESSION> SKIP : { " " | "\t" | "\n" | "\r" }\r
-\r
-<IN_EXPRESSION> TOKEN :\r
-{\r
- < INTEGER_LITERAL: ["0"-"9"] (["0"-"9"])* >\r
-| < FLOATING_POINT_LITERAL: (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? \r
- | "." (["0"-"9"])+ (<EXPONENT>)?\r
- | (["0"-"9"])+ <EXPONENT>\r
- >\r
-| < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >\r
-| < STRING_LITERAL: ("\"" ((~["\"","\\"])\r
- | ("\\" ( ["\\","\""] )))* "\"")\r
- | ("\'" ((~["\'","\\"])\r
- | ("\\" ( ["\\","\'"] )))* "\'")\r
- >\r
-| < BADLY_ESCAPED_STRING_LITERAL: ("\"" (~["\"","\\"])* ("\\" ( ~["\\","\""] )))\r
- | ("\'" (~["\'","\\"])* ("\\" ( ~["\\","\'"] )))\r
- >\r
-| < TRUE : "true" >\r
-| < FALSE : "false" >\r
-| < NULL : "null" >\r
-| < END_EXPRESSION : "}" > : DEFAULT\r
-| < DOT : "." >\r
-| < LPAREN : "(" >\r
-| < RPAREN : ")" >\r
-| < LBRACK : "[" >\r
-| < RBRACK : "]" >\r
-| < COLON : ":" >\r
-| < COMMA : "," >\r
-| < GT0 : ">" >\r
-| < GT1 : "gt" >\r
-| < LT0 : "<" >\r
-| < LT1 : "lt" >\r
-| < GE0 : ">=" >\r
-| < GE1 : "ge" >\r
-| < LE0 : "<=" >\r
-| < LE1 : "le" >\r
-| < EQ0 : "==" >\r
-| < EQ1 : "eq" >\r
-| < NE0 : "!=" >\r
-| < NE1 : "ne" >\r
-| < NOT0 : "!" >\r
-| < NOT1 : "not" >\r
-| < AND0 : "&&" >\r
-| < AND1 : "and" >\r
-| < OR0 : "||" >\r
-| < OR1 : "or" >\r
-| < EMPTY : "empty" >\r
-| < INSTANCEOF : "instanceof" >\r
-| < MULT : "*" >\r
-| < PLUS : "+" >\r
-| < MINUS : "-" >\r
-| < QUESTIONMARK : "?" >\r
-| < DIV0 : "/" >\r
-| < DIV1 : "div" >\r
-| < MOD0 : "%" >\r
-| < MOD1 : "mod" >\r
-| < IDENTIFIER : (<LETTER>|<IMPL_OBJ_START>) (<LETTER>|<DIGIT>)* >\r
-| < NAMESPACE : (<IDENTIFIER> (<IDENTIFIER>|<MINUS>|<DOT>)* <COLON>) >\r
-| < FUNCTIONSUFFIX : (<IDENTIFIER>) >\r
-| < #IMPL_OBJ_START: "#" >\r
-| < #LETTER:\r
- [\r
- "\u0024",\r
- "\u0041"-"\u005a",\r
- "\u005f",\r
- "\u0061"-"\u007a",\r
- "\u00c0"-"\u00d6",\r
- "\u00d8"-"\u00f6",\r
- "\u00f8"-"\u00ff",\r
- "\u0100"-"\u1fff",\r
- "\u3040"-"\u318f",\r
- "\u3300"-"\u337f",\r
- "\u3400"-"\u3d2d",\r
- "\u4e00"-"\u9fff",\r
- "\uf900"-"\ufaff"\r
- ]\r
- >\r
-| < #DIGIT:\r
- [\r
- "\u0030"-"\u0039",\r
- "\u0660"-"\u0669",\r
- "\u06f0"-"\u06f9",\r
- "\u0966"-"\u096f",\r
- "\u09e6"-"\u09ef",\r
- "\u0a66"-"\u0a6f",\r
- "\u0ae6"-"\u0aef",\r
- "\u0b66"-"\u0b6f",\r
- "\u0be7"-"\u0bef",\r
- "\u0c66"-"\u0c6f",\r
- "\u0ce6"-"\u0cef",\r
- "\u0d66"-"\u0d6f",\r
- "\u0e50"-"\u0e59",\r
- "\u0ed0"-"\u0ed9",\r
- "\u1040"-"\u1049"\r
- ]\r
- >\r
-| < ILLEGAL_CHARACTER: (~[]) >\r
-}\r
+/*
+ * 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.
+ */
+
+/*
+ Author: Jacob Hookom
+ Email: jacob at hookom.net
+*/
+
+/* == Option Declaration == */
+options
+{
+ STATIC=false;
+ NODE_PREFIX="Ast";
+ VISITOR_EXCEPTION="javax.el.ELException";
+ VISITOR=false;
+ MULTI=true;
+ NODE_DEFAULT_VOID=true;
+ JAVA_UNICODE_ESCAPE=false;
+ UNICODE_INPUT=true;
+ BUILD_NODE_FILES=true;
+}
+
+/* == Parser Declaration == */
+PARSER_BEGIN( ELParser )
+package org.apache.el.parser;
+import java.io.StringReader;
+import javax.el.ELException;
+public class ELParser
+{
+ public static Node parse(String ref) throws ELException
+ {
+ try {
+ return (new ELParser(new StringReader(ref))).CompositeExpression();
+ } catch (ParseException pe) {
+ throw new ELException(pe.getMessage());
+ }
+ }
+}
+PARSER_END( ELParser )
+
+/*
+ * CompositeExpression
+ * Allow most flexible parsing, restrict by examining
+ * type of returned node
+ */
+AstCompositeExpression CompositeExpression() #CompositeExpression : {}
+{
+ (DeferredExpression() | DynamicExpression() | LiteralExpression())* <EOF> { return jjtThis; }
+}
+
+/*
+ * LiteralExpression
+ * Non-EL Expression blocks
+ */
+void LiteralExpression() #LiteralExpression : { Token t = null; }
+{
+ t=<LITERAL_EXPRESSION> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * DeferredExpression
+ * #{..} Expressions
+ */
+void DeferredExpression() #DeferredExpression : {}
+{
+ <START_DEFERRED_EXPRESSION> Expression() <END_EXPRESSION>
+}
+
+/*
+ * DynamicExpression
+ * ${..} Expressions
+ */
+void DynamicExpression() #DynamicExpression : {}
+{
+ <START_DYNAMIC_EXPRESSION> Expression() <END_EXPRESSION>
+}
+
+/*
+ * Expression
+ * EL Expression Language Root, goes to Choice
+ */
+void Expression() : {}
+{
+ Choice()
+}
+
+/*
+ * Choice
+ * For Choice markup a ? b : c, then Or
+ */
+void Choice() : {}
+{
+ Or() (<QUESTIONMARK> Or() <COLON> Choice() #Choice(3))*
+}
+
+/*
+ * Or
+ * For 'or' '||', then And
+ */
+void Or() : {}
+{
+ And() ((<OR0>|<OR1>) And() #Or(2))*
+}
+
+/*
+ * And
+ * For 'and' '&&', then Equality
+ */
+void And() : {}
+{
+ Equality() ((<AND0>|<AND1>) Equality() #And(2))*
+}
+
+/*
+ * Equality
+ * For '==' 'eq' '!=' 'ne', then Compare
+ */
+void Equality() : {}
+{
+ Compare()
+ (
+ ((<EQ0>|<EQ1>) Compare() #Equal(2))
+ |
+ ((<NE0>|<NE1>) Compare() #NotEqual(2))
+ )*
+}
+
+/*
+ * Compare
+ * For a bunch of them, then Math
+ */
+void Compare() : {}
+{
+ Math()
+ (
+ ((<LT0>|<LT1>) Math() #LessThan(2))
+ |
+ ((<GT0>|<GT1>) Math() #GreaterThan(2))
+ |
+ ((<LE0>|<LE1>) Math() #LessThanEqual(2))
+ |
+ ((<GE0>|<GE1>) Math() #GreaterThanEqual(2))
+ )*
+}
+
+/*
+ * Math
+ * For '+' '-', then Multiplication
+ */
+void Math() : {}
+{
+ Multiplication()
+ (
+ (<PLUS> Multiplication() #Plus(2))
+ |
+ (<MINUS> Multiplication() #Minus(2))
+ )*
+}
+
+/*
+ * Multiplication
+ * For a bunch of them, then Unary
+ */
+void Multiplication() : {}
+{
+ Unary()
+ (
+ (<MULT> Unary() #Mult(2))
+ |
+ ((<DIV0>|<DIV1>) Unary() #Div(2))
+ |
+ ((<MOD0>|<MOD1>) Unary() #Mod(2))
+ )*
+}
+
+/*
+ * Unary
+ * For '-' '!' 'not' 'empty', then Value
+ */
+void Unary() : {}
+{
+ <MINUS> Unary() #Negative
+ |
+ (<NOT0>|<NOT1>) Unary() #Not
+ |
+ <EMPTY> Unary() #Empty
+ |
+ Value()
+}
+
+/*
+ * Value
+ * Defines Prefix plus zero or more Suffixes
+ */
+void Value() : {}
+{
+ (ValuePrefix() (ValueSuffix())*) #Value(>1)
+}
+
+/*
+ * ValuePrefix
+ * For Literals, Variables, and Functions
+ */
+void ValuePrefix() : {}
+{
+ Literal()
+ | NonLiteral()
+}
+
+/*
+ * ValueSuffix
+ * Either dot or bracket notation
+ */
+void ValueSuffix() : {}
+{
+ DotSuffix() | BracketSuffix()
+}
+
+/*
+ * DotSuffix
+ * Dot Property
+ */
+void DotSuffix() #DotSuffix : { Token t = null; }
+{
+ <DOT> t=<IDENTIFIER> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * BracketSuffix
+ * Sub Expression Suffix
+ */
+void BracketSuffix() #BracketSuffix : {}
+{
+ <LBRACK> Expression() <RBRACK>
+}
+
+/*
+ * NonLiteral
+ * For Grouped Operations, Identifiers, and Functions
+ */
+void NonLiteral() : {}
+{
+ <LPAREN> Expression() <RPAREN>
+ | LOOKAHEAD(3) Function()
+ | Identifier()
+}
+
+/*
+ * Identifier
+ * Java Language Identifier
+ */
+void Identifier() #Identifier : { Token t = null; }
+{
+ t=<IDENTIFIER> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * Function
+ * Namespace:Name(a,b,c)
+ */
+void Function() #Function :
+{
+ Token t0 = null;
+ Token t1 = null;
+}
+{
+ (t0=<NAMESPACE>)? t1=<IDENTIFIER>
+ {
+ if (t0 != null) {
+ jjtThis.setPrefix(t0.image.substring(0, t0.image.length() - 1));
+ jjtThis.setLocalName(t1.image);
+ } else {
+ jjtThis.setLocalName(t1.image);
+ }
+ }
+ <LPAREN> (Expression() (<COMMA> Expression())*)? <RPAREN>
+}
+
+/*
+ * Literal
+ * Reserved Keywords
+ */
+void Literal() : {}
+{
+ Boolean()
+ | FloatingPoint()
+ | Integer()
+ | String()
+ | Null()
+}
+
+/*
+ * Boolean
+ * For 'true' 'false'
+ */
+void Boolean() : {}
+{
+ <TRUE> #True
+ | <FALSE> #False
+}
+
+/*
+ * FloatinPoint
+ * For Decimal and Floating Point Literals
+ */
+void FloatingPoint() #FloatingPoint : { Token t = null; }
+{
+ t=<FLOATING_POINT_LITERAL> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * Integer
+ * For Simple Numeric Literals
+ */
+void Integer() #Integer : { Token t = null; }
+{
+ t=<INTEGER_LITERAL> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * String
+ * For Quoted Literals
+ */
+void String() #String : { Token t = null; }
+{
+ t=<STRING_LITERAL> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * Null
+ * For 'null'
+ */
+void Null() #Null : {}
+{
+ <NULL>
+}
+
+
+/* ==================================================================================== */
+<DEFAULT> TOKEN :
+{
+ < LITERAL_EXPRESSION:
+ ((~["\\", "$", "#"])
+ | ("\\" ("\\" | "$" | "#"))
+ | ("$" ~["{", "$"])
+ | ("#" ~["{", "#"])
+ )+
+ | "$"
+ | "#"
+ >
+|
+ < START_DYNAMIC_EXPRESSION: "${" > : IN_EXPRESSION
+|
+ < START_DEFERRED_EXPRESSION: "#{" > : IN_EXPRESSION
+}
+
+<DEFAULT> SKIP : { "\\" }
+
+<IN_EXPRESSION> SKIP : { " " | "\t" | "\n" | "\r" }
+
+<IN_EXPRESSION> TOKEN :
+{
+ < INTEGER_LITERAL: ["0"-"9"] (["0"-"9"])* >
+| < FLOATING_POINT_LITERAL: (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)?
+ | "." (["0"-"9"])+ (<EXPONENT>)?
+ | (["0"-"9"])+ <EXPONENT>
+ >
+| < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
+| < STRING_LITERAL: ("\"" ((~["\"","\\"])
+ | ("\\" ( ["\\","\""] )))* "\"")
+ | ("\'" ((~["\'","\\"])
+ | ("\\" ( ["\\","\'"] )))* "\'")
+ >
+| < BADLY_ESCAPED_STRING_LITERAL: ("\"" (~["\"","\\"])* ("\\" ( ~["\\","\""] )))
+ | ("\'" (~["\'","\\"])* ("\\" ( ~["\\","\'"] )))
+ >
+| < TRUE : "true" >
+| < FALSE : "false" >
+| < NULL : "null" >
+| < END_EXPRESSION : "}" > : DEFAULT
+| < DOT : "." >
+| < LPAREN : "(" >
+| < RPAREN : ")" >
+| < LBRACK : "[" >
+| < RBRACK : "]" >
+| < COLON : ":" >
+| < COMMA : "," >
+| < GT0 : ">" >
+| < GT1 : "gt" >
+| < LT0 : "<" >
+| < LT1 : "lt" >
+| < GE0 : ">=" >
+| < GE1 : "ge" >
+| < LE0 : "<=" >
+| < LE1 : "le" >
+| < EQ0 : "==" >
+| < EQ1 : "eq" >
+| < NE0 : "!=" >
+| < NE1 : "ne" >
+| < NOT0 : "!" >
+| < NOT1 : "not" >
+| < AND0 : "&&" >
+| < AND1 : "and" >
+| < OR0 : "||" >
+| < OR1 : "or" >
+| < EMPTY : "empty" >
+| < INSTANCEOF : "instanceof" >
+| < MULT : "*" >
+| < PLUS : "+" >
+| < MINUS : "-" >
+| < QUESTIONMARK : "?" >
+| < DIV0 : "/" >
+| < DIV1 : "div" >
+| < MOD0 : "%" >
+| < MOD1 : "mod" >
+| < IDENTIFIER : (<LETTER>|<IMPL_OBJ_START>) (<LETTER>|<DIGIT>)* >
+| < NAMESPACE : (<IDENTIFIER> (<IDENTIFIER>|<MINUS>|<DOT>)* <COLON>) >
+| < FUNCTIONSUFFIX : (<IDENTIFIER>) >
+| < #IMPL_OBJ_START: "#" >
+| < #LETTER:
+ [
+ "\u0024",
+ "\u0041"-"\u005a",
+ "\u005f",
+ "\u0061"-"\u007a",
+ "\u00c0"-"\u00d6",
+ "\u00d8"-"\u00f6",
+ "\u00f8"-"\u00ff",
+ "\u0100"-"\u1fff",
+ "\u3040"-"\u318f",
+ "\u3300"-"\u337f",
+ "\u3400"-"\u3d2d",
+ "\u4e00"-"\u9fff",
+ "\uf900"-"\ufaff"
+ ]
+ >
+| < #DIGIT:
+ [
+ "\u0030"-"\u0039",
+ "\u0660"-"\u0669",
+ "\u06f0"-"\u06f9",
+ "\u0966"-"\u096f",
+ "\u09e6"-"\u09ef",
+ "\u0a66"-"\u0a6f",
+ "\u0ae6"-"\u0aef",
+ "\u0b66"-"\u0b6f",
+ "\u0be7"-"\u0bef",
+ "\u0c66"-"\u0c6f",
+ "\u0ce6"-"\u0cef",
+ "\u0d66"-"\u0d6f",
+ "\u0e50"-"\u0e59",
+ "\u0ed0"-"\u0ed9",
+ "\u1040"-"\u1049"
+ ]
+ >
+| < ILLEGAL_CHARACTER: (~[]) >
+}
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: jhook $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public interface Node {
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public interface NodeVisitor {
public void visit(Node node) throws Exception;
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: jhook $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public abstract class SimpleNode extends ELSupport implements Node {
protected Node parent;
/**
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dpatil $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public final class MessageFactory {
* Utilities for Managing Serialization and Reflection
*
* @author Jacob Hookom [jacob@hookom.net]
- * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: jhook $
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author$
*/
public class ReflectionUtil {
* Only used if a web application context is a directory.
*
* @author Glenn L. Nielsen
- * @version $Revision: 306189 $
+ * @version $Revision$
*/
public final class JspRuntimeContext {
-# $Id: LocalStrings.properties 379417 2006-02-21 10:57:35Z remm $
+# $Id$
#
# Default localized string information
# Localized this the Default Locale as is en_US
-# $Id: LocalStrings_es.properties 349479 2005-11-28 19:44:47Z yoavs $
+# $Id$
#
# Default localized string information
# Localized para Locale es_ES
-# $Id: LocalStrings_fr.properties 349479 2005-11-28 19:44:47Z yoavs $
+# $Id$
#
# Default localized string information
# Localized this the Default Locale as is fr_FR
-# $Id: LocalStrings_ja.properties 349479 2005-11-28 19:44:47Z yoavs $
+# $Id$
#
# Default localized string information
# Localized this the Default Locale as is ja_JP
*
* @author Fabien Carrion
* @author Remy Maucherat
- * @version $Revision: 303236 $, $Date: 2006-03-09 16:46:52 -0600 (Thu, 09 Mar 2006) $
+ * @version $Revision$, $Date$
*/
public class AnnotationHelper {
* Constructors are provided to easliy create such wrappers.
*
* @author Craig R. McClanahan
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public final class Enumerator implements Enumeration {
*
* @author Andy Clark, IBM
*
- * @version $Id: ASCIIReader.java 305933 2004-03-17 19:23:05Z luehe $
+ * @version $Id$
*/
public class ASCIIReader
extends Reader {
* @author TAMURA Kent, IBM
* @author Andy Clark, IBM
*
- * @version $Id: EncodingMap.java 306086 2004-10-01 19:24:35Z luehe $
+ * @version $Id$
*/
public class EncodingMap {
* use a separate class loader for the parser to be used.
*
* @author Craig R. McClanahan
- * @version $Revision: 306152 $ $Date: 2005-03-23 22:08:01 -0600 (Wed, 23 Mar 2005) $
+ * @version $Revision$ $Date$
*/
public class ParserUtils {
* </ul>
*
* @author Andy Clark
- * @version $Id: SymbolTable.java 306179 2005-07-27 15:12:04Z yoavs $
+ * @version $Id$
*/
public class SymbolTable {
* synchronized.
*
* @author Craig R. McClanahan
- * @version $Revision: 305933 $ $Date: 2004-03-17 13:23:05 -0600 (Wed, 17 Mar 2004) $
+ * @version $Revision$ $Date$
*/
public class TreeNode {
*
* @author Neil Graham, IBM
*
- * @version $Id: UCSReader.java 306148 2005-03-21 15:38:13Z remm $
+ * @version $Id$
*/
public class UCSReader extends Reader {
/**
* @author Andy Clark, IBM
*
- * @version $Id: UTF8Reader.java 306148 2005-03-21 15:38:13Z remm $
+ * @version $Id$
*/
public class UTF8Reader
extends Reader {
* @author Michael Glavassevich, IBM
* @author Rahul Srivastava, Sun Microsystems Inc.
*
- * @version $Id: XMLChar.java 306179 2005-07-27 15:12:04Z yoavs $
+ * @version $Id$
*/
public class XMLChar {
* @author Eric Ye, IBM
* @author Andy Clark, IBM
*
- * @version $Id: XMLString.java 306086 2004-10-01 19:24:35Z luehe $
+ * @version $Id$
*/
public class XMLString {
* @author Andy Clark, IBM
* @author Eric Ye, IBM
*
- * @version $Id: XMLStringBuffer.java 306086 2004-10-01 19:24:35Z luehe $
+ * @version $Id$
*/
public class XMLStringBuffer
extends XMLString {
@author Costin Manolache
@author Larry Isaacs
@author Bill Barker
- @version $Revision: 299988 $
+ @version $Revision$
*/
public class BaseJkConfig implements LifecycleListener {
private static org.apache.juli.logging.Log log =
* named {prefix}.{date}.{suffix} in a configured directory, with an
* optional preceding timestamp.
*
- * @version $Revision: 300331 $ $Date: 2005-03-03 19:29:45 +0100 (jeu., 03 mars 2005) $
+ * @version $Revision$ $Date$
*/
public class FileHandler
*
* @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
* @author Rod Waldhoff
- * @version $Id: Log.java 155426 2005-02-26 13:10:49Z dirkv $
+ * @version $Id$
*/
public interface Log {
* factory methods.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 155426 $ $Date: 2005-02-26 05:10:49 -0800 (Sat, 26 Feb 2005) $
+ * @version $Revision$ $Date$
*/
public class LogConfigurationException extends RuntimeException {
* @author Craig R. McClanahan
* @author Costin Manolache
* @author Richard A. Sitze
- * @version $Revision: 209449 $ $Date: 2005-07-06 05:06:32 -0700 (Wed, 06 Jul 2005) $
+ * @version $Revision$ $Date$
*/
public /* abstract */ class LogFactory {
* Handles the access control on the JNDI contexts.
*
* @author Remy Maucherat
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextAccessController {
* </ul>
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ContextBindings {
* Represents a reference address to an EJB.
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class EjbRef
* </p>
*
* @author Glenn Nielsen
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public final class JndiPermission extends BasicPermission {
-# $Id: LocalStrings_es.properties 301082 2002-07-18 16:47:23Z remm $
+# $Id$
# language es
* Parses names.
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class NameParserImpl
* Catalina JNDI Context implementation.
*
* @author Remy Maucherat
- * @version $Revision: 366304 $ $Date: 2006-01-05 22:42:29 +0100 (jeu., 05 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class NamingContext implements Context {
* Naming enumeration implementation.
*
* @author Remy Maucherat
- * @version $Revision: 366304 $ $Date: 2006-01-05 22:42:29 +0100 (jeu., 05 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class NamingContextBindingsEnumeration
* Naming enumeration implementation.
*
* @author Remy Maucherat
- * @version $Revision: 303022 $ $Date: 2004-07-24 00:46:08 +0200 (sam., 24 juil. 2004) $
+ * @version $Revision$ $Date$
*/
public class NamingContextEnumeration
* Represents a binding in a NamingContext.
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class NamingEntry {
* Implementation of the NamingService JMX MBean.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 303104 $
+ * @version $Revision$
*/
public final class NamingService
* Naming MBean interface.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
*/
public interface NamingServiceMBean {
* Represents a reference address to a resource environment.
*
* @author Remy Maucherat
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public class ResourceEnvRef
* Represents a reference address to a resource.
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ResourceLinkRef
* Represents a reference address to a resource.
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ResourceRef
* Catalina JNDI Context implementation.
*
* @author Remy Maucherat
- * @version $Revision: 303999 $ $Date: 2005-07-20 23:25:18 +0200 (mer., 20 juil. 2005) $
+ * @version $Revision$ $Date$
*/
public class SelectorContext implements Context {
* Represents a reference address to a transaction.
*
* @author Remy Maucherat
- * @version $Revision: 303133 $ $Date: 2004-08-29 18:46:15 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*/
public class TransactionRef
* Object factory for EJBs.
*
* @author Remy Maucherat
- * @version $Revision: 375650 $ $Date: 2006-02-07 18:55:11 +0100 (mar., 07 févr. 2006) $
+ * @version $Revision$ $Date$
*/
public class EjbFactory
* </pre>
*
* @author Craig R. McClanahan
- * @version $Revision: 303771 $ $Date: 2005-03-23 16:30:56 +0100 (mer., 23 mars 2005) $
+ * @version $Revision$ $Date$
*/
public class MailSessionFactory implements ObjectFactory {
*
* @author Jacek Laskowski
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class OpenEjbFactory implements ObjectFactory {
* Object factory for Resources env.
*
* @author Remy Maucherat
- * @version $Revision: 375650 $ $Date: 2006-02-07 18:55:11 +0100 (mar., 07 févr. 2006) $
+ * @version $Revision$ $Date$
*/
public class ResourceEnvFactory
* Object factory for Resources.
*
* @author Remy Maucherat
- * @version $Revision: 375650 $ $Date: 2006-02-07 18:55:11 +0100 (mar., 07 févr. 2006) $
+ * @version $Revision$ $Date$
*/
public class ResourceFactory
* <p>Object factory for resource links.</p>
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class ResourceLinkFactory
* Object factory for User trasactions.
*
* @author Remy Maucherat
- * @version $Revision: 375650 $ $Date: 2006-02-07 18:55:11 +0100 (mar., 07 févr. 2006) $
+ * @version $Revision$ $Date$
*/
public class TransactionFactory
* </ul>
*
* @author Remy Maucherat
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
*/
public class javaURLContextFactory
* Directory Context implementation helper class.
*
* @author Remy Maucherat
- * @version $Revision: 303999 $ $Date: 2005-07-20 23:25:18 +0200 (mer., 20 juil. 2005) $
+ * @version $Revision$ $Date$
*/
public abstract class BaseDirContext implements DirContext {
* Implements a cache entry.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
*/
public class CacheEntry {
* content is directly returned.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 303022 $
+ * @version $Revision$
*/
public class DirContextURLConnection
extends URLConnection {
* Stream handler to a JNDI directory context.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 304080 $
+ * @version $Revision$
*/
public class DirContextURLStreamHandler
extends URLStreamHandler {
* Factory for Stream handlers to a JNDI directory context.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
*/
public class DirContextURLStreamHandlerFactory
implements URLStreamHandlerFactory {
* Filesystem Directory Context implementation helper class.
*
* @author Remy Maucherat
- * @version $Revision: 366304 $ $Date: 2006-01-05 22:42:29 +0100 (jeu., 05 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class FileDirContext extends BaseDirContext {
* properly populated exceptions.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
*/
public class ImmutableNameNotFoundException
extends NameNotFoundException {
* Proxy Directory Context implementation.
*
* @author Remy Maucherat
- * @version $Revision: 304085 $ $Date: 2005-09-12 12:57:46 +0200 (lun., 12 sept. 2005) $
+ * @version $Revision$ $Date$
*/
public class ProxyDirContext implements DirContext {
* Naming enumeration implementation.
*
* @author Remy Maucherat
- * @version $Revision: 303022 $ $Date: 2004-07-24 00:46:08 +0200 (sam., 24 juil. 2004) $
+ * @version $Revision$ $Date$
*/
public class RecyclableNamingEnumeration
* Encapsultes the contents of a resource.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
*/
public class Resource {
* Attributes implementation.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 303877 $
+ * @version $Revision$
*/
public class ResourceAttributes implements Attributes {
* Implements a special purpose cache.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 304084 $
+ * @version $Revision$
*/
public class ResourceCache {
* WAR Directory Context implementation.
*
* @author Remy Maucherat
- * @version $Revision: 366304 $ $Date: 2006-01-05 22:42:29 +0100 (jeu., 05 janv. 2006) $
+ * @version $Revision$ $Date$
*/
public class WARDirContext extends BaseDirContext {
* Stream handler to a JNDI directory context.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
*/
public class Handler
extends DirContextURLStreamHandler {
/** Address
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Address {
/** Open SSL BIO Callback Interface
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public interface BIOCallback {
/** Directory
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Directory {
/** Error
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Error extends Exception {
/** File
*
* @author Mladen Turk
- * @version $Revision: 369000 $, $Date: 2006-01-14 09:08:44 +0100 (sam., 14 janv. 2006) $
+ * @version $Revision$, $Date$
*/
public class File {
/** Fileinfo
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class FileInfo {
/** Global
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Global {
/** Library
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public final class Library {
/** Local socket
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Local {
/** Lock
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Lock {
/** Mmap
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Mmap {
/** Multicast
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Multicast {
/** OS
*
* @author Mladen Turk
- * @version $Revision: 386278 $, $Date: 2006-03-16 08:15:58 +0100 (jeu., 16 mars 2006) $
+ * @version $Revision$, $Date$
*/
public class OS {
/** PasswordCallback Interface
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public interface PasswordCallback {
/** Poll
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Poll {
/** Pool
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Pool {
/** PoolCallback Interface
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public interface PoolCallback {
/** Proc
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Proc {
/** ProcErrorCallback Interface
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public interface ProcErrorCallback {
/** Procattr
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Procattr {
/** Windows Registy support
*
* @author Mladen Turk
- * @version $Revision: 301076 $, $Date: 2005-09-29 14:07:39 +0200 (jeu., 29 sept. 2005) $
+ * @version $Revision$, $Date$
*/
public class Registry {
/** SSL
*
* @author Mladen Turk
- * @version $Revision: 301018 $, $Date: 2005-08-01 09:23:09 +0200 (lun., 01 août 2005) $
+ * @version $Revision$, $Date$
*/
public final class SSL {
/** SSL Context
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public final class SSLContext {
/** SSL Socket
*
* @author Mladen Turk
- * @version $Revision: 300970 $, $Date: 2005-07-12 19:01:42 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class SSLSocket {
/** Shm
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Shm {
/** Sockaddr
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Sockaddr {
/** Socket
*
* @author Mladen Turk
- * @version $Revision: 327657 $, $Date: 2005-10-22 14:32:32 +0200 (sam., 22 oct. 2005) $
+ * @version $Revision$, $Date$
*/
public class Socket {
/** Status
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Status {
/** Stdlib
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Stdlib {
/** Time
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class Time {
/** User
*
* @author Mladen Turk
- * @version $Revision: 300969 $, $Date: 2005-07-12 16:56:11 +0200 (mar., 12 juil. 2005) $
+ * @version $Revision$, $Date$
*/
public class User {
* This class is used by XML Schema binary format validation
*
* @author Jeffrey Rodriguez
- * @version $Revision: 299789 $ $Date: 2004-09-17 20:34:19 +0200 (ven., 17 sept. 2004) $
+ * @version $Revision$ $Date$
*/
public final class Base64 {
-/* $Id: AbstractObjectCreationFactory.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: AbstractRulesImpl.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: ArrayStack.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: CallMethodRule.java 299765 2004-08-31 23:52:52Z yoavs $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: CallParamRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: Digester.java 380645 2006-02-24 11:37:46Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: FactoryCreateRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: GenericParser.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: NodeCreateRule.java 299765 2004-08-31 23:52:52Z yoavs $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: ObjectCreateRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: ObjectCreationFactory.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: ObjectParamRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: ParserFeatureSetterFactory.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: PathCallParamRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: Rule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: RuleSet.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: RuleSetBase.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: Rules.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: RulesBase.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: SetNextRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: SetPropertiesRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: SetPropertyRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: SetRootRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: SetTopRule.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: WithDefaultsRulesWrapper.java 299475 2004-06-26 17:41:32Z remm $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
-/* $Id: XercesParser.java 299768 2004-09-02 00:48:12Z yoavs $
+/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
/***************************************************************************
* Description: Base http request object. *
* Author: Keving Seguin [seguin@apache.org] *
- * Version: $Revision: 299484 $ *
+ * Version: $Revision$ *
***************************************************************************/
package org.apache.tomcat.util.http;
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
* @author Sean C. Sullivan
*
- * @version $Id: DefaultFileItem.java,v 1.21 2003/06/24 05:45:15 martinc Exp $
+ * @version $Id$
*/
public class DefaultFileItem
implements FileItem
*
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
*
- * @version $Id: DefaultFileItemFactory.java,v 1.2 2003/05/31 22:31:08 martinc Exp $
+ * @version $Id$
*/
public class DefaultFileItemFactory implements FileItemFactory
{
*
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
*
- * @version $Id: DeferredFileOutputStream.java,v 1.2 2003/05/31 22:31:08 martinc Exp $
+ * @version $Id$
*/
public class DeferredFileOutputStream
extends ThresholdingOutputStream
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
* @author Sean C. Sullivan
*
- * @version $Id: DiskFileUpload.java,v 1.3 2003/06/01 00:18:13 martinc Exp $
+ * @version $Id$
*/
public class DiskFileUpload
extends FileUploadBase
* @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
*
- * @version $Id: FileItem.java,v 1.15 2003/06/01 17:33:24 martinc Exp $
+ * @version $Id$
*/
public interface FileItem
extends Serializable
*
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
*
- * @version $Id: FileItemFactory.java,v 1.1 2003/04/27 17:30:06 martinc Exp $
+ * @version $Id$
*/
public interface FileItemFactory
{
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
* @author Sean C. Sullivan
*
- * @version $Id: FileUpload.java,v 1.23 2003/06/24 05:45:43 martinc Exp $
+ * @version $Id$
*/
public class FileUpload
extends FileUploadBase
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
* @author Sean C. Sullivan
*
- * @version $Id: FileUploadBase.java,v 1.3 2003/06/01 00:18:13 martinc Exp $
+ * @version $Id$
*/
public abstract class FileUploadBase
{
* Exception for errors encountered while processing the request.
*
* @author <a href="mailto:jmcnally@collab.net">John McNally</a>
- * @version $Id: FileUploadException.java,v 1.7 2003/04/27 17:30:06 martinc Exp $
+ * @version $Id$
*/
public class FileUploadException
extends Exception
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
* @author Sean C. Sullivan
*
- * @version $Id: MultipartStream.java,v 1.12 2003/06/01 00:18:13 martinc Exp $
+ * @version $Id$
*/
public class MultipartStream
{
*
* @author <a href="mailto:martinc@apache.org">Martin Cooper</a>
*
- * @version $Id: ThresholdingOutputStream.java,v 1.3 2003/05/31 22:31:08 martinc Exp $
+ * @version $Id$
*/
public abstract class ThresholdingOutputStream
extends OutputStream
-<!-- $Id: package.html,v 1.3 2003/04/27 17:30:06 martinc Exp $ -->
+<!-- $Id$ -->
<html>
<head>
<title>Overview of the org.apache.commons.fileupload component</title>
* supplied by the application.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 155428 $ $Date: 2005-02-26 14:12:25 +0100 (sam., 26 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public class BaseAttributeFilter implements NotificationFilter {
* <code>ModelMBean</code> beans exposed for management.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 155428 $ $Date: 2005-02-26 14:12:25 +0100 (sam., 26 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public class FeatureInfo implements Serializable {
* descriptor.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 383268 $ $Date: 2006-03-05 03:02:01 +0100 (dim., 05 mars 2006) $
+ * @version $Revision$ $Date$
*/
public class ManagedBean implements java.io.Serializable
* descriptor.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 155428 $ $Date: 2005-02-26 14:12:25 +0100 (sam., 26 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public class NotificationInfo extends FeatureInfo implements Serializable {
* descriptor.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 155428 $ $Date: 2005-02-26 14:12:25 +0100 (sam., 26 févr. 2005) $
+ * @version $Revision$ $Date$
*/
public class ParameterInfo extends FeatureInfo implements Serializable {
"-//Apache Software Foundation//DTD Model MBeans Configuration File"
"http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
- $Id: mbeans-descriptors.dtd 155428 2005-02-26 13:12:25Z dirkv $
+ $Id$
-->
-/*\r
- * Licensed to the Apache Software Foundation (ASF) under one or more\r
- * contributor license agreements. See the NOTICE file distributed with\r
- * this work for additional information regarding copyright ownership.\r
- * The ASF licenses this file to You under the Apache License, Version 2.0\r
- * (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * \r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- * \r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-\r
-package org.apache.tomcat.util.net;\r
-\r
-import java.io.IOException;\r
-import java.nio.ByteBuffer;\r
-import java.nio.channels.ByteChannel;\r
-import java.nio.channels.SocketChannel;\r
-\r
-import org.apache.tomcat.util.net.NioEndpoint.Poller;\r
-import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;\r
-import java.nio.channels.Selector;\r
-\r
-/**\r
- * \r
- * Base class for a SocketChannel wrapper used by the endpoint.\r
- * This way, logic for a SSL socket channel remains the same as for\r
- * a non SSL, making sure we don't need to code for any exception cases.\r
- * \r
- * @author Filip Hanik\r
- * @version 1.0\r
- */\r
-public class NioChannel implements ByteChannel{\r
-\r
- protected static ByteBuffer emptyBuf = ByteBuffer.allocate(0);\r
-\r
- protected SocketChannel sc = null;\r
-\r
- protected ApplicationBufferHandler bufHandler;\r
-\r
- protected Poller poller;\r
-\r
- public NioChannel(SocketChannel channel, ApplicationBufferHandler bufHandler) throws IOException {\r
- this.sc = channel;\r
- this.bufHandler = bufHandler;\r
- }\r
-\r
- public void reset() throws IOException {\r
- bufHandler.getReadBuffer().clear();\r
- bufHandler.getWriteBuffer().clear();\r
- }\r
-\r
- /**\r
- * returns true if the network buffer has \r
- * been flushed out and is empty\r
- * @return boolean\r
- */\r
- public boolean flush(Selector s) throws IOException {\r
- return true; //no network buffer in the regular channel\r
- }\r
-\r
-\r
- /**\r
- * Closes this channel.\r
- *\r
- * @throws IOException If an I/O error occurs\r
- * @todo Implement this java.nio.channels.Channel method\r
- */\r
- public void close() throws IOException {\r
- getIOChannel().socket().close();\r
- sc.close();\r
- }\r
-\r
- public void close(boolean force) throws IOException {\r
- if (isOpen() || force ) close();\r
- }\r
- /**\r
- * Tells whether or not this channel is open.\r
- *\r
- * @return <tt>true</tt> if, and only if, this channel is open\r
- * @todo Implement this java.nio.channels.Channel method\r
- */\r
- public boolean isOpen() {\r
- return sc.isOpen();\r
- }\r
-\r
- /**\r
- * Writes a sequence of bytes to this channel from the given buffer.\r
- *\r
- * @param src The buffer from which bytes are to be retrieved\r
- * @return The number of bytes written, possibly zero\r
- * @throws IOException If some other I/O error occurs\r
- * @todo Implement this java.nio.channels.WritableByteChannel method\r
- */\r
- public int write(ByteBuffer src) throws IOException {\r
- return sc.write(src);\r
- }\r
-\r
- /**\r
- * Reads a sequence of bytes from this channel into the given buffer.\r
- *\r
- * @param dst The buffer into which bytes are to be transferred\r
- * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the channel has reached end-of-stream\r
- * @throws IOException If some other I/O error occurs\r
- * @todo Implement this java.nio.channels.ReadableByteChannel method\r
- */\r
- public int read(ByteBuffer dst) throws IOException {\r
- return sc.read(dst);\r
- }\r
-\r
-\r
- /**\r
- * getBufHandler\r
- *\r
- * @return ApplicationBufferHandler\r
- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method\r
- */\r
- public ApplicationBufferHandler getBufHandler() {\r
- return bufHandler;\r
- }\r
-\r
- public Poller getPoller() {\r
- return poller;\r
- }\r
- /**\r
- * getIOChannel\r
- *\r
- * @return SocketChannel\r
- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method\r
- */\r
- public SocketChannel getIOChannel() {\r
- return sc;\r
- }\r
-\r
- /**\r
- * isClosing\r
- *\r
- * @return boolean\r
- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method\r
- */\r
- public boolean isClosing() {\r
- return false;\r
- }\r
-\r
- /**\r
- * isInitHandshakeComplete\r
- *\r
- * @return boolean\r
- * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method\r
- */\r
- public boolean isInitHandshakeComplete() {\r
- return true;\r
- }\r
-\r
- public int handshake(boolean read, boolean write) throws IOException {\r
- return 0;\r
- }\r
-\r
- public void setPoller(Poller poller) {\r
- this.poller = poller;\r
- }\r
-\r
- public void setIOChannel(SocketChannel IOChannel) {\r
- this.sc = IOChannel;\r
- }\r
-\r
- public String toString() {\r
- return super.toString()+":"+this.sc.toString();\r
- }\r
-\r
-}\r
+/*
+ * 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.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.SocketChannel;
+
+import org.apache.tomcat.util.net.NioEndpoint.Poller;
+import org.apache.tomcat.util.net.SecureNioChannel.ApplicationBufferHandler;
+import java.nio.channels.Selector;
+
+/**
+ *
+ * Base class for a SocketChannel wrapper used by the endpoint.
+ * This way, logic for a SSL socket channel remains the same as for
+ * a non SSL, making sure we don't need to code for any exception cases.
+ *
+ * @author Filip Hanik
+ * @version 1.0
+ */
+public class NioChannel implements ByteChannel{
+
+ protected static ByteBuffer emptyBuf = ByteBuffer.allocate(0);
+
+ protected SocketChannel sc = null;
+
+ protected ApplicationBufferHandler bufHandler;
+
+ protected Poller poller;
+
+ public NioChannel(SocketChannel channel, ApplicationBufferHandler bufHandler) throws IOException {
+ this.sc = channel;
+ this.bufHandler = bufHandler;
+ }
+
+ public void reset() throws IOException {
+ bufHandler.getReadBuffer().clear();
+ bufHandler.getWriteBuffer().clear();
+ }
+
+ /**
+ * returns true if the network buffer has
+ * been flushed out and is empty
+ * @return boolean
+ */
+ public boolean flush(Selector s) throws IOException {
+ return true; //no network buffer in the regular channel
+ }
+
+
+ /**
+ * Closes this channel.
+ *
+ * @throws IOException If an I/O error occurs
+ * @todo Implement this java.nio.channels.Channel method
+ */
+ public void close() throws IOException {
+ getIOChannel().socket().close();
+ sc.close();
+ }
+
+ public void close(boolean force) throws IOException {
+ if (isOpen() || force ) close();
+ }
+ /**
+ * Tells whether or not this channel is open.
+ *
+ * @return <tt>true</tt> if, and only if, this channel is open
+ * @todo Implement this java.nio.channels.Channel method
+ */
+ public boolean isOpen() {
+ return sc.isOpen();
+ }
+
+ /**
+ * Writes a sequence of bytes to this channel from the given buffer.
+ *
+ * @param src The buffer from which bytes are to be retrieved
+ * @return The number of bytes written, possibly zero
+ * @throws IOException If some other I/O error occurs
+ * @todo Implement this java.nio.channels.WritableByteChannel method
+ */
+ public int write(ByteBuffer src) throws IOException {
+ return sc.write(src);
+ }
+
+ /**
+ * Reads a sequence of bytes from this channel into the given buffer.
+ *
+ * @param dst The buffer into which bytes are to be transferred
+ * @return The number of bytes read, possibly zero, or <tt>-1</tt> if the channel has reached end-of-stream
+ * @throws IOException If some other I/O error occurs
+ * @todo Implement this java.nio.channels.ReadableByteChannel method
+ */
+ public int read(ByteBuffer dst) throws IOException {
+ return sc.read(dst);
+ }
+
+
+ /**
+ * getBufHandler
+ *
+ * @return ApplicationBufferHandler
+ * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+ */
+ public ApplicationBufferHandler getBufHandler() {
+ return bufHandler;
+ }
+
+ public Poller getPoller() {
+ return poller;
+ }
+ /**
+ * getIOChannel
+ *
+ * @return SocketChannel
+ * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+ */
+ public SocketChannel getIOChannel() {
+ return sc;
+ }
+
+ /**
+ * isClosing
+ *
+ * @return boolean
+ * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+ */
+ public boolean isClosing() {
+ return false;
+ }
+
+ /**
+ * isInitHandshakeComplete
+ *
+ * @return boolean
+ * @todo Implement this org.apache.tomcat.util.net.SecureNioChannel method
+ */
+ public boolean isInitHandshakeComplete() {
+ return true;
+ }
+
+ public int handshake(boolean read, boolean write) throws IOException {
+ return 0;
+ }
+
+ public void setPoller(Poller poller) {
+ this.poller = poller;
+ }
+
+ public void setIOChannel(SocketChannel IOChannel) {
+ this.sc = IOChannel;
+ }
+
+ public String toString() {
+ return super.toString()+":"+this.sc.toString();
+ }
+
+}
* package someplace.</p>
*
* @author Craig R. McClanahan
- * @version $Revision: 299472 $ $Date: 2004-06-20 02:08:15 +0200 (dim., 20 juin 2004) $
+ * @version $Revision$ $Date$
*/
public final class URL implements Serializable {
* <p>Please see the documentation for java.util.ResourceBundle for
* more information.
*
- * @version $Revision: 299753 $ $Date: 2004-08-29 19:14:42 +0200 (dim., 29 août 2004) $
+ * @version $Revision$ $Date$
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]