Correct props.
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Tue, 24 Oct 2006 03:17:11 +0000 (03:17 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Tue, 24 Oct 2006 03:17:11 +0000 (03:17 +0000)
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk@467222 13f79535-47bb-0310-9956-ffa450edef68

581 files changed:
java/javax/servlet/http/LocalStrings_es.properties
java/javax/servlet/resources/XMLSchema.dtd
java/javax/servlet/resources/datatypes.dtd
java/org/apache/AnnotationProcessor.java
java/org/apache/catalina/Authenticator.java
java/org/apache/catalina/Cluster.java
java/org/apache/catalina/Contained.java
java/org/apache/catalina/Container.java
java/org/apache/catalina/ContainerEvent.java
java/org/apache/catalina/ContainerListener.java
java/org/apache/catalina/ContainerServlet.java
java/org/apache/catalina/Context.java
java/org/apache/catalina/Engine.java
java/org/apache/catalina/Globals.java
java/org/apache/catalina/Group.java
java/org/apache/catalina/Host.java
java/org/apache/catalina/InstanceEvent.java
java/org/apache/catalina/InstanceListener.java
java/org/apache/catalina/Lifecycle.java
java/org/apache/catalina/LifecycleEvent.java
java/org/apache/catalina/LifecycleException.java
java/org/apache/catalina/LifecycleListener.java
java/org/apache/catalina/Loader.java
java/org/apache/catalina/Manager.java
java/org/apache/catalina/Pipeline.java
java/org/apache/catalina/Realm.java
java/org/apache/catalina/Role.java
java/org/apache/catalina/Server.java
java/org/apache/catalina/ServerFactory.java
java/org/apache/catalina/Service.java
java/org/apache/catalina/Session.java
java/org/apache/catalina/SessionEvent.java
java/org/apache/catalina/SessionListener.java
java/org/apache/catalina/Store.java
java/org/apache/catalina/User.java
java/org/apache/catalina/UserDatabase.java
java/org/apache/catalina/Valve.java
java/org/apache/catalina/Wrapper.java
java/org/apache/catalina/ant/AbstractCatalinaTask.java
java/org/apache/catalina/ant/BaseRedirectorHelperTask.java
java/org/apache/catalina/ant/DeployTask.java
java/org/apache/catalina/ant/InstallTask.java
java/org/apache/catalina/ant/JKStatusUpdateTask.java
java/org/apache/catalina/ant/JMXGetTask.java
java/org/apache/catalina/ant/JMXQueryTask.java
java/org/apache/catalina/ant/JMXSetTask.java
java/org/apache/catalina/ant/ListTask.java
java/org/apache/catalina/ant/ReloadTask.java
java/org/apache/catalina/ant/RemoveTask.java
java/org/apache/catalina/ant/ResourcesTask.java
java/org/apache/catalina/ant/RolesTask.java
java/org/apache/catalina/ant/ServerinfoTask.java
java/org/apache/catalina/ant/SessionsTask.java
java/org/apache/catalina/ant/StartTask.java
java/org/apache/catalina/ant/StopTask.java
java/org/apache/catalina/ant/UndeployTask.java
java/org/apache/catalina/ant/ValidatorTask.java
java/org/apache/catalina/ant/catalina.tasks
java/org/apache/catalina/ant/jmx/Arg.java
java/org/apache/catalina/ant/jmx/JMXAccessorCondition.java
java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java
java/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java
java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java
java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java
java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java
java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java
java/org/apache/catalina/ant/jmx/JMXAccessorTask.java
java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java
java/org/apache/catalina/ant/jmx/jmxaccessor.tasks
java/org/apache/catalina/authenticator/AuthenticatorBase.java
java/org/apache/catalina/authenticator/BasicAuthenticator.java
java/org/apache/catalina/authenticator/DigestAuthenticator.java
java/org/apache/catalina/authenticator/FormAuthenticator.java
java/org/apache/catalina/authenticator/NonLoginAuthenticator.java
java/org/apache/catalina/authenticator/SSLAuthenticator.java
java/org/apache/catalina/authenticator/SavedRequest.java
java/org/apache/catalina/authenticator/SingleSignOn.java
java/org/apache/catalina/authenticator/SingleSignOnEntry.java
java/org/apache/catalina/connector/ClientAbortException.java
java/org/apache/catalina/connector/Connector.java
java/org/apache/catalina/connector/CoyoteAdapter.java
java/org/apache/catalina/connector/CoyotePrincipal.java
java/org/apache/catalina/connector/Request.java
java/org/apache/catalina/connector/RequestFacade.java
java/org/apache/catalina/connector/Response.java
java/org/apache/catalina/connector/ResponseFacade.java
java/org/apache/catalina/core/ApplicationContext.java
java/org/apache/catalina/core/ApplicationContextFacade.java
java/org/apache/catalina/core/ApplicationDispatcher.java
java/org/apache/catalina/core/ApplicationFilterChain.java
java/org/apache/catalina/core/ApplicationFilterConfig.java
java/org/apache/catalina/core/ApplicationHttpRequest.java
java/org/apache/catalina/core/ApplicationHttpResponse.java
java/org/apache/catalina/core/ApplicationRequest.java
java/org/apache/catalina/core/ApplicationResponse.java
java/org/apache/catalina/core/AprLifecycleListener.java
java/org/apache/catalina/core/DummyRequest.java
java/org/apache/catalina/core/DummyResponse.java
java/org/apache/catalina/core/JasperListener.java
java/org/apache/catalina/core/NamingContextListener.java
java/org/apache/catalina/core/StandardContext.java
java/org/apache/catalina/core/StandardContextValve.java
java/org/apache/catalina/core/StandardEngine.java
java/org/apache/catalina/core/StandardEngineValve.java
java/org/apache/catalina/core/StandardHost.java
java/org/apache/catalina/core/StandardHostValve.java
java/org/apache/catalina/core/StandardServer.java
java/org/apache/catalina/core/StandardWrapper.java
java/org/apache/catalina/core/StandardWrapperFacade.java
java/org/apache/catalina/core/StandardWrapperValve.java
java/org/apache/catalina/deploy/ApplicationParameter.java
java/org/apache/catalina/deploy/ContextEjb.java
java/org/apache/catalina/deploy/ContextEnvironment.java
java/org/apache/catalina/deploy/ContextLocalEjb.java
java/org/apache/catalina/deploy/ContextResource.java
java/org/apache/catalina/deploy/ContextResourceEnvRef.java
java/org/apache/catalina/deploy/ContextResourceLink.java
java/org/apache/catalina/deploy/ContextService.java
java/org/apache/catalina/deploy/ContextTransaction.java
java/org/apache/catalina/deploy/ErrorPage.java
java/org/apache/catalina/deploy/FilterDef.java
java/org/apache/catalina/deploy/FilterMap.java
java/org/apache/catalina/deploy/LoginConfig.java
java/org/apache/catalina/deploy/MessageDestination.java
java/org/apache/catalina/deploy/MessageDestinationRef.java
java/org/apache/catalina/deploy/NamingResources.java
java/org/apache/catalina/deploy/ResourceBase.java
java/org/apache/catalina/deploy/SecurityCollection.java
java/org/apache/catalina/deploy/SecurityConstraint.java
java/org/apache/catalina/ha/CatalinaCluster.java
java/org/apache/catalina/ha/ClusterDeployer.java
java/org/apache/catalina/ha/ClusterListener.java
java/org/apache/catalina/ha/ClusterManager.java
java/org/apache/catalina/ha/ClusterMessage.java
java/org/apache/catalina/ha/ClusterRuleSet.java
java/org/apache/catalina/ha/ClusterSession.java
java/org/apache/catalina/ha/ClusterValve.java
java/org/apache/catalina/ha/Constants.java
java/org/apache/catalina/ha/context/ReplicatedContext.java
java/org/apache/catalina/ha/deploy/FarmWarDeployer.java
java/org/apache/catalina/ha/deploy/FileChangeListener.java
java/org/apache/catalina/ha/deploy/FileMessage.java
java/org/apache/catalina/ha/deploy/FileMessageFactory.java
java/org/apache/catalina/ha/deploy/UndeployMessage.java
java/org/apache/catalina/ha/deploy/WarWatcher.java
java/org/apache/catalina/ha/session/BackupManager.java
java/org/apache/catalina/ha/session/ClusterManagerBase.java
java/org/apache/catalina/ha/session/ClusterSessionListener.java
java/org/apache/catalina/ha/session/Constants.java
java/org/apache/catalina/ha/session/DeltaManager.java
java/org/apache/catalina/ha/session/DeltaRequest.java
java/org/apache/catalina/ha/session/DeltaSession.java
java/org/apache/catalina/ha/session/JvmRouteBinderValve.java
java/org/apache/catalina/ha/session/JvmRouteSessionIDBinderListener.java
java/org/apache/catalina/ha/session/ReplicatedSession.java
java/org/apache/catalina/ha/session/SerializablePrincipal.java
java/org/apache/catalina/ha/session/SessionIDMessage.java
java/org/apache/catalina/ha/session/SessionMessage.java
java/org/apache/catalina/ha/session/SessionMessageImpl.java
java/org/apache/catalina/ha/session/SimpleTcpReplicationManager.java
java/org/apache/catalina/ha/tcp/Constants.java
java/org/apache/catalina/ha/tcp/ReplicationValve.java
java/org/apache/catalina/ha/tcp/SendMessageData.java
java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java
java/org/apache/catalina/ha/util/IDynamicProperty.java
java/org/apache/catalina/loader/Reloader.java
java/org/apache/catalina/loader/ResourceEntry.java
java/org/apache/catalina/loader/StandardClassLoader.java
java/org/apache/catalina/loader/StandardClassLoaderMBean.java
java/org/apache/catalina/loader/WebappClassLoader.java
java/org/apache/catalina/loader/WebappLoader.java
java/org/apache/catalina/manager/HTMLManagerServlet.java
java/org/apache/catalina/manager/ManagerServlet.java
java/org/apache/catalina/manager/StatusManagerServlet.java
java/org/apache/catalina/manager/StatusTransformer.java
java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java
java/org/apache/catalina/manager/host/HostManagerServlet.java
java/org/apache/catalina/mbeans/ClassNameMBean.java
java/org/apache/catalina/mbeans/ConnectorMBean.java
java/org/apache/catalina/mbeans/ContextEnvironmentMBean.java
java/org/apache/catalina/mbeans/ContextResourceLinkMBean.java
java/org/apache/catalina/mbeans/ContextResourceMBean.java
java/org/apache/catalina/mbeans/DefaultContextMBean.java
java/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java
java/org/apache/catalina/mbeans/GroupMBean.java
java/org/apache/catalina/mbeans/MBeanFactory.java
java/org/apache/catalina/mbeans/MBeanUtils.java
java/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java
java/org/apache/catalina/mbeans/NamingResourcesMBean.java
java/org/apache/catalina/mbeans/RoleMBean.java
java/org/apache/catalina/mbeans/ServerLifecycleListener.java
java/org/apache/catalina/mbeans/StandardContextMBean.java
java/org/apache/catalina/mbeans/StandardEngineMBean.java
java/org/apache/catalina/mbeans/StandardHostMBean.java
java/org/apache/catalina/mbeans/StandardServerMBean.java
java/org/apache/catalina/mbeans/StandardServiceMBean.java
java/org/apache/catalina/mbeans/UserMBean.java
java/org/apache/catalina/realm/Constants.java
java/org/apache/catalina/realm/DataSourceRealm.java
java/org/apache/catalina/realm/GenericPrincipal.java
java/org/apache/catalina/realm/JAASCallbackHandler.java
java/org/apache/catalina/realm/JAASMemoryLoginModule.java
java/org/apache/catalina/realm/JAASRealm.java
java/org/apache/catalina/realm/JDBCRealm.java
java/org/apache/catalina/realm/JNDIRealm.java
java/org/apache/catalina/realm/LocalStrings.properties
java/org/apache/catalina/realm/LocalStrings_es.properties
java/org/apache/catalina/realm/LocalStrings_fr.properties
java/org/apache/catalina/realm/LocalStrings_ja.properties
java/org/apache/catalina/realm/MemoryRealm.java
java/org/apache/catalina/realm/MemoryRuleSet.java
java/org/apache/catalina/realm/RealmBase.java
java/org/apache/catalina/realm/UserDatabaseRealm.java
java/org/apache/catalina/security/SecurityClassLoad.java
java/org/apache/catalina/servlets/CGIServlet.java
java/org/apache/catalina/servlets/DefaultServlet.java
java/org/apache/catalina/servlets/InvokerHttpRequest.java
java/org/apache/catalina/servlets/InvokerServlet.java
java/org/apache/catalina/servlets/WebdavServlet.java
java/org/apache/catalina/session/FileStore.java
java/org/apache/catalina/session/JDBCStore.java
java/org/apache/catalina/session/ManagerBase.java
java/org/apache/catalina/session/PersistentManager.java
java/org/apache/catalina/session/PersistentManagerBase.java
java/org/apache/catalina/session/StandardManager.java
java/org/apache/catalina/session/StandardSession.java
java/org/apache/catalina/session/StandardSessionFacade.java
java/org/apache/catalina/session/StoreBase.java
java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java
java/org/apache/catalina/ssi/ExpressionParseTree.java
java/org/apache/catalina/ssi/ExpressionTokenizer.java
java/org/apache/catalina/ssi/ResponseIncludeWrapper.java
java/org/apache/catalina/ssi/SSICommand.java
java/org/apache/catalina/ssi/SSIConditional.java
java/org/apache/catalina/ssi/SSIConditionalState.java
java/org/apache/catalina/ssi/SSIConfig.java
java/org/apache/catalina/ssi/SSIEcho.java
java/org/apache/catalina/ssi/SSIExec.java
java/org/apache/catalina/ssi/SSIExternalResolver.java
java/org/apache/catalina/ssi/SSIFilter.java
java/org/apache/catalina/ssi/SSIFlastmod.java
java/org/apache/catalina/ssi/SSIFsize.java
java/org/apache/catalina/ssi/SSIInclude.java
java/org/apache/catalina/ssi/SSIMediator.java
java/org/apache/catalina/ssi/SSIPrintenv.java
java/org/apache/catalina/ssi/SSIProcessor.java
java/org/apache/catalina/ssi/SSIServlet.java
java/org/apache/catalina/ssi/SSIServletExternalResolver.java
java/org/apache/catalina/ssi/SSISet.java
java/org/apache/catalina/ssi/SSIStopProcessingException.java
java/org/apache/catalina/startup/Bootstrap.java
java/org/apache/catalina/startup/Catalina.java
java/org/apache/catalina/startup/CatalinaProperties.java
java/org/apache/catalina/startup/ClassLoaderFactory.java
java/org/apache/catalina/startup/ClusterRuleSetFactory.java
java/org/apache/catalina/startup/ConnectorCreateRule.java
java/org/apache/catalina/startup/Constants.java
java/org/apache/catalina/startup/ContextConfig.java
java/org/apache/catalina/startup/ContextRuleSet.java
java/org/apache/catalina/startup/CopyParentClassLoaderRule.java
java/org/apache/catalina/startup/Embedded.java
java/org/apache/catalina/startup/EngineConfig.java
java/org/apache/catalina/startup/EngineRuleSet.java
java/org/apache/catalina/startup/ExpandWar.java
java/org/apache/catalina/startup/HomesUserDatabase.java
java/org/apache/catalina/startup/HostConfig.java
java/org/apache/catalina/startup/HostRuleSet.java
java/org/apache/catalina/startup/NamingRuleSet.java
java/org/apache/catalina/startup/PasswdUserDatabase.java
java/org/apache/catalina/startup/SetNextNamingRule.java
java/org/apache/catalina/startup/TldRuleSet.java
java/org/apache/catalina/startup/Tool.java
java/org/apache/catalina/startup/UserConfig.java
java/org/apache/catalina/startup/UserDatabase.java
java/org/apache/catalina/startup/WebAnnotationSet.java
java/org/apache/catalina/startup/WebRuleSet.java
java/org/apache/catalina/tribes/ByteMessage.java
java/org/apache/catalina/tribes/Channel.java
java/org/apache/catalina/tribes/ChannelException.java
java/org/apache/catalina/tribes/ChannelInterceptor.java
java/org/apache/catalina/tribes/ChannelListener.java
java/org/apache/catalina/tribes/ChannelMessage.java
java/org/apache/catalina/tribes/ChannelReceiver.java
java/org/apache/catalina/tribes/ChannelSender.java
java/org/apache/catalina/tribes/Constants.java
java/org/apache/catalina/tribes/ErrorHandler.java
java/org/apache/catalina/tribes/Heartbeat.java
java/org/apache/catalina/tribes/ManagedChannel.java
java/org/apache/catalina/tribes/Member.java
java/org/apache/catalina/tribes/MembershipListener.java
java/org/apache/catalina/tribes/MembershipService.java
java/org/apache/catalina/tribes/MessageListener.java
java/org/apache/catalina/tribes/UniqueId.java
java/org/apache/catalina/tribes/group/ChannelCoordinator.java
java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
java/org/apache/catalina/tribes/group/GroupChannel.java
java/org/apache/catalina/tribes/group/InterceptorPayload.java
java/org/apache/catalina/tribes/group/Response.java
java/org/apache/catalina/tribes/group/RpcCallback.java
java/org/apache/catalina/tribes/group/RpcChannel.java
java/org/apache/catalina/tribes/group/RpcMessage.java
java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java
java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java
java/org/apache/catalina/tribes/group/interceptors/MessageDispatch15Interceptor.java
java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java
java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java
java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java
java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
java/org/apache/catalina/tribes/io/BufferPool.java
java/org/apache/catalina/tribes/io/BufferPool14Impl.java
java/org/apache/catalina/tribes/io/BufferPool15Impl.java
java/org/apache/catalina/tribes/membership/Constants.java
java/org/apache/catalina/tribes/membership/McastService.java
java/org/apache/catalina/tribes/membership/McastServiceImpl.java
java/org/apache/catalina/tribes/membership/MemberImpl.java
java/org/apache/catalina/tribes/membership/Membership.java
java/org/apache/catalina/tribes/membership/StaticMember.java
java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java
java/org/apache/catalina/tribes/tipis/ReplicatedMap.java
java/org/apache/catalina/tribes/tipis/ReplicatedMapEntry.java
java/org/apache/catalina/tribes/tipis/Streamable.java
java/org/apache/catalina/tribes/transport/Constants.java
java/org/apache/catalina/tribes/transport/DataSender.java
java/org/apache/catalina/tribes/transport/MultiPointSender.java
java/org/apache/catalina/tribes/transport/PooledSender.java
java/org/apache/catalina/tribes/transport/ReplicationTransmitter.java
java/org/apache/catalina/tribes/transport/SenderState.java
java/org/apache/catalina/tribes/transport/ThreadPool.java
java/org/apache/catalina/tribes/transport/WorkerThread.java
java/org/apache/catalina/tribes/transport/bio/BioReplicationThread.java
java/org/apache/catalina/tribes/transport/bio/BioSender.java
java/org/apache/catalina/tribes/transport/bio/util/FastQueue.java
java/org/apache/catalina/tribes/transport/bio/util/LinkObject.java
java/org/apache/catalina/tribes/transport/bio/util/SingleRemoveSynchronizedAddLock.java
java/org/apache/catalina/tribes/transport/nio/NioReceiver.java
java/org/apache/catalina/tribes/transport/nio/NioReplicationThread.java
java/org/apache/catalina/tribes/transport/nio/NioSender.java
java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java
java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java
java/org/apache/catalina/tribes/util/Arrays.java
java/org/apache/catalina/tribes/util/Logs.java
java/org/apache/catalina/tribes/util/StringManager.java
java/org/apache/catalina/tribes/util/UUIDGenerator.java
java/org/apache/catalina/users/AbstractGroup.java
java/org/apache/catalina/users/AbstractRole.java
java/org/apache/catalina/users/AbstractUser.java
java/org/apache/catalina/users/Constants.java
java/org/apache/catalina/users/MemoryGroup.java
java/org/apache/catalina/users/MemoryRole.java
java/org/apache/catalina/users/MemoryUser.java
java/org/apache/catalina/users/MemoryUserDatabase.java
java/org/apache/catalina/users/MemoryUserDatabaseFactory.java
java/org/apache/catalina/util/Base64.java
java/org/apache/catalina/util/CharsetMapper.java
java/org/apache/catalina/util/CustomObjectInputStream.java
java/org/apache/catalina/util/DefaultAnnotationProcessor.java
java/org/apache/catalina/util/Enumerator.java
java/org/apache/catalina/util/Extension.java
java/org/apache/catalina/util/ExtensionValidator.java
java/org/apache/catalina/util/InstanceSupport.java
java/org/apache/catalina/util/LifecycleSupport.java
java/org/apache/catalina/util/MD5Encoder.java
java/org/apache/catalina/util/MIME2Java.java
java/org/apache/catalina/util/ManifestResource.java
java/org/apache/catalina/util/ParameterMap.java
java/org/apache/catalina/util/RequestUtil.java
java/org/apache/catalina/util/ResourceSet.java
java/org/apache/catalina/util/ServerInfo.java
java/org/apache/catalina/util/Strftime.java
java/org/apache/catalina/util/StringParser.java
java/org/apache/catalina/util/URL.java
java/org/apache/catalina/valves/AccessLogValve.java
java/org/apache/catalina/valves/CometConnectionManagerValve.java
java/org/apache/catalina/valves/ErrorReportValve.java
java/org/apache/catalina/valves/ExtendedAccessLogValve.java
java/org/apache/catalina/valves/FastCommonAccessLogValve.java
java/org/apache/catalina/valves/PersistentValve.java
java/org/apache/catalina/valves/RemoteAddrValve.java
java/org/apache/catalina/valves/RemoteHostValve.java
java/org/apache/catalina/valves/RequestDumperValve.java
java/org/apache/catalina/valves/RequestFilterValve.java
java/org/apache/catalina/valves/SemaphoreValve.java
java/org/apache/catalina/valves/ValveBase.java
java/org/apache/coyote/ajp/LocalStrings.properties
java/org/apache/coyote/http11/LocalStrings.properties
java/org/apache/coyote/http11/LocalStrings_es.properties
java/org/apache/coyote/http11/LocalStrings_fr.properties
java/org/apache/coyote/http11/LocalStrings_ja.properties
java/org/apache/el/ExpressionFactoryImpl.java
java/org/apache/el/MethodExpressionImpl.java
java/org/apache/el/ValueExpressionImpl.java
java/org/apache/el/lang/ELArithmetic.java
java/org/apache/el/lang/ELSupport.java
java/org/apache/el/lang/ExpressionBuilder.java
java/org/apache/el/lang/FunctionMapperFactory.java
java/org/apache/el/lang/FunctionMapperImpl.java
java/org/apache/el/parser/ArithmeticNode.java
java/org/apache/el/parser/AstAnd.java
java/org/apache/el/parser/AstBracketSuffix.java
java/org/apache/el/parser/AstChoice.java
java/org/apache/el/parser/AstCompositeExpression.java
java/org/apache/el/parser/AstDeferredExpression.java
java/org/apache/el/parser/AstDiv.java
java/org/apache/el/parser/AstDotSuffix.java
java/org/apache/el/parser/AstDynamicExpression.java
java/org/apache/el/parser/AstEmpty.java
java/org/apache/el/parser/AstEqual.java
java/org/apache/el/parser/AstFalse.java
java/org/apache/el/parser/AstFloatingPoint.java
java/org/apache/el/parser/AstFunction.java
java/org/apache/el/parser/AstGreaterThan.java
java/org/apache/el/parser/AstGreaterThanEqual.java
java/org/apache/el/parser/AstIdentifier.java
java/org/apache/el/parser/AstInteger.java
java/org/apache/el/parser/AstLessThan.java
java/org/apache/el/parser/AstLessThanEqual.java
java/org/apache/el/parser/AstLiteralExpression.java
java/org/apache/el/parser/AstMinus.java
java/org/apache/el/parser/AstMod.java
java/org/apache/el/parser/AstMult.java
java/org/apache/el/parser/AstNegative.java
java/org/apache/el/parser/AstNot.java
java/org/apache/el/parser/AstNotEqual.java
java/org/apache/el/parser/AstNull.java
java/org/apache/el/parser/AstOr.java
java/org/apache/el/parser/AstPlus.java
java/org/apache/el/parser/AstString.java
java/org/apache/el/parser/AstTrue.java
java/org/apache/el/parser/AstValue.java
java/org/apache/el/parser/BooleanNode.java
java/org/apache/el/parser/ELParser.jjt
java/org/apache/el/parser/Node.java
java/org/apache/el/parser/NodeVisitor.java
java/org/apache/el/parser/SimpleNode.java
java/org/apache/el/util/MessageFactory.java
java/org/apache/el/util/ReflectionUtil.java
java/org/apache/jasper/compiler/JspRuntimeContext.java
java/org/apache/jasper/resources/LocalStrings.properties
java/org/apache/jasper/resources/LocalStrings_es.properties
java/org/apache/jasper/resources/LocalStrings_fr.properties
java/org/apache/jasper/resources/LocalStrings_ja.properties
java/org/apache/jasper/runtime/AnnotationHelper.java
java/org/apache/jasper/util/Enumerator.java
java/org/apache/jasper/xmlparser/ASCIIReader.java
java/org/apache/jasper/xmlparser/EncodingMap.java
java/org/apache/jasper/xmlparser/ParserUtils.java
java/org/apache/jasper/xmlparser/SymbolTable.java
java/org/apache/jasper/xmlparser/TreeNode.java
java/org/apache/jasper/xmlparser/UCSReader.java
java/org/apache/jasper/xmlparser/UTF8Reader.java
java/org/apache/jasper/xmlparser/XMLChar.java
java/org/apache/jasper/xmlparser/XMLString.java
java/org/apache/jasper/xmlparser/XMLStringBuffer.java
java/org/apache/jk/config/BaseJkConfig.java
java/org/apache/juli/FileHandler.java
java/org/apache/juli/logging/Log.java
java/org/apache/juli/logging/LogConfigurationException.java
java/org/apache/juli/logging/LogFactory.java
java/org/apache/naming/ContextAccessController.java
java/org/apache/naming/ContextBindings.java
java/org/apache/naming/EjbRef.java
java/org/apache/naming/JndiPermission.java
java/org/apache/naming/LocalStrings_es.properties
java/org/apache/naming/NameParserImpl.java
java/org/apache/naming/NamingContext.java
java/org/apache/naming/NamingContextBindingsEnumeration.java
java/org/apache/naming/NamingContextEnumeration.java
java/org/apache/naming/NamingEntry.java
java/org/apache/naming/NamingService.java
java/org/apache/naming/NamingServiceMBean.java
java/org/apache/naming/ResourceEnvRef.java
java/org/apache/naming/ResourceLinkRef.java
java/org/apache/naming/ResourceRef.java
java/org/apache/naming/SelectorContext.java
java/org/apache/naming/TransactionRef.java
java/org/apache/naming/factory/EjbFactory.java
java/org/apache/naming/factory/MailSessionFactory.java
java/org/apache/naming/factory/OpenEjbFactory.java
java/org/apache/naming/factory/ResourceEnvFactory.java
java/org/apache/naming/factory/ResourceFactory.java
java/org/apache/naming/factory/ResourceLinkFactory.java
java/org/apache/naming/factory/TransactionFactory.java
java/org/apache/naming/java/javaURLContextFactory.java
java/org/apache/naming/resources/BaseDirContext.java
java/org/apache/naming/resources/CacheEntry.java
java/org/apache/naming/resources/DirContextURLConnection.java
java/org/apache/naming/resources/DirContextURLStreamHandler.java
java/org/apache/naming/resources/DirContextURLStreamHandlerFactory.java
java/org/apache/naming/resources/FileDirContext.java
java/org/apache/naming/resources/ImmutableNameNotFoundException.java
java/org/apache/naming/resources/ProxyDirContext.java
java/org/apache/naming/resources/RecyclableNamingEnumeration.java
java/org/apache/naming/resources/Resource.java
java/org/apache/naming/resources/ResourceAttributes.java
java/org/apache/naming/resources/ResourceCache.java
java/org/apache/naming/resources/WARDirContext.java
java/org/apache/naming/resources/jndi/Handler.java
java/org/apache/tomcat/jni/Address.java
java/org/apache/tomcat/jni/BIOCallback.java
java/org/apache/tomcat/jni/Directory.java
java/org/apache/tomcat/jni/Error.java
java/org/apache/tomcat/jni/File.java
java/org/apache/tomcat/jni/FileInfo.java
java/org/apache/tomcat/jni/Global.java
java/org/apache/tomcat/jni/Library.java
java/org/apache/tomcat/jni/Local.java
java/org/apache/tomcat/jni/Lock.java
java/org/apache/tomcat/jni/Mmap.java
java/org/apache/tomcat/jni/Multicast.java
java/org/apache/tomcat/jni/OS.java
java/org/apache/tomcat/jni/PasswordCallback.java
java/org/apache/tomcat/jni/Poll.java
java/org/apache/tomcat/jni/Pool.java
java/org/apache/tomcat/jni/PoolCallback.java
java/org/apache/tomcat/jni/Proc.java
java/org/apache/tomcat/jni/ProcErrorCallback.java
java/org/apache/tomcat/jni/Procattr.java
java/org/apache/tomcat/jni/Registry.java
java/org/apache/tomcat/jni/SSL.java
java/org/apache/tomcat/jni/SSLContext.java
java/org/apache/tomcat/jni/SSLSocket.java
java/org/apache/tomcat/jni/Shm.java
java/org/apache/tomcat/jni/Sockaddr.java
java/org/apache/tomcat/jni/Socket.java
java/org/apache/tomcat/jni/Status.java
java/org/apache/tomcat/jni/Stdlib.java
java/org/apache/tomcat/jni/Time.java
java/org/apache/tomcat/jni/User.java
java/org/apache/tomcat/util/buf/Base64.java
java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java
java/org/apache/tomcat/util/digester/AbstractRulesImpl.java
java/org/apache/tomcat/util/digester/ArrayStack.java
java/org/apache/tomcat/util/digester/CallMethodRule.java
java/org/apache/tomcat/util/digester/CallParamRule.java
java/org/apache/tomcat/util/digester/Digester.java
java/org/apache/tomcat/util/digester/FactoryCreateRule.java
java/org/apache/tomcat/util/digester/GenericParser.java
java/org/apache/tomcat/util/digester/NodeCreateRule.java
java/org/apache/tomcat/util/digester/ObjectCreateRule.java
java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
java/org/apache/tomcat/util/digester/ObjectParamRule.java
java/org/apache/tomcat/util/digester/ParserFeatureSetterFactory.java
java/org/apache/tomcat/util/digester/PathCallParamRule.java
java/org/apache/tomcat/util/digester/Rule.java
java/org/apache/tomcat/util/digester/RuleSet.java
java/org/apache/tomcat/util/digester/RuleSetBase.java
java/org/apache/tomcat/util/digester/Rules.java
java/org/apache/tomcat/util/digester/RulesBase.java
java/org/apache/tomcat/util/digester/SetNextRule.java
java/org/apache/tomcat/util/digester/SetPropertiesRule.java
java/org/apache/tomcat/util/digester/SetPropertyRule.java
java/org/apache/tomcat/util/digester/SetRootRule.java
java/org/apache/tomcat/util/digester/SetTopRule.java
java/org/apache/tomcat/util/digester/WithDefaultsRulesWrapper.java
java/org/apache/tomcat/util/digester/XercesParser.java
java/org/apache/tomcat/util/http/BaseRequest.java
java/org/apache/tomcat/util/http/fileupload/DefaultFileItem.java
java/org/apache/tomcat/util/http/fileupload/DefaultFileItemFactory.java
java/org/apache/tomcat/util/http/fileupload/DeferredFileOutputStream.java
java/org/apache/tomcat/util/http/fileupload/DiskFileUpload.java
java/org/apache/tomcat/util/http/fileupload/FileItem.java
java/org/apache/tomcat/util/http/fileupload/FileItemFactory.java
java/org/apache/tomcat/util/http/fileupload/FileUpload.java
java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
java/org/apache/tomcat/util/http/fileupload/FileUploadException.java
java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
java/org/apache/tomcat/util/http/fileupload/ThresholdingOutputStream.java
java/org/apache/tomcat/util/http/fileupload/package.html
java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java
java/org/apache/tomcat/util/modeler/FeatureInfo.java
java/org/apache/tomcat/util/modeler/ManagedBean.java
java/org/apache/tomcat/util/modeler/NotificationInfo.java
java/org/apache/tomcat/util/modeler/ParameterInfo.java
java/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd
java/org/apache/tomcat/util/net/NioChannel.java
java/org/apache/tomcat/util/net/URL.java
java/org/apache/tomcat/util/res/StringManager.java

index 0fe2cac..85cda00 100644 (file)
@@ -13,7 +13,7 @@
 # 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
index 0a758a7..7969fe1 100644 (file)
@@ -1,7 +1,7 @@
 <!-- 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-->
index f0ca8d2..13e8920 100644 (file)
@@ -16,7 +16,7 @@
 -->
 <!--
         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
index d7af22a..97f9475 100644 (file)
@@ -25,7 +25,7 @@ import javax.naming.NamingException;
  * 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)
