Yes I agree it should be replicating, the problem is that it isn't. I must be doing something wrong, but for the life of me I can't see what it is. I've gone through all of the tutorials and reference pages, and my configuration seems correct. The synchronous replication of the cached objects in the TreeCacheAop just doesn't happen as advertised. When a new object is added to the TreeCacheAop it's correctly replicated into the other TreeCacheAops on all nodes of the cluster, but any subsequent modifications of the cached objects are not replicated, and everything quickly becomes out of sync.
Below is a summary of my configuration and code, hopefully someone can see where I'm going awry: The class which uses the TreeCacheAop is UserActivityManager, which is a Simple MBean. This class has methods which are invoked by Servlets and other MBeans to update UserActivity JavaBean objects which are being cached. As mentioned in the original post these objects are being "aspectized" by virtue of the entry in jboss-aop.xml. The jboss-service.xml included in my SAR is as follows: <?xml version="1.0" encoding="UTF-8"?> | | <server> | | <mbean code="org.jboss.cache.aop.TreeCacheAop" | name="jboss.cache:service=TreeCacheAop"> | <depends>jboss:service=Naming</depends> | <depends>jboss:service=TransactionManager</depends> | <attribute name="TransactionManagerLookupClass">org.jboss.cache.JBossTransactionManagerLookup</attribute> | <attribute name="IsolationLevel">REPEATABLE_READ</attribute> | <attribute name="CacheMode">REPL_SYNC</attribute> | <attribute name="UseReplQueue">false</attribute> | <attribute name="ReplQueueInterval">0</attribute> | <attribute name="ReplQueueMaxElements">0</attribute> | <attribute name="ClusterName">TreeCache-Cluster</attribute> | <attribute name="ClusterConfig"> | <config> | <UDP mcast_addr="228.1.2.83" | mcast_port="45556" | ip_ttl="64" | ip_mcast="true" | mcast_send_buf_size="150000" | mcast_recv_buf_size="80000" | ucast_send_buf_size="150000" | ucast_recv_buf_size="80000" | loopback="false"/> | <PING timeout="2000" | num_initial_members="3" | up_thread="false" | down_thread="false"/> | <MERGE2 min_interval="10000" | max_interval="20000"/> | <FD_SOCK/> | <VERIFY_SUSPECT timeout="1500" | up_thread="false" | down_thread="false"/> | <pbcast.NAKACK gc_lag="50" | retransmit_timeout="600,1200,2400,4800" | up_thread="false" | down_thread="false"/> | <pbcast.STABLE desired_avg_gossip="20000" | up_thread="false" | down_thread="false"/> | <UNICAST timeout="600,1200,2400" | window_size="100" | min_threshold="10" | down_thread="false"/> | <FRAG frag_size="8192" | down_thread="false" | up_thread="false"/> | <pbcast.GMS join_timeout="5000" | join_retry_timeout="2000" | shun="true" | print_local_addr="true"/> | <pbcast.STATE_TRANSFER up_thread="true" | down_thread="true"/> | </config> | </attribute> | <attribute name="FetchStateOnStartup">true</attribute> | <attribute name="InitialStateRetrievalTimeout">15000</attribute> | <attribute name="SyncReplTimeout">10000</attribute> | <attribute name="LockAcquisitionTimeout">30000</attribute> | <attribute name="EvictionPolicyClass"></attribute> | </mbean> | | <mbean code="com.mycom.grover.mbean.UserActivityManager" | name="grover.management:service=UserActivityManager"> | <depends>jboss.cache:service=TreeCacheAop</depends> | </mbean> | | </server> A typical method of the UserActivityManager MBean which accesses the cached UserActivity objects and updates a couple of Date properties: /** | * Updates the user activity information for the specified user to indicate the last time | * the user accessed the system (heartbeat) and the last time the user sent an IOI message. | * | * @param userId the user's ID | * @throws MBeanException | */ | public void updateMessageSentTime (String userId) | throws MBeanException | { | // handle a null/blank parameter | if ((userId == null) || (userId.trim().equals(""))) | { | // log the error and throw a new Exception | this.logger.error("Required user ID parameter was either null or blank"); | throw new MBeanException(new IllegalArgumentException("Required user ID parameter was either null or blank")); | } | | | // create and begin a transaction | UserTransaction userTransaction; | try | { | userTransaction = (UserTransaction) this.jndiContext.lookup("UserTransaction"); | userTransaction.begin(); | } | catch (Exception e) | { | // log the error and throw an Exception | this.logger.error("Unable to create and begin a UserTransaction", e); | throw new MBeanException(e, "Unable to create and begin a UserTransaction"); | } | | | try | { | // get the UserActivity for the user, creating one if one doesn't already exist | UserActivity userActivity = getUserActivity(userId, new Boolean(true)); | | // set the heartbeat and message sent times | userActivity.setHeartbeatTime(new Date()); | userActivity.setMessageSentTime(new Date()); | | // commit the transaction | userTransaction.commit(); | } | catch (Exception e) | { | try | { | // rollback the exception | userTransaction.setRollbackOnly(); | } | catch (SystemException e2) | { | // log the error | this.logger.error("Unable to set the UserTransaction for rollback", e2); | } | | // log the error and throw an Exception | this.logger.error("Unable to update the user activity information for the user with ID " + userId, e); | throw new MBeanException(e, "Unable to update the user activity information for the user with ID " + userId); | } | } | | | /** | * Gets the cached UserActivity object for the specified user, or null if a | * corresponding UserActivity object for the specified user is not found and | * not requested to be created (as specified by the create parameter). | * | * NO TRANSACTION | * | * @param userId the user's ID | * @param create whether or not we should create a new UserActivity object for the | * user if one doesn't already exist | * @return a dynamic proxy for the user's cached UserActivity object, or null if a | * UserActivity object for the specified user is not found and the create | * argument is either null or false | * @throws MBeanException | */ | private UserActivity getUserActivity (String userId, | Boolean create) | throws MBeanException | { | // handle a null/blank parameter | if ((userId == null) || (userId.trim().equals(""))) | { | // log the error and throw a new Exception | this.logger.error("Required user ID parameter was either null or blank"); | throw new MBeanException(new IllegalArgumentException("Required user ID parameter was either null or blank")); | } | | | // get the corresponding fully qualified name of the UserActivity object for the user | // (this will be null if the user's activity information doesn't yet exist) | String fullyQualifiedName = getFullyQualifiedName(userId); | | | // get the proxy for the cached UserActivity for the user if it already exists | if (fullyQualifiedName != null) | { | try | { | // get the dynamic proxy for the UserActivity object which is cached for the user | UserActivity userActivity = | (UserActivity) this.mbeanServer.invoke(this.cacheServiceName, | "getObject", | new Object[] {fullyQualifiedName}, | new String[] {String.class.getName()}); | | // return the proxy for the user's cached UserActivity object | return userActivity; | } | catch (Exception e) | { | // log the error and throw an Exception | this.logger.error("Unable to get the cached UserActivity object for the user with ID " + userId, e); | throw new MBeanException(e, "Unable to get the cached UserActivity object for the user with ID " + userId); | } | } | else if ((create != null) && create.booleanValue()) | { | // get the user's Preferences | Preferences preferences = null; | try | { | // get the PreferencesAccess EJB | PreferencesAccessHome preferencesAccessHome = | (PreferencesAccessHome) ServiceLocator.getInstance().getRemoteHome("ejb/PreferencesAccess", PreferencesAccessHome.class); | PreferencesAccess preferencesAccess = preferencesAccessHome.create(); | | // get the user's Preferences Data Transfer Object | preferences = preferencesAccess.getPreferences(userId); | } | catch (Exception e) | { | // log the error and throw a new Exception | this.logger.error("Unable to get Preferences for the user with ID " + userId, e); | throw new MBeanException(e, "Unable to get Preferences for the user with ID " + userId); | } | | | // get the cache node name to use based on the user type | String nodeName; | if (preferences.isHimUser()) | { | nodeName = NODE_HIM_USERS; | } | else if (preferences.isWebUser()) | { | nodeName = NODE_WEB_USERS; | } | else | { | // log the error and throw a new Exception | this.logger.error("Unable to determine the user type (HIM or WEB) for the user with ID " + userId); | throw new MBeanException(new Exception("Unable to determine the user type (HIM or WEB) for the user with ID " + userId)); | } | | // create the fully qualified name using the node name and user ID | fullyQualifiedName = nodeName + "/" + userId; | | try | { | // create a UserActivity object and set the login time | UserActivity userActivity = new UserActivity(preferences); | userActivity.setLoginTime(new Date()); | | // add the UserActivity to the TreeCache | this.mbeanServer.invoke(this.cacheServiceName, | "putObject", | new Object[] {fullyQualifiedName, userActivity}, | new String[] {String.class.getName(), Object.class.getName()}); | | // get the dynamic proxy for the UserActivity object which is cached for the user | userActivity = (UserActivity) this.mbeanServer.invoke(this.cacheServiceName, | "getObject", | new Object[] {fullyQualifiedName}, | new String[] {String.class.getName()}); | | // return the dynamic proxy for the user's cached UserActivity object | return userActivity; | } | catch (Exception e) | { | // log the error and throw an Exception | this.logger.error("Unable to cache a UserActivity object for the user with ID " + userId + | " using the fully qualified name " + fullyQualifiedName); | throw new MBeanException(new Exception("Unable to cache a UserActivity object for the user with ID " + userId + | " using the fully qualified name " + fullyQualifiedName)); | } | } | else | { | // a corresponding UserActivity object wasn't found in the cache, | // and we don't want to create and cache a new one | return null; | } | } | | | /** | * Get the fully qualified name for the UserActivity object for a user with | * the specified user ID. If the user has no cached UserActivity record | * (doesn't exist) then a null string is returned. | * | * NO TRANSACTION | * | [EMAIL PROTECTED] userId the user's ID | [EMAIL PROTECTED] the fully qualified name of the user's UserActivity record, | * or null if one doesn't yet exist | [EMAIL PROTECTED] MBeanException | */ | private String getFullyQualifiedName (String userId) | throws MBeanException | { | // build fully qualified names using the node names and the user ID | String himName = NODE_HIM_USERS + "/" + userId; | String webName = NODE_WEB_USERS + "/" + userId; | | try | { | // see if a UserActivity object exists for the user under the HIM node | Boolean userExists = | (Boolean) this.mbeanServer.invoke(this.cacheServiceName, | "exists", | new Object[] {himName}, | new String[] {String.class.getName()}); | | if (userExists.booleanValue()) | { | // the fully qualified name using the HIM node is correct | return himName; | } | else | { | // see if a UserActivity object exists for the user under the WEB node | userExists = (Boolean) this.mbeanServer.invoke(this.cacheServiceName, | "exists", | new Object[] {webName}, | new String[] {String.class.getName()}); | if (userExists.booleanValue()) | { | // the fully qualified name using the WEB node is correct | return webName; | } | else | { | // a UserActivity object doesn't yet exist for the user | // and hence no corresponding fully qualified name is available | return null; | } | } | } | catch (Exception e) | { | // log the error and throw a new Exception | this.logger.warn("Unable to find the fully qualified name for user with ID " + userId, e); | throw new MBeanException(e, "Unable to find the fully qualified name for user with ID " + userId); | } | } As you can see in the updateMessageSentTime() method the entire process of updating the cached UserActivity object's properties is done within a UserTransaction. All other methods which access the cached UserActivity objects follow the same pattern. Any suggestions would be greatly appreciated! --James View the original post : http://www.jboss.org/index.html?module=bb&op=viewtopic&p=3876312#3876312 Reply to the post : http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=3876312 ------------------------------------------------------- This SF.Net email is sponsored by: NEC IT Guy Games. Get your fingers limbered up and give it your best shot. 4 great events, 4 opportunities to win big! Highest score wins.NEC IT Guy Games. Play to win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20 _______________________________________________ JBoss-user mailing list JBoss-user@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jboss-user