index 5877a99..96f6070 100644 (file)
@@ -27,7 +27,7 @@ package org.apache.catalina;
  * 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 {
index 7755415..6b33c86 100644 (file)
@@ -31,7 +31,7 @@ import java.net.URL;
  * @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 {
index 697f5ff..a67a672 100644 (file)
@@ -25,7 +25,7 @@ package org.apache.catalina;
  *
  * @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 {
index 05cf61a..04a73b3 100644 (file)
@@ -78,7 +78,7 @@ import org.apache.juli.logging.Log;
  *
  * @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 {
index 5533a06..998021c 100644 (file)
@@ -26,7 +26,7 @@ import java.util.EventObject;
  * 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
index e83dba4..e9402b1 100644 (file)
@@ -27,7 +27,7 @@ package org.apache.catalina;
  * 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 {
index 9419365..1fe3aff 100644 (file)
@@ -27,7 +27,7 @@ package org.apache.catalina;
  * 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 {
index d64b789..7a4096c 100644 (file)
@@ -50,7 +50,7 @@ import org.apache.catalina.util.CharsetMapper;
  * <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 {
index 393bb55..2e845d1 100644 (file)
@@ -41,7 +41,7 @@ package org.apache.catalina;
  * 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 {
index d3fe575..dc89b4e 100644 (file)
@@ -23,7 +23,7 @@ package org.apache.catalina;
  * 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 {
index a50b4d8..fb3ff93 100644 (file)
@@ -29,7 +29,7 @@ import java.util.Iterator;
  * 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
  */
 
index e183453..0ad833c 100644 (file)
@@ -41,7 +41,7 @@ package org.apache.catalina;
  * 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 {
index 3c81445..1fe6a6f 100644 (file)
@@ -32,7 +32,7 @@ import javax.servlet.ServletResponse;
  * 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
index a545bd5..d9062d9 100644 (file)
@@ -25,7 +25,7 @@ package org.apache.catalina;
  * 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 {
index 73f07b2..a549f3e 100644 (file)
@@ -26,7 +26,7 @@ package org.apache.catalina;
  * 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 {
index bd73ffc..9364cf1 100644 (file)
@@ -29,7 +29,7 @@ import java.util.EventObject;
  * 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
index 7801b66..3af7585 100644 (file)
@@ -25,7 +25,7 @@ package org.apache.catalina;
  * 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 {
index a61372c..3bdc677 100644 (file)
@@ -27,7 +27,7 @@ package org.apache.catalina;
  * 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 {
index 28b30d9..46d6853 100644 (file)
@@ -50,7 +50,7 @@ import java.beans.PropertyChangeListener;
  * </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 {
index 52a3901..8c214ca 100644 (file)
@@ -40,7 +40,7 @@ import java.io.IOException;
  * </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 {
index f7f8e6c..beb8fed 100644 (file)
@@ -36,7 +36,7 @@ package org.apache.catalina;
  *
  * @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 {
index 37ec068..6e5fa37 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.catalina.deploy.SecurityConstraint;
  * 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 {
index 08cc6ce..eddd0af 100644 (file)
@@ -27,7 +27,7 @@ import java.security.Principal;
  * 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
  */
 
index 547a7b2..014f6fd 100644 (file)
@@ -42,7 +42,7 @@ import org.apache.catalina.deploy.NamingResources;
  * 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 {
index f2fb382..be1e398 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.catalina.core.StandardServer;
  * 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 {
index bcb9639..a8e8d6a 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.catalina.connector.Connector;
  * 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 {
index c55e933..610084c 100644 (file)
@@ -31,7 +31,7 @@ import javax.servlet.http.HttpSession;
  * 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 {
index e65c585..69eea43 100644 (file)
@@ -26,7 +26,7 @@ import java.util.EventObject;
  * 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
index dd7d07c..3327250 100644 (file)
@@ -25,7 +25,7 @@ package org.apache.catalina;
  * 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 {
index 2533425..a9ddab0 100644 (file)
@@ -31,7 +31,7 @@ import java.io.IOException;
  * 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 {
index 86b703d..b6c48bd 100644 (file)
@@ -30,7 +30,7 @@ import java.util.Iterator;
  * 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
  */
 
index e377a6d..461ac51 100644 (file)
@@ -29,7 +29,7 @@ import java.util.Iterator;
  * 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
  */
 
index 72f9656..c319657 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.catalina.connector.Response;
  * @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 {
index e650505..a795dfe 100644 (file)
@@ -44,7 +44,7 @@ import javax.servlet.UnavailableException;
  * <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 {
index 332f514..fa579c1 100644 (file)
@@ -36,7 +36,7 @@ import org.apache.tools.ant.Project;
  * 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
  */
 
index 11fb0a1..ddab137 100644 (file)
@@ -46,7 +46,7 @@ import org.apache.tools.ant.types.RedirectorElement;
  * 
  *
  * @author Gabriele Garuglieri
- * @version $Revision: 303609 $ $Date: 2005-01-03 17:21:21 +0100 (lun., 03 janv. 2005) $
+ * @version $Revision$ $Date$
  * @since 5.5
  */
 
index 4c9beac..8eb11ec 100644 (file)
@@ -35,7 +35,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index 8e9d705..dd13c00 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.tools.ant.BuildException;
  * 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
  */
index 55e2b92..a0f5e05 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * mod_jk status (1.2.9) application.
  * 
  * @author Peter Rossbach
- * @version $Revision: 303886 $
+ * @version $Revision$
  * @since 5.5.9
  */
 public class JKStatusUpdateTask extends AbstractCatalinaTask {
index 23cbd91..064a7c0 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * supported by the Tomcat manager application.
  *
  * @author Peter Rossbach
- * @version $Revision: 303880 $
+ * @version $Revision$
  */
 public class JMXGetTask extends AbstractCatalinaTask {
 
index fdb6e9a..ceb89bf 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * (<code>/jmxproxy/?qry</code>) supported by the Tomcat manager application.
  *
  * @author Vivek Chopra
- * @version $Revision: 303236 $
+ * @version $Revision$
  */
 public class JMXQueryTask extends AbstractCatalinaTask {
 
index c286f85..5bab49b 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * supported by the Tomcat manager application.
  *
  * @author Vivek Chopra
- * @version $Revision: 303236 $
+ * @version $Revision$
  */
 public class JMXSetTask extends AbstractCatalinaTask {
 
index 778fceb..bc3dfdf 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index 2543e8d..75e21a6 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index ed8416c..5eb9395 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index cd1ce56..76cbb54 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index c96fa8d..4041e10 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index 3381789..352fdf8 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
 
index bdd438a..3e615d3 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.tools.ant.BuildException;
  * supported by the Tomcat manager application.
  *
  * @author Vivek Chopra
- * @version $Revision: 303657 $
+ * @version $Revision$
  */
 public class SessionsTask extends AbstractCatalinaTask {
 
index 271e5a9..260ea16 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index f1421d9..959331c 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index a09c5e1..2c0b81a 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index d8f96ce..ae9a3ba 100644 (file)
@@ -36,7 +36,7 @@ import org.xml.sax.InputSource;
  * 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
  */
 
index 04e3166..703c2e8 100644 (file)
@@ -1,23 +1,23 @@
-# 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
index 57003da..e5c4a8d 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.catalina.ant.jmx;
 /**
  *
  * @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 {
index 8ccb921..4c3ded3 100644 (file)
@@ -86,7 +86,7 @@ import org.apache.tools.ant.taskdefs.condition.Condition;
  * <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
  *
  */
index 5c26230..78861a0 100644 (file)
@@ -55,7 +55,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index fe39bab..4913767 100644 (file)
@@ -65,7 +65,7 @@ import org.apache.tools.ant.taskdefs.condition.Condition;
  * </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
  *
  */
index cfa7863..9fd0d60 100644 (file)
@@ -52,7 +52,7 @@ import org.apache.tools.ant.BuildException;
  * 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
  */
 
index 982c6a0..277753d 100644 (file)
@@ -87,7 +87,7 @@ import org.apache.tools.ant.BuildException;
  * 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
  */
 
index 9ac9bcf..1c292bb 100644 (file)
@@ -56,7 +56,7 @@ import org.apache.tools.ant.BuildException;
  * 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
  */
 
index 2e888e7..0e40d26 100644 (file)
@@ -58,7 +58,7 @@ import org.apache.tools.ant.BuildException;
  * 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
  */
 
index b701f49..a6d560d 100644 (file)
@@ -85,7 +85,7 @@ import org.apache.tools.ant.Project;
  * </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
  */
 
index b1a6179..4e30d74 100644 (file)
@@ -50,7 +50,7 @@ import org.apache.tools.ant.BuildException;
  * 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 {
index 2eee6dd..797e89e 100644 (file)
@@ -1,8 +1,8 @@
-# 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
index a176722..572ec9f 100644 (file)
@@ -70,7 +70,7 @@ import org.apache.juli.logging.LogFactory;
  * 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$
  */
 
 
index 5b08802..643cfd3 100644 (file)
@@ -42,7 +42,7 @@ import org.apache.tomcat.util.buf.MessageBytes;
  * 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
index 5004043..b629845 100644 (file)
@@ -44,7 +44,7 @@ import org.apache.juli.logging.LogFactory;
  *
  * @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
index fb9c5d5..ed423d7 100644 (file)
@@ -50,7 +50,7 @@ import org.apache.tomcat.util.http.MimeHeaders;
  *
  * @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
index 042f2ac..263838c 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.catalina.deploy.LoginConfig;
  * 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
index 466f3ef..5c9a666 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.catalina.deploy.LoginConfig;
  * 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
index be32a26..6bc6cd7 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.tomcat.util.buf.ByteChunk;
  * 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 {
index 4136e6a..23200c8 100644 (file)
@@ -59,7 +59,7 @@ import org.apache.catalina.valves.ValveBase;
  * </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
index 924d29d..7cf68ac 100644 (file)
@@ -28,7 +28,7 @@ import org.apache.catalina.authenticator.Constants;
  * 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
index 4786855..d9af270 100644 (file)
@@ -25,7 +25,7 @@ import java.io.IOException;
  * 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 {
index fc9886b..babc2f6 100644 (file)
@@ -49,7 +49,7 @@ import org.apache.tomcat.util.modeler.Registry;
  *
  * @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$
  */
 
 
index 2984466..05ef81a 100644 (file)
@@ -44,7 +44,7 @@ import org.apache.tomcat.util.net.SocketStatus;
  *
  * @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
index 08a3a98..5e10900 100644 (file)
@@ -25,7 +25,7 @@ import java.security.Principal;
  * 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 
index 7101820..61e3de2 100644 (file)
@@ -77,7 +77,7 @@ import org.apache.catalina.util.StringParser;
  *
  * @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
index ec89e5f..783b968 100644 (file)
@@ -44,7 +44,7 @@ import org.apache.catalina.security.SecurityUtil;
  * @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")
index c305a0b..d9c4bc8 100644 (file)
@@ -58,7 +58,7 @@ import org.apache.tomcat.util.net.URL;
  *
  * @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
index 22d29d9..af72652 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.catalina.security.SecurityUtil;
  *
  * @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 
index 21bc0dd..d313820 100644 (file)
@@ -61,7 +61,7 @@ import org.apache.tomcat.util.http.mapper.MappingData;
  *
  * @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
index a32855d..3a849b9 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.catalina.security.SecurityUtil;
  *
  * @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
index dddb2e7..b0bec2f 100644 (file)
@@ -61,7 +61,7 @@ import org.apache.juli.logging.LogFactory;
  * <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
index 0bb9165..375879e 100644 (file)
@@ -49,7 +49,7 @@ import org.apache.catalina.util.StringManager;
  * 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 {
index 4446ffb..e71e44a 100644 (file)
@@ -49,7 +49,7 @@ import org.apache.tomcat.util.log.SystemLogHandler;
  * 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 {
index 721602b..eb273db 100644 (file)
@@ -55,7 +55,7 @@ import org.apache.catalina.util.StringManager;
  *
  * @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 {
index a86ac23..ce24523 100644 (file)
@@ -42,7 +42,7 @@ import org.apache.catalina.util.StringManager;
  * 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 {
index 5637424..5bf0144 100644 (file)
@@ -43,7 +43,7 @@ import org.apache.catalina.util.StringManager;
  * 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 {
index ea12b90..8ebf384 100644 (file)
@@ -40,7 +40,7 @@ import org.apache.catalina.util.StringManager;
  * 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 {
index ff08f05..34ad43e 100644 (file)
@@ -35,7 +35,7 @@ import java.lang.reflect.InvocationTargetException;
  *
  * @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
  */
 
index 183e026..2b6efce 100644 (file)
@@ -51,7 +51,7 @@ import org.apache.tomcat.util.buf.MessageBytes;
  * 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
index 3e4aa96..f4f3667 100644 (file)
@@ -38,7 +38,7 @@ import org.apache.catalina.connector.Request;
  * 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
index 924da16..05f80aa 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.juli.logging.LogFactory;
  * 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
  */
 
index fe4f77a..c0702c8 100644 (file)
@@ -71,7 +71,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * 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
index 05e91a8..e645f95 100644 (file)
@@ -109,7 +109,7 @@ import org.apache.tomcat.util.modeler.Registry;
  *
  * @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
index c1be326..c473590 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.tomcat.util.buf.MessageBytes;
  * 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
index fd889f3..e6f01a8 100644 (file)
@@ -46,7 +46,7 @@ import org.apache.tomcat.util.modeler.modules.MbeansSource;
  * 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
index 7520f36..a388a71 100644 (file)
@@ -40,7 +40,7 @@ import org.apache.catalina.valves.ValveBase;
  * 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
index 3bf330f..2848667 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.tomcat.util.modeler.Registry;
  *
  * @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
index 6846680..861374f 100644 (file)
@@ -50,7 +50,7 @@ import org.apache.juli.logging.LogFactory;
  *
  * @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
index 9052c69..4d00476 100644 (file)
@@ -56,7 +56,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * (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 
index 37e0805..a08c226 100644 (file)
@@ -71,7 +71,7 @@ import org.apache.tomcat.util.modeler.Registry;
  *
  * @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
index a4c0749..88ac27a 100644 (file)
@@ -29,7 +29,7 @@ import javax.servlet.ServletContext;
  * 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
index b520c3a..b35149a 100644 (file)
@@ -47,7 +47,7 @@ import org.apache.tomcat.util.log.SystemLogHandler;
  * <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
index 9aca3c2..22a4883 100644 (file)
@@ -29,7 +29,7 @@ import java.io.Serializable;
  * 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 {
index 69ee22a..0e0f907 100644 (file)
@@ -28,7 +28,7 @@ import java.io.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 {
index 363d59d..7e0022c 100644 (file)
@@ -26,7 +26,7 @@ import java.io.Serializable;
  * an <code>&lt;env-entry&gt;</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 {
index f2b25e8..5d009af 100644 (file)
@@ -28,7 +28,7 @@ import java.io.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 {
index 5d8e9dd..fd4d03d 100644 (file)
@@ -27,7 +27,7 @@ import java.io.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 {
index a00058f..5e229ed 100644 (file)
@@ -27,7 +27,7 @@ import java.io.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 {
index ca4bf29..939fcf8 100644 (file)
@@ -28,7 +28,7 @@ import java.io.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 {
index d8796b4..629f403 100644 (file)
@@ -26,7 +26,7 @@ import java.io.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 {
index 2e8374a..abd3b95 100644 (file)
@@ -28,7 +28,7 @@ import java.util.Iterator;
  * an <code>&lt;res-env-refy&gt;</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 {
index 6f10df4..a29ec14 100644 (file)
@@ -29,7 +29,7 @@ import java.io.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 {
index b94b8fa..a1f4fd9 100644 (file)
@@ -29,7 +29,7 @@ import java.io.Serializable;
  * in a <code>&lt;filter&gt;</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 {
index ef255bf..3df2a86 100644 (file)
@@ -30,7 +30,7 @@ import java.io.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 {
index a377a4b..711830d 100644 (file)
@@ -29,7 +29,7 @@ import java.io.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 {
index a764e7e..d75522c 100644 (file)
@@ -25,7 +25,7 @@ package org.apache.catalina.deploy;
  * 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
  */
 
index d5ebcaa..b3c63d1 100644 (file)
@@ -27,7 +27,7 @@ import java.io.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
  */
 
index 8ba59d3..a297234 100644 (file)
@@ -31,7 +31,7 @@ import java.io.Serializable;
  * 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 {
index d83bd6e..def1367 100644 (file)
@@ -27,7 +27,7 @@ import java.util.HashMap;
  * 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 {
index 2ff3035..ead1a16 100644 (file)
@@ -39,7 +39,7 @@ import java.io.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 {
index 4060580..84a604f 100644 (file)
@@ -33,7 +33,7 @@ import java.io.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 {
index de60e2c..4c927b1 100644 (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;\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();
+    
+
+}
index 4823abc..4d38eec 100644 (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;\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);
+
+}
index a99bba4..cbb4a29 100644 (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;\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) ;
+
+}
index 0a5da49..f236854 100644 (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;\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();
+}
index 579a85a..5762a80 100644 (file)
@@ -1,34 +1,34 @@
-/*\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);
+}
index 118a0e8..1a41637 100644 (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.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
+    }
+
+}
index 6266c27..40e3c93 100644 (file)
@@ -1,40 +1,40 @@
-/*\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);
+   
+   
+
+}
index 1add40e..f2f268e 100644 (file)
@@ -1,40 +1,40 @@
-/*\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);
+}
index c2221e5..bbbf6ff 100644 (file)
@@ -1,31 +1,31 @@
-/*\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";
+}
index 35602a1..ec23373 100644 (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.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
index dc17bb7..8d877a1 100644 (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>&lt;description&gt;/&lt;version&gt;</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>&lt;description&gt;/&lt;version&gt;</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;
+    }
+
+}
index 51858cc..4358cac 100644 (file)
@@ -1,24 +1,24 @@
-/*\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);
+}
index acab7fd..4f2d0ef 100644 (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.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;
+    }
+    
+}
index 4386669..225f964 100644 (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.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
index 24f1c46..3cd284a 100644 (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;
+    }
+
+}
index 925b6c2..beb7636 100644 (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.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
index 1e39e4b..c4bc697 100644 (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;
+    }
+
+}
index bdcebd1..90a50f1 100644 (file)
@@ -1,75 +1,75 @@
-/*\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
index 14d2d03..14a9114 100644 (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);
+    }
+}
+
index 16e2e90..6fe996b 100644 (file)
@@ -1,32 +1,32 @@
-/*\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";
+
+}
index 3aae2f9..478d28f 100644 (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.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>&lt;description&gt;/&lt;version&gt;</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>&lt;description&gt;/&lt;version&gt;</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;
+    }
+}
index 0762255..83c6426 100644 (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.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();
+        }
+
+    }
+
+}
index 1104db5..96a2f8c 100644 (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.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);
+    }
+
+}
index b0e0a0b..8e574a9 100644 (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.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
- *  &lt;Cluster&gt;\r
- *  &lt;Valve className=&quot;org.apache.catalina.ha.session.JvmRouteBinderValve&quot; /&gt;  \r
- *  &lt;/Cluster&gt;\r
- * </pre>\r
- * <br />\r
- * Before 5.5.10 as Host element:<br/>\r
- * <pre>\r
- *  &lt;Hostr&gt;\r
- *  &lt;Valve className=&quot;org.apache.catalina.ha.session.JvmRouteBinderValve&quot; /&gt;  \r
- *  &lt;/Hostr&gt;\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>
+ *  &lt;Cluster&gt;
+ *  &lt;Valve className=&quot;org.apache.catalina.ha.session.JvmRouteBinderValve&quot; /&gt;  
+ *  &lt;/Cluster&gt;
+ * </pre>
+ * <br />
+ * Before 5.5.10 as Host element:<br/>
+ * <pre>
+ *  &lt;Hostr&gt;
+ *  &lt;Valve className=&quot;org.apache.catalina.ha.session.JvmRouteBinderValve&quot; /&gt;  
+ *  &lt;/Hostr&gt;
+ * </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"));
+
+    }
+
+}
index 509a136..6157f80 100644 (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.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);
+    }
+}
+
index 33d9ca4..2fe1d70 100644 (file)
-\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;
+    }
+
+}
index 7e3d223..a2240f9 100644 (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.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]);
+    }
+
+
+}
index e03cf5e..ecd3e03 100644 (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 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;
+       }
+
+    
+
+
+}
+
index ab28acf..3408657 100644 (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
-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
index 137a9e1..cf89664 100644 (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
-\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;
+    }
+
+}
index b4e89cd..2360d66 100644 (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.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();
+    }
+
+}
index 01fa257..6d3f7a6 100644 (file)
@@ -1,33 +1,33 @@
-/*\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";
+
+}
index 733dae4..524680b 100644 (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.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));
+                    }
+                }
+            }
+        }
+    }
+
+}
index 4fa914f..79f5cc4 100644 (file)
@@ -20,7 +20,7 @@ import org.apache.catalina.tribes.Member;
 
 /**
  * @author Peter Rossbach
- * @version $Revision: 303987 $ $Date: 2005-07-08 15:50:30 -0500 (Fri, 08 Jul 2005) $
+ * @version $Revision$ $Date$
  */
 public class SendMessageData {
 
index be8c8fc..ae5e342 100644 (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.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>&lt;description&gt;/&lt;version&gt;</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 &lt;Manager&gt; 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>&lt;description&gt;/&lt;version&gt;</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 &lt;Manager&gt; 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 {
+        
+    }
+}
index 645f349..f88d66b 100644 (file)
@@ -1,58 +1,58 @@
-/*\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) ;
+
+}
index 0711062..d6c58b5 100644 (file)
@@ -25,7 +25,7 @@ package org.apache.catalina.loader;
  * 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 {
index 2817f2c..6449d1c 100644 (file)
@@ -26,7 +26,7 @@ import java.util.jar.Manifest;
  * 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 {
 
index 2c80459..b569a5d 100644 (file)
@@ -42,7 +42,7 @@ import java.net.URLClassLoader;
  *
  * @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
index c5ea26a..db9a884 100644 (file)
@@ -22,7 +22,7 @@ package org.apache.catalina.loader;
  * 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 {
 }
index 2001dd2..a7648da 100644 (file)
@@ -99,7 +99,7 @@ import org.apache.tomcat.util.IntrospectionUtils;
  *
  * @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
index 554ddc8..08f233c 100644 (file)
@@ -79,7 +79,7 @@ import org.apache.tomcat.util.modeler.Registry;
  *
  * @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
index 7deea1d..b267f44 100644 (file)
@@ -57,7 +57,7 @@ import org.apache.tomcat.util.http.fileupload.FileItem;
 * @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
 */
 
index bad0c69..fce3393 100644 (file)
@@ -152,7 +152,7 @@ import org.apache.tomcat.util.modeler.Registry;
  *
  * @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
index de55780..51596a1 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * 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
index ddf2e98..63c2b3c 100644 (file)
@@ -42,7 +42,7 @@ import org.apache.catalina.util.RequestUtil;
  * 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 {
index effea1d..55fc94b 100644 (file)
@@ -53,7 +53,7 @@ import org.apache.catalina.util.ServerInfo;
 * @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
 */
 
index 3cfdc2a..3912018 100644 (file)
@@ -94,7 +94,7 @@ import org.apache.catalina.core.ContainerBase;
  *
  * @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
index 1b55d3a..8cab9de 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.tomcat.util.modeler.BaseModelMBean;
  * 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 {
index b97ec37..17cc794 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.tomcat.util.IntrospectionUtils;
  * <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 {
index bda039f..e5825ff 100644 (file)
@@ -36,7 +36,7 @@ import org.apache.tomcat.util.modeler.BaseModelMBean;
  * <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 {
index c79ef9b..3449a6b 100644 (file)
@@ -36,7 +36,7 @@ import org.apache.tomcat.util.modeler.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 {
index 651ba14..8329e71 100644 (file)
@@ -36,7 +36,7 @@ import org.apache.tomcat.util.modeler.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 {
index 69cf7bc..303105e 100644 (file)
@@ -38,7 +38,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index 765d160..ef66366 100644 (file)
@@ -43,7 +43,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * 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
  */
 
index 6cad755..bb92e5b 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index 8c00ed5..43f213b 100644 (file)
@@ -62,7 +62,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index 2b83206..94501ee 100644 (file)
@@ -60,7 +60,7 @@ import org.apache.tomcat.util.modeler.Registry;
  *
  * @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 {
index 08be635..20e7a9d 100644 (file)
@@ -40,7 +40,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index 29215d1..c3e6d6f 100644 (file)
@@ -37,7 +37,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index 18967be..ca2e430 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index 2a9120c..1a2d761 100644 (file)
@@ -59,7 +59,7 @@ import org.apache.juli.logging.LogFactory;
  *
  * @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
index b10eb1f..0971646 100644 (file)
@@ -38,7 +38,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index b303219..0e0d7e9 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.tomcat.util.modeler.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 {
index 7692a32..cb90024 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index 0463bd4..3599da3 100644 (file)
@@ -33,7 +33,7 @@ import org.apache.tomcat.util.modeler.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 {
index d6e53e0..1d57460 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.tomcat.util.modeler.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 {
index adcd02b..104f25a 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * <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 {
index eb29ad2..6507c42 100644 (file)
@@ -24,7 +24,7 @@ package org.apache.catalina.realm;
  *
  *
  * @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 {
index 9766641..b2f0c4a 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.catalina.util.StringManager;
 * @author Craig R. McClanahan
 * @author Carson McDonald
 * @author Ignacio Ortega
-* @version $Revision: 394121 $
+* @version $Revision$
 */
 
 public class DataSourceRealm
index d181af4..f66c082 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.catalina.Realm;
  * 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 {
index bee4ede..0a7c434 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.juli.logging.LogFactory;
  *
  * @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 {
index 5db1296..c4255f9 100644 (file)
@@ -73,7 +73,7 @@ import org.apache.tomcat.util.digester.Digester;
  * 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 {
index 28ccb4b..530a911 100644 (file)
@@ -120,7 +120,7 @@ org.foobar.auth.DatabaseLoginModule REQUIRED
 *
 * @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
index ab9573b..df1b56d 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.catalina.util.StringManager;
 * @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
index 7c1c21d..a5144c6 100644 (file)
@@ -150,7 +150,7 @@ import org.apache.tomcat.util.buf.CharChunk;
  *
  * @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 {
index a414182..5f90f80 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings.properties 303678 2005-02-03 15:14:34Z remm $
+# $Id$
 
 # language
 
index 94696f7..105f348 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings_es.properties 302991 2004-07-03 04:16:41Z billbarker $
+# $Id$
 
 # language es
 
index 5750597..46b3ba0 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings_fr.properties 302991 2004-07-03 04:16:41Z billbarker $
+# $Id$
 
 # language
 
index b4868f1..7dcf209 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings_ja.properties 302991 2004-07-03 04:16:41Z billbarker $
+# $Id$
 
 # language ja
 
index 274e0e5..88b4919 100644 (file)
@@ -43,7 +43,7 @@ import org.apache.tomcat.util.digester.Digester;
  * 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 {
index 66512be..788cefc 100644 (file)
@@ -30,7 +30,7 @@ import org.xml.sax.Attributes;
  * 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 {
index 8e763a0..a184f52 100644 (file)
@@ -61,7 +61,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * 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
index 2fef268..18a242d 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.catalina.util.StringManager;
  * 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
  */
 
index 98fd56d..71c644d 100644 (file)
@@ -25,7 +25,7 @@ package org.apache.catalina.security;
  *
  * @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 {
index 31742eb..b685fea 100644 (file)
@@ -235,7 +235,7 @@ import org.apache.catalina.util.IOTools;
  *
  * @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
  *
  */
@@ -694,7 +694,7 @@ public final class CGIServlet extends HttpServlet {
      * <p>
      * </p>
      *
-     * @version  $Revision: 421476 $, $Date: 2006-07-12 22:08:28 -0400 (Wed, 12 Jul 2006) $
+     * @version  $Revision$, $Date$
      * @since    Tomcat 4.0
      *
      */
@@ -1421,7 +1421,7 @@ public final class CGIServlet extends HttpServlet {
      * 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 {
index ffba049..46bb7e8 100644 (file)
@@ -71,7 +71,7 @@ import org.apache.naming.resources.ResourceAttributes;
  *
  * @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
index 3138db5..71f1431 100644 (file)
@@ -30,7 +30,7 @@ import javax.servlet.http.HttpServletRequestWrapper;
  * 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 {
index a21a1b6..de498ef 100644 (file)
@@ -41,7 +41,7 @@ import org.apache.catalina.util.StringManager;
  * 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
index 44ae4ac..0483a08 100644 (file)
@@ -66,7 +66,7 @@ import org.xml.sax.SAXException;
  * 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
index 3a63630..9ce457c 100644 (file)
@@ -47,7 +47,7 @@ import org.apache.catalina.util.CustomObjectInputStream;
  * 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
index c33c1ed..685f85e 100644 (file)
@@ -45,7 +45,7 @@ import java.util.Properties;
  * 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
index a2fa69c..eeaf1cb 100644 (file)
@@ -60,7 +60,7 @@ import org.apache.tomcat.util.modeler.Registry;
  * 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 {
index 29a8a9d..0959c7d 100644 (file)
@@ -29,7 +29,7 @@ package org.apache.catalina.session;
  * <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)
  */
 
index ff07867..9fc4486 100644 (file)
@@ -47,7 +47,7 @@ import org.apache.juli.logging.LogFactory;
  *
  * @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
index 490bc8f..23dea93 100644 (file)
@@ -59,7 +59,7 @@ import org.apache.catalina.security.SecurityUtil;
  *
  * @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
index 22e1b6a..6b29c74 100644 (file)
@@ -77,7 +77,7 @@ import org.apache.catalina.security.SecurityUtil;
  * @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
index 4a42b7a..3c94f3e 100644 (file)
@@ -30,7 +30,7 @@ import javax.servlet.http.HttpSessionContext;
  * 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
index 526eb9a..0eddfd6 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.catalina.util.StringManager;
  * 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
index ace4911..f16c335 100644 (file)
@@ -26,7 +26,7 @@ import javax.servlet.ServletOutputStream;
  * <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 {
index 903b076..6328f43 100644 (file)
@@ -17,7 +17,7 @@ import java.util.List;
 /**
  * Represents a parsed expression.
  * 
- * @version $Revision: 303166 $
+ * @version $Revision$
  * @author Paul Speed
  */
 public class ExpressionParseTree {
index 9e006c3..2427361 100644 (file)
@@ -16,7 +16,7 @@ package org.apache.catalina.ssi;
  * 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 {
index e6bfb78..f214c7a 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.catalina.util.DateTool;
  * 
  * @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 {
     /**
index 1b0557d..d0cbbd9 100644 (file)
@@ -19,7 +19,7 @@ import java.io.PrintWriter;
  * @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 {
     /**
index 3febc7f..6a2664d 100644 (file)
@@ -16,7 +16,7 @@ import java.text.ParseException;
 /**
  * SSI command that handles all conditional directives.
  * 
- * @version $Revision: 303882 $
+ * @version $Revision$
  * @author Paul Speed
  * @author David Becker
  */
index cb8df77..31c1db6 100644 (file)
@@ -16,7 +16,7 @@ package org.apache.catalina.ssi;
  * information necessary to process the nested conditional commands ( if, elif,
  * else, endif ).
  * 
- * @version $Revision: 303166 $
+ * @version $Revision$
  * @author Dan Sandberg
  * @author Paul Speed
  */
index 8205db0..6ef561e 100644 (file)
@@ -19,7 +19,7 @@ import java.io.PrintWriter;
  * @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 {
     /**
index 605f984..8df9441 100644 (file)
@@ -19,7 +19,7 @@ import java.io.PrintWriter;
  * @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";
index c0c4f9c..aa40645 100644 (file)
@@ -24,7 +24,7 @@ import org.apache.catalina.util.IOTools;
  * @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();
index f944d39..9364d8d 100644 (file)
@@ -19,7 +19,7 @@ import java.util.Date;
  * 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 {
     /**
index 57b73e5..d4231fd 100644 (file)
@@ -38,7 +38,7 @@ import org.apache.catalina.Globals;
  * 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 {
index 16ba362..2d3bef5 100644 (file)
@@ -23,7 +23,7 @@ import org.apache.catalina.util.Strftime;
  * @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 {
     /**
index 157ed18..683c2b8 100644 (file)
@@ -21,7 +21,7 @@ import java.text.DecimalFormat;
  * @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;
index 991be71..4e25683 100644 (file)
@@ -20,7 +20,7 @@ import java.io.PrintWriter;
  * @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 {
     /**
index e585e4f..18f4e87 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.catalina.util.URLEncoder;
  * @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]";
index 68b7b5c..772097d 100644 (file)
@@ -19,7 +19,7 @@ import java.util.Iterator;
  * 
  * @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 {
     /**
index dfeb8e0..0476ccd 100644 (file)
@@ -25,7 +25,7 @@ import org.apache.catalina.util.IOTools;
  * 
  * @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 */
index e28a94c..2e03d1e 100644 (file)
@@ -33,7 +33,7 @@ import org.apache.catalina.Globals;
  * @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. */
index 9b0c764..eefbd59 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.coyote.Constants;
  * 
  * @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",
index b125017..ea9f871 100644 (file)
@@ -18,7 +18,7 @@ import java.io.PrintWriter;
  * @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 {
     /**
index 8a8a50e..3f62bca 100644 (file)
@@ -18,7 +18,7 @@ package org.apache.catalina.ssi;
  * 
  * @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
index 28bc37b..b775f0a 100644 (file)
@@ -46,7 +46,7 @@ import org.apache.juli.logging.LogFactory;
  *
  * @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 {
index 1f9b76d..ae2692b 100644 (file)
@@ -52,7 +52,7 @@ import org.xml.sax.InputSource;
  *
  * @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 {
index 1e0bd15..a3bc765 100644 (file)
@@ -30,7 +30,7 @@ import java.util.Properties;
  * 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 {
index 6d83660..a9d36b7 100644 (file)
@@ -44,7 +44,7 @@ import org.apache.juli.logging.LogFactory;
  * </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 {
index f3589f3..ea1494d 100644 (file)
@@ -85,7 +85,7 @@ public class ClusterRuleSetFactory {
      *
      * @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 {
index 7e4cbb6..308b3dd 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index c7c6776..2cc9ba5 100644 (file)
@@ -24,7 +24,7 @@ package org.apache.catalina.startup;
  *
  * @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 {
index 73df31c..27328a0 100644 (file)
@@ -64,7 +64,7 @@ import org.xml.sax.SAXParseException;
  *
  * @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
index 490b1dd..62edc98 100644 (file)
@@ -35,7 +35,7 @@ import org.xml.sax.Attributes;
  * 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 {
index 5154432..7804608 100644 (file)
@@ -32,7 +32,7 @@ import org.xml.sax.Attributes;
  * <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 {
index 9be9748..30f3b81 100644 (file)
@@ -98,7 +98,7 @@ import org.apache.tomcat.util.log.SystemLogHandler;
  * 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 {
index 683f38f..a29d008 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.catalina.util.StringManager;
  * 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
index 545c4a6..1e95b8f 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.tomcat.util.digester.RuleSetBase;
  * <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 {
index 5a22a2f..a091268 100644 (file)
@@ -42,7 +42,7 @@ import org.apache.juli.logging.LogFactory;
  * @author Craig R. McClanahan
  * @author Remy Maucherat
  * @author Glenn L. Nielsen
- * @version $Revision: 303769 $
+ * @version $Revision$
  */
 
 public class ExpandWar {
index 663e7df..0707123 100644 (file)
@@ -30,7 +30,7 @@ import java.util.Enumeration;
  * 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
index 2f5fe37..dbdf82e 100644 (file)
@@ -52,7 +52,7 @@ import org.apache.tomcat.util.modeler.Registry;
  *
  * @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 {
index 2b533f1..c3b594d 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.tomcat.util.digester.RuleSetBase;
  * 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 {
index 76d902c..10fdd69 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.tomcat.util.digester.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 {
index 4ad7041..82e6ec8 100644 (file)
@@ -31,7 +31,7 @@ import java.util.Enumeration;
  * 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
index 9e3b1f7..253d576 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index b24b540..e11ea99 100644 (file)
@@ -28,7 +28,7 @@ import org.apache.tomcat.util.digester.RuleSetBase;
  * 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 {
index 9faee0c..91c52c2 100644 (file)
@@ -67,7 +67,7 @@ import org.apache.juli.logging.LogFactory;
  * </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 {
index 994dbe3..5889cab 100644 (file)
@@ -38,7 +38,7 @@ import org.apache.catalina.util.StringManager;
  * 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
index 8cc0b54..580143a 100644 (file)
@@ -27,7 +27,7 @@ import java.util.Enumeration;
  * 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 {
index b1fa073..18e57cb 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.catalina.deploy.MessageDestinationRef;
  * 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 {
index 38d6da5..cc4f631 100644 (file)
@@ -39,7 +39,7 @@ import org.xml.sax.Attributes;
  * 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 {
index ee35677..1418179 100644 (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.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);
+    }
+
+}
index c085918..73760d2 100644 (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.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);
+
+    
+}
index d7a885f..8519daa 100644 (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.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();
+        }
+    }
+
+}
index b457ef1..b5c8514 100644 (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 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();
+    }
+    
+
+}
index ad29211..8d97777 100644 (file)
@@ -1,68 +1,68 @@
-/*\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();
+
+}
index 98a438c..1efedd9 100644 (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.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();
+}
index 3aaae9c..36c77da 100644 (file)
@@ -1,75 +1,75 @@
-/*\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();
+
+}
index 1dea5f8..815bc08 100644 (file)
@@ -1,69 +1,69 @@
-/*\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;
+}
index 8e7feb2..57ccb8e 100644 (file)
@@ -1,32 +1,32 @@
-/*\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";
+}
index 4b79be0..db5de4f 100644 (file)
@@ -1,46 +1,46 @@
-/*\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
index 2127730..7cd91e4 100644 (file)
@@ -1,34 +1,34 @@
-/*\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
index 98d7e2c..79ae9a8 100644 (file)
@@ -1,78 +1,78 @@
-/*\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();
+}
index 351aa77..149a546 100644 (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
- * 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();
+}
index 50e34bc..be32019 100644 (file)
@@ -1,45 +1,45 @@
-/*\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
index 95256de..ff2da42 100644 (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);
+
+}
index d9458f6..02adf5a 100644 (file)
@@ -1,43 +1,43 @@
-/*\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();
+
+}
index 4be5c43..db69de0 100644 (file)
@@ -1,72 +1,72 @@
-/*\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
index b3634dd..c185f4e 100644 (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);
+    }
+
+   
+}
index 3c1b6d6..5359666 100644 (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.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
+    }
+
+
+}
index c12529e..251f13a 100644 (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
-\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
+
+
+
+}
index 61b7bcf..27ef3e8 100644 (file)
@@ -1,35 +1,35 @@
-/*\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
index 03ee1d0..16e01a8 100644 (file)
@@ -1,54 +1,54 @@
-/*\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
index e19eea3..93140db 100644 (file)
@@ -1,46 +1,46 @@
-/*\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
index 2fb35ab..2239e35 100644 (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
index 53db66e..0bf1eb8 100644 (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);
+        }
+    }    
+
+
+}
index 5ccd5bf..f2e898f 100644 (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
-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;
+    }
+}
index cf8e673..cee3955 100644 (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.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
index de69869..2515d59 100644 (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");
+        
+    }
+    
+}
index 0510218..03104e2 100644 (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
-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
index fa75355..afcda25 100644 (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;
+    }
+
+
+}
index 3e0b244..8052842 100644 (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
-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
index c0610c4..5f6626f 100644 (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;
+    }
+
+}
index 2169c51..59c1fea 100644 (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
-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 &quot;timed out&quot;\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 &quot;timed out&quot;
+ * 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
index 537cf55..e9636a9 100644 (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;
+    }
+
+}
index dd5d224..28fbbc3 100644 (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.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
index 62b9160..39f6e44 100644 (file)
@@ -1,94 +1,94 @@
-/*\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();
+    }    
+}
index 95de910..55c696d 100644 (file)
@@ -1,70 +1,70 @@
-/*\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;
+    }
+
+}
index 6cd2974..a1d32d4 100644 (file)
@@ -1,63 +1,63 @@
-/*\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;
+    }
+
+}
index 1197024..0879fcb 100644 (file)
@@ -1,40 +1,40 @@
-/*\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()));
+    }
+}
index 7ae1b73..da85409 100644 (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.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>&lt;description&gt;/&lt;version&gt;</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>&lt;description&gt;/&lt;version&gt;</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);
+    }
+}
index c35234a..580e117 100644 (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.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
+}
index 7728fcd..fc40dad 100644 (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.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);
+    }
+    
+}
index 869df34..355e8d9 100644 (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.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;
+        }
+    }
+}
index 4d51b9f..933b36d 100644 (file)
@@ -1,80 +1,80 @@
-/*\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
index 53382e5..a17379e 100644 (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
index 64dbfeb..55556a7 100644 (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
index 7879d81..49fc8ab 100644 (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
index 78bcb85..ad00498 100644 (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
index a38adb2..5607bb2 100644 (file)
@@ -1,61 +1,61 @@
-/*\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
- * &nbsp;&nbsp;int length = st.read(data,0,data.length);\r
- * &nbsp;&nbsp;String s = new String(data,0,length);\r
- * &nbsp;&nbsp;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() ) {
+ * &nbsp;&nbsp;int length = st.read(data,0,data.length);
+ * &nbsp;&nbsp;String s = new String(data,0,length);
+ * &nbsp;&nbsp;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
index 42b6477..6192403 100644 (file)
@@ -1,43 +1,43 @@
-/*\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);
+
+}
index e9c98c7..88ce300 100644 (file)
@@ -1,47 +1,47 @@
-/*\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
index a20b587..1a5f1e7 100644 (file)
@@ -1,38 +1,38 @@
-/*\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);
+}
index d7d558d..7524b16 100644 (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;\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
index d2f33b5..d7e14b7 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.catalina.tribes.transport.nio.PooledParallelSender;
  * 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);
index 0713147..c8eebbf 100644 (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
-\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
+
+}
index fbe1e3e..e15a018 100644 (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 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();
+    }
+}
index d5e3051..8c7df84 100644 (file)
@@ -1,89 +1,89 @@
-/*\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;
+    }
+}
index 4edc9ee..61ae793 100644 (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.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();
+    }
+}
index 2b97624..496e069 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.catalina.tribes.util.StringManager;
  * 
  * @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 {
index 064a0f0..39395ad 100644 (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.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;
+    }
+
+}
index f649767..a21c1f1 100644 (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.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;
+    }
+
+}
index dd008c3..28ca2e0 100644 (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.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();
+        }
+    }
+
+}
index 5d916ff..fac742c 100644 (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.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>&lt;description&gt;/&lt;version&gt;</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>&lt;description&gt;/&lt;version&gt;</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);
+        }
+    }
+
+
+}
index 44308e9..7dac62b 100644 (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.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;
+    }
+}
index 38f2364..b295ef8 100644 (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.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;
+    }
+}
index 12d6316..e02e566 100644 (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
-\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
index 582785b..5b9fa4e 100644 (file)
@@ -1,85 +1,85 @@
-/*\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
index b7488ab..5b92e0f 100644 (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
index 12e1d91..23b1d34 100644 (file)
@@ -1,29 +1,29 @@
-/*\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" );
+}
index dd4e889..e2b2229 100644 (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.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;
+    }
+}
index b450d35..4514f49 100644 (file)
@@ -1,77 +1,77 @@
-/*\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
index 1831c27..e41c3a7 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.catalina.UserDatabase;
  * <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
  */
 
index e950dbd..83aa863 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.catalina.UserDatabase;
  * <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
  */
 
index 2665ff5..46c4d09 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.catalina.User;
  * <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
  */
 
index 9cd49eb..a0b3487 100644 (file)
@@ -24,7 +24,7 @@ package org.apache.catalina.users;
  *
  *
  * @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
  */
 
index 4e1a638..94d7c46 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.catalina.UserDatabase;
  * {@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
  */
 
index f0f91c6..7716901 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.catalina.UserDatabase;
  * {@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
  */
 
index e7a4139..0270350 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.catalina.util.RequestUtil;
  * {@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
  */
 
index d4f26ba..6533d18 100644 (file)
@@ -45,7 +45,7 @@ import org.xml.sax.Attributes;
  * 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
  */
 
index 6a1906d..00f6733 100644 (file)
@@ -45,7 +45,7 @@ import javax.naming.spi.ObjectFactory;
  * </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
  */
 
index c01bded..3148a84 100644 (file)
@@ -28,7 +28,7 @@ import org.apache.tomcat.util.buf.CharChunk;
  * 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
 {
index f52395b..80378b1 100644 (file)
@@ -33,7 +33,7 @@ import java.util.Properties;
  * 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 {
index 3547775..f5f7e11 100644 (file)
@@ -30,7 +30,7 @@ import java.lang.reflect.Proxy;
  *
  * @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
index be2ee92..0c5039c 100644 (file)
@@ -39,7 +39,7 @@ import org.apache.AnnotationProcessor;
  *
  * @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 {
     
index 0944103..d04b4f1 100644 (file)
@@ -35,7 +35,7 @@ import java.util.NoSuchElementException;
  * 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 {
index 18960bb..87cb396 100644 (file)
@@ -41,7 +41,7 @@ import java.util.StringTokenizer;
  * @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 {
index f58318a..a476501 100644 (file)
@@ -47,7 +47,7 @@ import org.apache.naming.resources.Resource;
  *
  * @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 {
index 6f82442..90560fe 100644 (file)
@@ -33,7 +33,7 @@ import org.apache.catalina.Wrapper;
  * registered InstanceListeners.
  *
  * @author Craig R. McClanahan
- * @version $Id: InstanceSupport.java 303133 2004-08-29 16:46:15Z yoavs $
+ * @version $Id$
  */
 
 public final class InstanceSupport {
index 32dd060..82fe25d 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.catalina.LifecycleListener;
  * registered LifecycleListeners.
  *
  * @author Craig R. McClanahan
- * @version $Id: LifecycleSupport.java 302726 2004-02-27 14:59:07Z yoavs $
+ * @version $Id$
  */
 
 public final class LifecycleSupport {
index 5e1f1c9..3643168 100644 (file)
@@ -27,7 +27,7 @@ package org.apache.catalina.util;
  * 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 {
index 45eff4d..831c177 100644 (file)
@@ -466,7 +466,7 @@ import java.util.*;
  *  </TR>
  * </TABLE>
  *
- * @version $Revision: 302726 $ $Date: 2004-02-27 15:59:07 +0100 (ven., 27 févr. 2004) $
+ * @version $Revision$ $Date$
  * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
  */
 public class MIME2Java {
index 6bef95d..29806db 100644 (file)
@@ -27,7 +27,7 @@ import java.util.ArrayList;
  *  
  * @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 {
index 50836f7..c56243c 100644 (file)
@@ -31,7 +31,7 @@ import java.util.Map;
  * <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 {
index a29adc4..5840aec 100644 (file)
@@ -32,7 +32,7 @@ import javax.servlet.http.Cookie;
  *
  * @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 {
index 4187ace..efc726a 100644 (file)
@@ -31,7 +31,7 @@ import java.util.HashSet;
  * 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 {
index 52d4ebd..18aadf2 100644 (file)
@@ -28,7 +28,7 @@ import java.util.Properties;
  * 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 {
index 3799062..9574deb 100644 (file)
@@ -40,7 +40,7 @@ import java.util.TimeZone;
  *
  * @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;
index 8f2f6d9..2b804b8 100644 (file)
@@ -28,7 +28,7 @@ package org.apache.catalina.util;
  * 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 {
index 5159ace..1bd5b2e 100644 (file)
@@ -42,7 +42,7 @@ import java.net.MalformedURLException;
  * 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 {
index 08008bd..0afdd21 100644 (file)
@@ -105,7 +105,7 @@ import org.apache.catalina.util.StringManager;
  *
  * @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
index 59d6716..48e344f 100644 (file)
@@ -48,7 +48,7 @@ import org.apache.catalina.util.StringManager;
  * <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
index 6775e1d..29b6006 100644 (file)
@@ -48,7 +48,7 @@ import org.apache.tomcat.util.IntrospectionUtils;
  * @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
index 9388cd2..252164f 100644 (file)
@@ -130,7 +130,7 @@ import org.apache.juli.logging.LogFactory;
  *
  *
  * @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
index 3d151da..38c6ab3 100644 (file)
@@ -55,7 +55,7 @@ import org.apache.catalina.util.StringManager;
  * @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
index f3c9a7e..1b7da1e 100644 (file)
@@ -42,7 +42,7 @@ import org.apache.catalina.util.StringManager;
  * <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
index 41893e9..4ea725e 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.catalina.connector.Response;
  * 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
index ca4d794..b6d67d1 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.catalina.connector.Response;
  * 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
index 1097ade..ad8c50b 100644 (file)
@@ -41,7 +41,7 @@ import org.apache.juli.logging.Log;
  * 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
index 8a40048..9d8c12c 100644 (file)
@@ -62,7 +62,7 @@ import org.apache.catalina.util.StringManager;
  * 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
index e8d4009..e366a93 100644 (file)
@@ -40,7 +40,7 @@ import org.apache.catalina.util.StringManager;
  * 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
index 2009853..e3e5792 100644 (file)
@@ -52,7 +52,7 @@ import org.apache.juli.logging.LogFactory;
  * 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
index c175a82..491b5ef 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings.properties 301021 2005-08-01 10:00:59Z remm $
+# $Id$
 
 # language 
 
index 883df52..980feb1 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings.properties 301004 2005-07-29 10:23:56Z remm $
+# $Id$
 
 # language 
 
index 63cd15b..1d6002c 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings_es.properties 299220 2004-02-24 08:50:56Z hgomez $
+# $Id$
 
 # language es
 
index 374635c..c4d53a8 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings_fr.properties 299220 2004-02-24 08:50:56Z hgomez $
+# $Id$
 
 # language fr
 
index e4cd208..653de69 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings_ja.properties 299758 2004-08-30 17:29:47Z amyroh $
+# $Id$
 
 # language ja
 
index 8b30e92..79718bd 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.el.util.MessageFactory;
  * @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 {
 
index 3381390..12beb2d 100644 (file)
@@ -76,7 +76,7 @@ import org.apache.el.util.ReflectionUtil;
  * @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 {
index 31f38d1..759a0fd 100644 (file)
@@ -87,7 +87,7 @@ import org.apache.el.util.ReflectionUtil;
  * @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 {
index c248f75..752a6e3 100644 (file)
@@ -26,7 +26,7 @@ import org.apache.el.util.MessageFactory;
 /**
  * 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 {
 
index 793ce2a..3ecbd99 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.el.util.MessageFactory;
  * 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 {
 
index 994bf26..a8bca8f 100644 (file)
@@ -51,7 +51,7 @@ import org.apache.el.util.MessageFactory;
 
 /**
  * @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 {
 
index 7745cee..63d2a6e 100644 (file)
@@ -23,7 +23,7 @@ import javax.el.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 FunctionMapperFactory extends FunctionMapper {
 
index 012ffd4..0a938d0 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.el.util.ReflectionUtil;
 
 /**
  * @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 {
index 2e8694e..74b9bd4 100644 (file)
@@ -24,7 +24,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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 {
 
index 41f8e8f..feaac6f 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 2734d83..9afd6d8 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 02c1307..b3969ea 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index db3d7eb..843fcdd 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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 {
 
index d965b8a..b63299d 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index ca0095d..8309452 100644 (file)
@@ -10,7 +10,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 6edcb88..11d84b3 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 0004a44..792febd 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 5416304..0f03539 100644 (file)
@@ -12,7 +12,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index b550311..a5ea421 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 0477953..64264f4 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 2e1d978..7a41e75 100644 (file)
@@ -11,7 +11,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 92b563b..d3451f2 100644 (file)
@@ -14,7 +14,7 @@ import org.apache.el.util.MessageFactory;
 
 /**
  * @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 {
 
index 9c952c5..fdf0ada 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 6704a2b..0365a5e 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index eb8c0e1..3fa837c 100644 (file)
@@ -14,7 +14,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 4133a7f..e7b78f7 100644 (file)
@@ -11,7 +11,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 6020ed2..99195a9 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 3b5c582..5af5b41 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index cd83c2c..95f6991 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 5526804..6fea776 100644 (file)
@@ -10,7 +10,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index b3b2deb..90695b7 100644 (file)
@@ -10,7 +10,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 6b3aefe..f19cf56 100644 (file)
@@ -10,7 +10,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index d7609d2..7ac0ff8 100644 (file)
@@ -12,7 +12,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index a1b8ae1..adc60a6 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 2ea2dd6..cd3611e 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 876272d..8759fca 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 7827c38..801401d 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 0924e29..445e517 100644 (file)
@@ -10,7 +10,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 39ac13e..a9d24ee 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index d7df8ec..d58eec4 100644 (file)
@@ -9,7 +9,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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) {
index 1d8c486..479d1c8 100644 (file)
@@ -17,7 +17,7 @@ import org.apache.el.util.ReflectionUtil;
 
 /**
  * @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 {
 
index d7a2c2c..1d4cc78 100644 (file)
@@ -23,7 +23,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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 {
     /**
index ae6fe61..9e8f6dd 100644 (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
-       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: (~[]) >
+}
index b72814b..256e838 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.el.lang.EvaluationContext;
 
 /**
  * @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 {
 
index 4058836..57d456e 100644 (file)
@@ -18,7 +18,7 @@ package org.apache.el.parser;
 
 /**
  * @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;
index 784bc16..8bcd68b 100644 (file)
@@ -13,7 +13,7 @@ import org.apache.el.util.MessageFactory;
 
 /**
  * @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;
index cca3ede..f387181 100644 (file)
@@ -21,7 +21,7 @@ import java.util.ResourceBundle;
 
 /**
  * @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 {
 
index ead44f4..42f162a 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.el.lang.ELSupport;
  * 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 {
 
index a90e3a7..2c5d247 100644 (file)
@@ -53,7 +53,7 @@ import org.apache.juli.logging.LogFactory;
  * Only used if a web application context is a directory.
  *
  * @author Glenn L. Nielsen
- * @version $Revision: 306189 $
+ * @version $Revision$
  */
 public final class JspRuntimeContext {
 
index 5216685..f664790 100644 (file)
@@ -1,4 +1,4 @@
-# $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
index 34aee50..ff15399 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings_es.properties 349479 2005-11-28 19:44:47Z yoavs $
+# $Id$
 #
 # Default localized string information
 # Localized para Locale es_ES
index c7588ad..fdb5bd9 100644 (file)
@@ -1,4 +1,4 @@
-# $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
index eb24a57..cfaa070 100644 (file)
@@ -1,4 +1,4 @@
-# $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
index 63b4761..428f3d2 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.AnnotationProcessor;
  *
  * @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 {
 
index 254467c..8c6e7e3 100644 (file)
@@ -35,7 +35,7 @@ import java.util.NoSuchElementException;
  * 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 {
index 279b58a..7a13dd5 100644 (file)
@@ -28,7 +28,7 @@ import org.apache.jasper.compiler.Localizer;
  *
  * @author Andy Clark, IBM
  *
- * @version $Id: ASCIIReader.java 305933 2004-03-17 19:23:05Z luehe $
+ * @version $Id$
  */
 public class ASCIIReader
     extends Reader {
index ffdc73c..0ebf31f 100644 (file)
@@ -474,7 +474,7 @@ import java.util.Hashtable;
  * @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 {
 
index 1e3a115..e27cc85 100644 (file)
@@ -48,7 +48,7 @@ import org.xml.sax.SAXParseException;
  * 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 {
index 5edbde7..ad77e29 100644 (file)
@@ -49,7 +49,7 @@ package org.apache.jasper.xmlparser;
  * </ul>
  *
  * @author Andy Clark
- * @version $Id: SymbolTable.java 306179 2005-07-27 15:12:04Z yoavs $
+ * @version $Id$
  */
 public class SymbolTable {
 
index 6a76ffe..4ccf97b 100644 (file)
@@ -34,7 +34,7 @@ import java.util.Iterator;
  * 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 {
index f2005fa..8f3de57 100644 (file)
@@ -27,7 +27,7 @@ import java.io.Reader;
  *
  * @author Neil Graham, IBM
  *
- * @version $Id: UCSReader.java 306148 2005-03-21 15:38:13Z remm $
+ * @version $Id$
  */
 public class UCSReader extends Reader {
 
index cd3f7a6..c562e4c 100644 (file)
@@ -26,7 +26,7 @@ import org.apache.jasper.compiler.Localizer;
 /**
  * @author Andy Clark, IBM
  *
- * @version $Id: UTF8Reader.java 306148 2005-03-21 15:38:13Z remm $
+ * @version $Id$
  */
 public class UTF8Reader
     extends Reader {
index 858cd8e..4e022ec 100644 (file)
@@ -49,7 +49,7 @@ import java.util.Arrays;
  * @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 {
 
index 66919fa..4311cdd 100644 (file)
@@ -47,7 +47,7 @@ package org.apache.jasper.xmlparser;
  * @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 {
 
index f96cb49..69802d0 100644 (file)
@@ -44,7 +44,7 @@ package org.apache.jasper.xmlparser;
  * @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 {
index 29c9771..661c01c 100644 (file)
@@ -89,7 +89,7 @@ import org.apache.catalina.Server;
     @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 =
index 447789a..2aff256 100644 (file)
@@ -36,7 +36,7 @@ import java.util.logging.SimpleFormatter;
  * 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
index b00c6d7..9264a15 100644 (file)
@@ -58,7 +58,7 @@ package org.apache.juli.logging;
  *
  * @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 {
 
index b0e0029..5fd32c5 100644 (file)
@@ -24,7 +24,7 @@ package org.apache.juli.logging;
  * 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 {
index bc9d622..0bdd955 100644 (file)
@@ -63,7 +63,7 @@ import java.util.Properties;
  * @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 {
 
index c8e4434..20b4906 100644 (file)
@@ -24,7 +24,7 @@ import java.util.Hashtable;
  * 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 {
index 25935d3..523848b 100644 (file)
@@ -30,7 +30,7 @@ import javax.naming.Context;
  * </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 {
index 0486af4..86e330e 100644 (file)
@@ -26,7 +26,7 @@ import javax.naming.StringRefAddr;
  * 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
index 98ee945..a41aa0a 100644 (file)
@@ -33,7 +33,7 @@ import java.security.BasicPermission;
  * </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 {
index 2cfe4bb..ec2c2b6 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: LocalStrings_es.properties 301082 2002-07-18 16:47:23Z remm $
+# $Id$
 
 # language es
 
index be7af2b..3c8cb05 100644 (file)
@@ -27,7 +27,7 @@ import javax.naming.CompositeName;
  * 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 
index 3d600bb..092a093 100644 (file)
@@ -41,7 +41,7 @@ import javax.naming.spi.NamingManager;
  * 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 {
 
index c8acd98..27adb0d 100644 (file)
@@ -30,7 +30,7 @@ import javax.naming.NamingException;
  * 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 
index bc893df..239bb4e 100644 (file)
@@ -28,7 +28,7 @@ import javax.naming.NamingException;
  * 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 
index f1d688c..a8caef8 100644 (file)
@@ -23,7 +23,7 @@ package org.apache.naming;
  * 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 {
index 5020c43..4e1348b 100644 (file)
@@ -29,7 +29,7 @@ import javax.management.Notification;
  * 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
index f0309bb..936c8a8 100644 (file)
@@ -21,7 +21,7 @@ package org.apache.naming;
  * Naming MBean interface.
  * 
  * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
  */
 
 public interface NamingServiceMBean {
index 8f0153e..e4c188b 100644 (file)
@@ -25,7 +25,7 @@ import javax.naming.Reference;
  * 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
index c8e9c85..e8879ab 100644 (file)
@@ -26,7 +26,7 @@ import javax.naming.StringRefAddr;
  * 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
index 2e5f08c..3456fde 100644 (file)
@@ -29,7 +29,7 @@ import javax.naming.StringRefAddr;
  * 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
index 26e51c6..c44ca69 100644 (file)
@@ -29,7 +29,7 @@ import javax.naming.NamingException;
  * 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 {
index 650fa40..50eb61a 100644 (file)
@@ -25,7 +25,7 @@ import javax.naming.Reference;
  * 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
index e55309f..57195f8 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.naming.EjbRef;
  * 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
index 0595bdb..eefcf08 100644 (file)
@@ -62,7 +62,7 @@ import javax.naming.spi.ObjectFactory;
  * </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 {
index 270ea1d..9a88ba5 100644 (file)
@@ -34,7 +34,7 @@ import java.util.Properties;
  * 
  * @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 {
 
index d10ff35..781a284 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.naming.ResourceEnvRef;
  * 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
index 77dd4c8..341c0d1 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.naming.ResourceRef;
  * 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
index 85ea9dc..babbce2 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.naming.ResourceLinkRef;
  * <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
index d2dc8c6..7541f14 100644 (file)
@@ -31,7 +31,7 @@ import org.apache.naming.TransactionRef;
  * 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
index 61519f1..2f759e9 100644 (file)
@@ -44,7 +44,7 @@ import org.apache.naming.ContextBindings;
  * </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
index 3593ecf..78f3c02 100644 (file)
@@ -37,7 +37,7 @@ import org.apache.naming.StringManager;
  * 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 {
index 88f97ce..aafc1a8 100644 (file)
@@ -23,7 +23,7 @@ import javax.naming.directory.DirContext;
  * Implements a cache entry.
  * 
  * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
  */
 public class CacheEntry {
     
index bdae090..aa60769 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.naming.resources.ResourceAttributes;
  * content is directly returned.
  * 
  * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 303022 $
+ * @version $Revision$
  */
 public class DirContextURLConnection 
     extends URLConnection {
index c1d9dfa..389042d 100644 (file)
@@ -29,7 +29,7 @@ import javax.naming.directory.DirContext;
  * 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 {
index 64a4f2e..ab77b5a 100644 (file)
@@ -24,7 +24,7 @@ import java.net.URLStreamHandlerFactory;
  * 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 {
index 38fa992..ddb40fe 100644 (file)
@@ -45,7 +45,7 @@ import org.apache.naming.NamingEntry;
  * 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 {
index af70d53..d924fa7 100644 (file)
@@ -26,7 +26,7 @@ import javax.naming.NameNotFoundException;
  * properly populated exceptions.
  * 
  * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
  */
 public class ImmutableNameNotFoundException
     extends NameNotFoundException {
index 602ac27..509c0e2 100644 (file)
@@ -40,7 +40,7 @@ import org.apache.naming.StringManager;
  * 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 {
index e013b71..efd31c4 100644 (file)
@@ -28,7 +28,7 @@ import javax.naming.NamingException;
  * 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 
index 83abc8d..7dc44dc 100644 (file)
@@ -25,7 +25,7 @@ import java.io.IOException;
  * Encapsultes the contents of a resource.
  * 
  * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 302726 $
+ * @version $Revision$
  */
 public class Resource {
     
index 300ab6d..59bf3e4 100644 (file)
@@ -34,7 +34,7 @@ import javax.naming.directory.BasicAttribute;
  * Attributes implementation.
  * 
  * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 303877 $
+ * @version $Revision$
  */
 public class ResourceAttributes implements Attributes {
     
index da14599..2e6fc6a 100644 (file)
@@ -25,7 +25,7 @@ import java.util.Random;
  * Implements a special purpose cache.
  * 
  * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
- * @version $Revision: 304084 $
+ * @version $Revision$
  */
 public class ResourceCache {
     
index 61a2478..788a162 100644 (file)
@@ -48,7 +48,7 @@ import org.apache.naming.NamingEntry;
  * 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 {
index a308d60..4d568b8 100644 (file)
@@ -23,7 +23,7 @@ import org.apache.naming.resources.DirContextURLStreamHandler;
  * 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 {
index cc7b4f8..048db45 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 296bc16..3632244 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 7fddcd4..db31124 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index bc76af1..9d56b89 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index b4d4c05..7f72d55 100644 (file)
@@ -21,7 +21,7 @@ import java.nio.ByteBuffer;
 /** 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 {
index 0ec0d19..59a1cbd 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index a398911..47638e4 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 59346f2..d178c78 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 37f112a..fb5a7a6 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index b759069..df6e549 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index de6b243..f42fd37 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 88f3b40..4e2e680 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index f63dc0f..0e7c8a6 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 2c7609b..d085ae3 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 5fff9ea..8eb0ee8 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 7f79a9f..408c9cb 100644 (file)
@@ -22,7 +22,7 @@ import java.nio.ByteBuffer;
 /** 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 {
index e38e820..fa93ec4 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 90ab151..7f037c7 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 7e567c7..f359823 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index bcd7f10..f66f642 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 4fbe10f..724151f 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index e001e61..daf8d29 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index fa216a7..90472f8 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 0245614..fd408ff 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index af2e96d..8384405 100644 (file)
@@ -22,7 +22,7 @@ import java.nio.ByteBuffer;
 /** 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 {
index aab46ba..3f2b90c 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 6848f4d..969a16b 100644 (file)
@@ -23,7 +23,7 @@ import java.nio.ByteBuffer;
 /** 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 {
index caa1033..9f883ea 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index c63da45..1be544b 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 2fc271e..dc41535 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index eb0fe14..69723f9 100644 (file)
@@ -20,7 +20,7 @@ package org.apache.tomcat.jni;
 /** 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 {
index 173b77c..c1e5e5f 100644 (file)
@@ -28,7 +28,7 @@ package org.apache.tomcat.util.buf;
  * 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 {
index 95444e5..43eaf48 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 3db53b6..34b552c 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 573c71f..6830bd0 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index e253627..985953f 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index a307fdd..56d1783 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 4ae08f5..2c5d8c3 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 1a66d4a..98fa702 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 4f7e9a7..c61acdc 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 4e22bb6..7f7f682 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index ac70a32..742d616 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 8155efc..ced916f 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 5149123..927200b 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index bff9195..7760499 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 897d1f5..d3f8549 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 251814b..c20239c 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index a168cfa..090c66a 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index fb53692..1a84b83 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 91ae6b7..34b2e7e 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index b3defb4..1ba91e7 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 8d6241e..5406300 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 2d7b8c6..f49ddbd 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 1aab6c7..60d8e90 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 2f47eae..b801c50 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 32d1695..08500cd 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index e8e0684..eadfc5c 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 6a64ae4..47d0d10 100644 (file)
@@ -1,4 +1,4 @@
-/* $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
index 1fc018e..b00f5d3 100644 (file)
@@ -18,7 +18,7 @@
 /***************************************************************************
  * Description: Base http request object.                                  *
  * Author:      Keving Seguin [seguin@apache.org]                          *
- * Version:     $Revision: 299484 $                                           *
+ * Version:     $Revision$                                           *
  ***************************************************************************/
 
 package org.apache.tomcat.util.http;
index 2e492e9..8d9087c 100644 (file)
@@ -51,7 +51,7 @@ import java.io.UnsupportedEncodingException;
  * @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
index 75e6436..2d29b38 100644 (file)
@@ -41,7 +41,7 @@ import java.io.File;
  *
  * @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
 {
index ccd97c1..5ba4165 100644 (file)
@@ -32,7 +32,7 @@ import java.io.OutputStream;
  *
  * @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
index 12288ac..d867f81 100644 (file)
@@ -45,7 +45,7 @@ import javax.servlet.http.HttpServletRequest;
  * @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
index c0a475a..7237c08 100644 (file)
@@ -52,7 +52,7 @@ import java.io.UnsupportedEncodingException;
  * @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
index 0300137..f6988ae 100644 (file)
@@ -26,7 +26,7 @@ package org.apache.tomcat.util.http.fileupload;
  *
  * @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
 {
index 7e1b082..5ed1944 100644 (file)
@@ -40,7 +40,7 @@ package org.apache.tomcat.util.http.fileupload;
  * @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
index 04a0176..5b9251d 100644 (file)
@@ -50,7 +50,7 @@ import javax.servlet.http.HttpServletRequest;
  * @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
 {
index c8bcc00..fd5def1 100644 (file)
@@ -23,7 +23,7 @@ package org.apache.tomcat.util.http.fileupload;
  * 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
index 5562a38..1ce4ec3 100644 (file)
@@ -85,7 +85,7 @@ import java.io.UnsupportedEncodingException;
  * @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
 {
index e5b6d32..9e1d49e 100644 (file)
@@ -38,7 +38,7 @@ import java.io.OutputStream;
  *
  * @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
index 853871f..87c27b7 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
index b9eca5c..a142c08 100644 (file)
@@ -33,7 +33,7 @@ import javax.management.NotificationFilter;
  * 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 {
index 7dedc87..e979da9 100644 (file)
@@ -31,7 +31,7 @@ import javax.management.MBeanFeatureInfo;
  * <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 {
index 9d7c55b..04a6daa 100644 (file)
@@ -43,7 +43,7 @@ import javax.management.ServiceNotFoundException;
  * 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
index c685896..f924d7e 100644 (file)
@@ -29,7 +29,7 @@ import javax.management.MBeanNotificationInfo;
  * 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 {
index 003d141..3771e98 100644 (file)
@@ -29,7 +29,7 @@ import javax.management.MBeanParameterInfo;
  * 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 {
index f2d9c61..8a6a852 100644 (file)
@@ -11,7 +11,7 @@
       "-//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$
 -->
 
 
index 33b008f..2cff92b 100644 (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.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();
+    }
+
+}
index e264d63..3521f6e 100644 (file)
@@ -41,7 +41,7 @@ import java.net.MalformedURLException;
  * 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 {
index dba3ea2..593980e 100644 (file)
@@ -42,7 +42,7 @@ import java.util.ResourceBundle;
  * <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]