Package org.jboss.web.tomcat.service.session.distributedcache.impl.jbc

Source Code of org.jboss.web.tomcat.service.session.distributedcache.impl.jbc.AbstractJBossCacheService

/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.web.tomcat.service.session.distributedcache.impl.jbc;

import java.io.IOException;
import java.io.Serializable;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.transaction.TransactionManager;

import org.jboss.cache.Cache;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheManager;
import org.jboss.cache.CacheStatus;
import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.cache.Region;
import org.jboss.cache.buddyreplication.BuddyManager;
import org.jboss.cache.config.CacheLoaderConfig;
import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig;
import org.jboss.cache.transaction.BatchModeTransactionManager;
import org.jboss.ha.framework.server.MarshalledValueHelper;
import org.jboss.ha.framework.server.SimpleCachableMarshalledValue;
import org.jboss.logging.Logger;
import org.jboss.util.loading.ContextClassLoaderSwitcher;
import org.jboss.web.tomcat.service.session.distributedcache.spi.BatchingManager;
import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
import org.jboss.web.tomcat.service.session.distributedcache.spi.IncomingDistributableSessionData;
import org.jboss.web.tomcat.service.session.distributedcache.spi.LocalDistributableSessionManager;
import org.jboss.web.tomcat.service.session.distributedcache.spi.OutgoingDistributableSessionData;
import org.jboss.web.tomcat.service.session.distributedcache.spi.SessionSerializationFactory;

/**
* Abstract base implementation of {@link DistributedCacheManager}.
*/
public abstract class AbstractJBossCacheService<T extends OutgoingDistributableSessionData> implements DistributedCacheManager<T>
{  
   public static final String BUDDY_BACKUP = BuddyManager.BUDDY_BACKUP_SUBTREE;
   @SuppressWarnings("unchecked")
   public static final Fqn<String> BUDDY_BACKUP_FQN = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN;
   public static final String SESSION = "JSESSION";
   public static final String DEAD_BUDDY_SUFFIX = ":DEAD";
  
   // Use Integers as JBC keys -- replication performant but won't be
   // confused with String attribute keys for ATTRIBUTE granularity sessions
   // Alternative is an enum, but then we replicate a long classname
   public static final Integer VERSION_KEY = Integer.valueOf(0);
   public static final Integer TIMESTAMP_KEY = Integer.valueOf(1);
   public static final Integer METADATA_KEY = Integer.valueOf(2);
   public static final Integer ATTRIBUTE_KEY = Integer.valueOf(3);
   protected static final Set<Integer> INTERNAL_KEYS = new HashSet<Integer>(Arrays.asList(VERSION_KEY, TIMESTAMP_KEY, METADATA_KEY, ATTRIBUTE_KEY));
   
   public static final String FQN_DELIMITER = "/";
  
   public static String getCombinedPath(String hostname, String contextPath)
   {
      return contextPath + "_" + hostname;
   }
  
   public static Fqn<String> getSessionFqn(String contextHostPath, String sessionId)
   {
      return Fqn.fromElements(SESSION, contextHostPath, sessionId);
   }
  
   @Deprecated
   public static Fqn<String> getBuddyBackupSessionFqn(String dataOwner, String contextHostPath, String sessionId)
   {
      return Fqn.fromElements(BUDDY_BACKUP, dataOwner, SESSION, contextHostPath, sessionId);
   }
  
   @SuppressWarnings("unchecked")
   private static ContextClassLoaderSwitcher getContextClassLoaderSwitcher()
   {
      return (ContextClassLoaderSwitcher) AccessController.doPrivileged(ContextClassLoaderSwitcher.INSTANTIATOR);
   }
  
   protected Logger log_ = Logger.getLogger(getClass());
  
   private CacheManager cacheManager_;
   private Cache<Object, Object> plainCache_;
  
   /** Context path for webapp + hostName; this + session id is a unique combo. */
   protected String combinedPath_;
   protected BatchingManager batchingManager;

   private LocalDistributableSessionManager manager_;
   private ClassLoader webAppClassLoader_;
   private CacheListener cacheListener_;
   protected JBossCacheWrapper cacheWrapper_;
  
   /** Do we have to marshall attributes ourself or can we let JBC do it? */
   private boolean useTreeCacheMarshalling_ = false;
  
   /** Are we configured for passivation? */
   private boolean usePassivation_ = false;
   private PassivationListener passivationListener_;
  
   /** Is cache configured for buddy replication? */
   private boolean useBuddyReplication_ = false;
  
   protected String cacheConfigName_;
  
   private boolean purgeOnStartStop_ = true;
  
   protected AbstractJBossCacheService(LocalDistributableSessionManager localManager) throws ClusteringNotSupportedException
   {
      if (localManager == null)
      {
         throw new IllegalArgumentException("localManager is null");
      }
      this.manager_    = localManager;
      establishCacheManager();
      this.cacheConfigName_ = Util.getCacheConfigName(localManager);
   }
  
   protected AbstractJBossCacheService(LocalDistributableSessionManager localManager, Cache<Object, Object> cache)
   {
      if (localManager == null)
      {
         throw new IllegalArgumentException("localManager is null");
      }
      if (cache == null)
      {
         throw new IllegalArgumentException("cache is null");
      }
     
      this.manager_    = localManager;
      this.plainCache_ = cache;    
   }
  
   protected LocalDistributableSessionManager getManager()
   {
      return manager_;
   }
  
   protected Cache<Object, Object> getCache()
   {
      return plainCache_;
   }
  
   protected void setCache(Cache<Object, Object> cache)
   {
      this.plainCache_ = cache;
   }

   public void start()
   {
      establishCache();
     
      this.webAppClassLoader_ = this.manager_.getApplicationClassLoader();
     
      String webAppPath;
      String path = manager_.getContextName();
      if( path.length() == 0 || path.equals("/")) {
         // If this is root.
         webAppPath = "ROOT";
      } else if ( path.startsWith("/") ) {
         webAppPath = path.substring(1);
      } else {
         webAppPath = path;
      }
      // JBAS-3941 -- context path can be multi-level, but we don't
      // want that turning into a multilevel Fqn, so escape it
      // Use '-' which is legal in a filesystem path
      webAppPath = webAppPath.replace('/', '_');
      log_.debug("Old and new web app path are: " +path + ", " +webAppPath);
     
      String hostName;     
      String host = manager_.getHostName();
      if( host == null || host.length() == 0) {
         hostName = "localhost";
      }else {
         hostName = host;
      }
      log_.debug("Old and new virtual host name are: " + host + ", " + hostName);

      this.combinedPath_ = getCombinedPath(hostName, webAppPath);
     
      if (plainCache_.getCacheStatus() != CacheStatus.STARTED)
      {
         plainCache_.start();
      }

      // We require the cache batchingManager to be BatchModeTransactionManager now.
      TransactionManager tm = plainCache_.getConfiguration().getRuntimeConfig().getTransactionManager();
      if( ! (tm instanceof BatchModeTransactionManager) )
      {
         throw new RuntimeException("start(): JBoss Cache transaction manager " +
                                    "is not of type BatchModeTransactionManager. " +
                                    "It is " + (tm == null ? "null" : tm.getClass().getName()));
      }
      this.batchingManager = new BatchingManagerImpl(tm);
     
      Fqn<String> pathFqn = Fqn.fromElements(SESSION, combinedPath_);
     
//      if (useTreeCacheMarshalling_ || this.useBuddyReplication_)
      if (this.purgeOnStartStop_)
      {
         // JBAS-5628/JBAS-5629 -- clean out persistent store
         cleanWebappRegion(pathFqn);
      }

      // Listen for cache changes
      cacheListener_ = new CacheListener(cacheWrapper_, manager_, combinedPath_,
                                         Util.getReplicationGranularity(manager_));
      plainCache_.addCacheListener(cacheListener_);
     
      if(useTreeCacheMarshalling_)
      {
         // register the tcl and bring over the state for the webapp
         try
         {
           
            log_.debug("UseMarshalling is true. We will register the fqn: " +
                        pathFqn + " with class loader" +webAppClassLoader_ +
                        " and activate the webapp's Region");
            Node<Object, Object> root = plainCache_.getRoot();
            if (root.hasChild(pathFqn) == false)
            {
               plainCache_.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
               root.addChild(pathFqn);
            }
            Region region = plainCache_.getRegion(pathFqn, true);
            region.registerContextClassLoader(webAppClassLoader_);
            region.activate();
         }
         catch (Exception ex)
         {
            throw new RuntimeException("Can't register class loader", ex);
         }
      }
     
      if(manager_.isPassivationEnabled())
      {
         log_.debug("Passivation is enabled");
         passivationListener_ = new PassivationListener(manager_, combinedPath_);
         plainCache_.addCacheListener(passivationListener_);
      }
      else
      {
         log_.debug("Passivation is disabled");
      }
   }

   public void stop()
   {
      plainCache_.removeCacheListener(cacheListener_);     
      if (passivationListener_ != null)
         plainCache_.removeCacheListener(passivationListener_);
     
      // Construct the fqn
      Fqn<String> pathFqn = Fqn.fromElements(SESSION, combinedPath_);

      if(useTreeCacheMarshalling_)
      {
         log_.debug("UseMarshalling is true. We will inactivate the fqn: " +
                    pathFqn + " and un-register its classloader");
           
         try {
            Region region = plainCache_.getRegion(pathFqn, false);
            if (region != null)
            {
               region.deactivate();
               region.unregisterContextClassLoader();
            }
         }
         catch (Exception e)
         {
            log_.error("Exception during inactivation of webapp region " + pathFqn +
                       " or un-registration of its class loader", e);
         }
      }
     
//      if (useTreeCacheMarshalling_ || this.useBuddyReplication_)
      if (this.purgeOnStartStop_)
      {
         // JBAS-5628/JBAS-5629 -- clean out persistent store
         cleanWebappRegion(pathFqn);
      }
      // remove session data
      // BES 2007/08/18 Can't do this as it will
      // 1) blow away passivated sessions
      // 2) leave the cache in an inconsistent state if the war
      //    is restarted
//      cacheWrapper_.removeLocalSubtree(pathFqn);
     
      this.webAppClassLoader_ = null;
     
      if (cacheConfigName_ != null)
      {
         releaseCacheToManager(cacheConfigName_);
      }
   }

   /**
    * Get specfically the BatchModeTransactionManager.
    */
   public BatchingManager getBatchingManager()
   {
      return batchingManager;
   }
  
   /**
    * Gets whether TreeCache-based marshalling is available
    */
   public boolean isMarshallingAvailable()
   {
      return useTreeCacheMarshalling_;
   }
  
   public void sessionCreated(String realId)
   {
      // no-op by default   
   }
  
   public IncomingDistributableSessionData getSessionData(String realId, boolean initialLoad)
   {
      if (realId == null)
      {
         throw new IllegalArgumentException("Null realId");
      }
     
      Fqn<String> fqn = getSessionFqn(combinedPath_, realId);
     
      boolean ourBatch = false;
      boolean loadCompleted = false;
      Map<Object, Object> sessionData = null;
      try
      {
         // We need batching so any data gravitation replication
         // is sent in batch.
         // Don't do anything if there is already a batch
         // associated with this thread.
         ourBatch = ensureBatchInProgress();
        
         sessionData =  cacheWrapper_.getData(fqn, true);
     
         if (sessionData == null) {
            // Requested session is no longer in the cache; return null
            return null;
         }
        
         if (initialLoad)
         {
            setupSessionRegion(fqn);
         }
        
         IncomingDistributableSessionData dsd = null;
        
         try
         {
            dsd = getDistributableSessionData(realId, sessionData, true);
         }
         catch (Exception e)
         {
            String masked = Util.maskId(realId);
            log_.warn("Problem accessing session data for session " + masked + " (" +
                  e.getClass().getName() + ") -- existing session cannot be used");
            log_.debug("Details on problem accessing session data for " + masked, e);
            // Clean up
            removeSessionLocal(realId);
            return null;
         }
        
         loadCompleted = true;
        
         return dsd;
      }
      catch (Exception e)
      {
         throw handleBatchException(ourBatch, realId, e);
      }
      finally
      {
         if (ourBatch)
         {
            finishBatch(realId, loadCompleted);
         }
      }
   }

   public void storeSessionData(T sessionData)
   {
      String realId = sessionData.getRealId();
     
      if (log_.isTraceEnabled())
      {
         log_.trace("putSession(): putting session " + Util.maskId(realId));
      }    
     
      Fqn<String> fqn = getSessionFqn(combinedPath_, realId);
     
      Map<Object, Object> map = new HashMap<Object, Object>();
      map.put(VERSION_KEY, Integer.valueOf(sessionData.getVersion()));
     
      DistributableSessionMetadata dsm = sessionData.getMetadata();
      if (dsm != null)
      {  
         map.put(METADATA_KEY, dsm);
      }
     
      Long timestamp = sessionData.getTimestamp();
      if (timestamp != null)
      {
         map.put(TIMESTAMP_KEY, timestamp);
      }
     
      storeSessionAttributes(map, sessionData);
     
      cacheWrapper_.put(fqn, map);
   }
  
   protected void establishCacheManager() throws ClusteringNotSupportedException
   {
      this.cacheManager_ = Util.findPlainCacheManager();     
   }

   protected void establishCache()
   {
      if (this.plainCache_ == null)
      {
         obtainCacheFromManager();        
      }
      this.cacheWrapper_ = new JBossCacheWrapper(plainCache_);
     
      this.useTreeCacheMarshalling_ = plainCache_.getConfiguration().isUseRegionBasedMarshalling();
      boolean purge = true;
      CacheLoaderConfig clc = plainCache_.getConfiguration().getCacheLoaderConfig();
      if(clc != null)
      {
         usePassivation_ = (clc.isPassivation() && !clc.isShared());
         purge = false;
         for (IndividualCacheLoaderConfig iclc : clc.getIndividualCacheLoaderConfigs())
         {
            if (iclc.isPurgeOnStartup())
            {
               purge = true;
               break;
            }
         }
      }
     
      this.purgeOnStartStop_ = purge && (this.useBuddyReplication_ || this.useTreeCacheMarshalling_);
   }

   protected void obtainCacheFromManager()
   {
      if (this.cacheManager_ == null)
      {
         throw new IllegalStateException("No CacheManager available");
      }
      if (this.cacheConfigName_ == null)
      {
         throw new IllegalStateException("No cache configuration name available");
      }
      this.plainCache_ = Util.findPlainCache(this.cacheConfigName_, this.cacheManager_);     
   }

   /**
    * Extension point to allow subclasses to add per-session JBC regions.
    *
    * @param session the session
    * @param fqn the fqn for the session
    */
   protected void setupSessionRegion(Fqn<String> fqn)
   {     
   }
  
   protected abstract void storeSessionAttributes(Map<Object, Object> dataMap, T sessionData);

   /**
    * Extension point to allow subclasses to remove per-session JBC regions.
    *
    * @param session the session
    * @param fqn the fqn for the session
    */
   protected void removeSessionRegion(String realId, Fqn<String> fqn)
   {     
   }

   public void removeSession(String realId)
   {
      Fqn<String> fqn = getSessionFqn(combinedPath_, realId);
      if (log_.isTraceEnabled())
      {
         log_.trace("Remove session " + Util.maskId(realId) + " from " +
             "distributed store. Parent Fqn: " + fqn.getParent());
      }

      cacheWrapper_.remove(fqn);
     
      removeSessionRegion(realId, fqn);
   }

   public void removeSessionLocal(String realId)
   {
      Fqn<String> fqn = getSessionFqn(combinedPath_, realId);
      if (log_.isTraceEnabled())
      {
         log_.trace("Removing session " + Util.maskId(realId) + " from my " +
               "own distributed store only. Parent Fqn: " + fqn.getParent());
      }
     
      cacheWrapper_.removeLocal(fqn);
     
      removeSessionRegion(realId, fqn);
   }

   public void removeSessionLocal(String realId, String dataOwner)
   {
      if (dataOwner == null)
      {
         removeSessionLocal(realId);
      }
      else
      {        
         List<Fqn<Object>> fqns = getBuddyBackupSessionFqns(dataOwner, combinedPath_, realId);
         for (Fqn<Object> fqn : fqns)
         {
            if (log_.isTraceEnabled())
            {
               log_.trace("Removing session " + Util.maskId(realId) + " from my " +
                   "own distributed store only. Parent Fqn: " + fqn.getParent());
            }
            cacheWrapper_.removeLocal(fqn);
         }
      }
   }  
     
   public void evictSession(String realId)
   {
      Fqn<String> fqn = getSessionFqn(combinedPath_, realId);
      if(log_.isTraceEnabled())
      {
         log_.trace("evictSession(): evicting session " + Util.maskId(realId) +
               " from distributed store. Parent Fqn: " + fqn.getParent());
      }
      cacheWrapper_.evictSubtree(fqn);  
   }  
     
   public void evictSession(String realId, String dataOwner)
   {   
      if (dataOwner == null)
      {
         evictSession(realId);
      }
      else
      {
         List<Fqn<Object>> fqns = getBuddyBackupSessionFqns(dataOwner, combinedPath_, realId);
         for (Fqn<Object> fqn : fqns)
         {
            if(log_.isTraceEnabled())
            {
               log_.trace("evictSession(): evicting session " + Util.maskId(realId) +
                     " from distributed store. Parent Fqn: " + fqn.getParent());
            }
            cacheWrapper_.evictSubtree(fqn);
         }
      }
   }
  
   public IncomingDistributableSessionData getSessionData(String realId, String dataOwner, boolean includeAttributes)
   {
      boolean ourBatch = false;
      boolean loadCompleted = false;
      try
      {
         ourBatch = ensureBatchInProgress();

         Map<Object, Object> distributedCacheData = null;
         if (dataOwner == null)
         {
            Fqn<String> fqn = getSessionFqn(combinedPath_, realId);
            distributedCacheData = cacheWrapper_.getData(fqn, false);
         }
         else
         {
            List<Fqn<Object>> fqns = getBuddyBackupSessionFqns(dataOwner, combinedPath_, realId);     
            for (Fqn<Object> fqn : fqns)
            {
               distributedCacheData = cacheWrapper_.getData(fqn, false);
               if (distributedCacheData != null && distributedCacheData.size() > 0)
               {
                  break;
               }
            }
         }
         IncomingDistributableSessionData result = distributedCacheData == null ? null : getDistributableSessionData(realId, distributedCacheData, includeAttributes);
         loadCompleted = true;
         return result;
      }
      catch (Exception e)
      {        
         throw handleBatchException(ourBatch, realId, e);
      }
      finally
      {
         if (ourBatch)
         {
            finishBatch(realId, loadCompleted);
         }
      }
   }

   /**
    * Gets the ids of all sessions in the underlying cache.
    *
    * @return Map<String, String> containing all of the session ids of sessions in the cache
    *         (with any jvmRoute removed) as keys, and the identifier of the data owner for
    *         the session as value (or a <code>null</code>  value if buddy
    *         replication is not enabled.) Will not return <code>null</code>.
    */
   public Map<String, String> getSessionIds()
   {
      Map<String, String> result = new HashMap<String, String>();
     
      Fqn<String> webappFqn = getWebappFqn();
     
      Node<Object, Object> bbRoot = plainCache_.getRoot().getChild(BUDDY_BACKUP_FQN);
      if (bbRoot != null)
      {
         Set<Node<Object, Object>> owners = bbRoot.getChildren();
         if (owners != null)
         {
            for (Node<Object, Object> owner : owners)
            {
               @SuppressWarnings("unchecked")
               Fqn<String> ownerFqn = owner.getFqn();
               if (Util.isBuddyOwnerDead(ownerFqn))
               {
                  for (Node<Object, Object> ownerVersion : owner.getChildren())
                  {
                     storeSessionIdsForOwner(ownerVersion, webappFqn, result);
                  }
               }
               else
               {
                  storeSessionIdsForOwner(owner, webappFqn, result);
               }
            }
         }
      }
     
      storeSessionOwners(getChildrenNames(webappFqn), null, result);

      return result;
   }
  
   protected Set<String> getChildrenNames(Fqn<String> fqn)
   {
      Node<Object, Object> node = plainCache_.getRoot().getChild(fqn);
      @SuppressWarnings("unchecked")
      Set<String> children = (node == null ? Collections.EMPTY_SET : node.getChildrenNames());
      return children;
   }
  
   private void storeSessionIdsForOwner(Node<Object, Object> owner, Fqn<String> webappFqn, Map<String, String> result)
   {
      @SuppressWarnings("unchecked")
      Node webRoot = owner.getChild(webappFqn);
      if (webRoot != null)
      {
         @SuppressWarnings("unchecked")
         Set<String> ids = webRoot.getChildrenNames();
         @SuppressWarnings("unchecked")
         String ownerName = Util.getBuddyOwner(owner.getFqn());
         storeSessionOwners(ids, ownerName, result);
      }
   }

   private void storeSessionOwners(Set<String> ids, String owner, Map<String, String> map)
   {
      if (ids != null)
      {
         for (String id : ids)
         {           
            map.put(id, owner);
         }
      }
   }
   public boolean isPassivationEnabled()
   {
      return usePassivation_;     
   }

   public void setForceSynchronous(boolean forceSynchronous)
   {
      this.cacheWrapper_.setForceSynchronous(forceSynchronous);
   }

   protected Fqn<String> getWebappFqn()
   {
      // /SESSION/webAppPath_hostName
      return Fqn.fromElements(SESSION, combinedPath_);
   }
  
   /**
    * Extracts the contents of <code>distributedCacheData</code>.
    *
    * <strong>Note:</strong> This operation may alter the contents of the
    * passed in map. If this is unacceptable, pass in a defensive copy.
    */
   protected IncomingDistributableSessionData getDistributableSessionData(String realId,
                                       Map<Object, Object> distributedCacheData,
                                       boolean includeAttributes)
   {
      Integer version = (Integer) distributedCacheData.get(VERSION_KEY);
      Long timestamp = (Long) distributedCacheData.get(TIMESTAMP_KEY);
      DistributableSessionMetadata metadata = (DistributableSessionMetadata) distributedCacheData.get(METADATA_KEY);
      IncomingDistributableSessionData result = null;
      if (includeAttributes)
      {
         Map<String, Object> attrs = getSessionAttributes(realId, distributedCacheData);     
         result = new IncomingDistributableSessionDataImpl(version, timestamp, metadata, attrs);
      }
      else
      {
         result = new IncomingDistributableSessionDataImpl(version, timestamp, metadata);
      }
      return result;
   }
  
   /**
    * Returns the session attributes, possibly using the passed in
    * <code>distributedCacheData</code> as a source.
    *
    * <strong>Note:</strong> This operation may alter the contents of the
    * passed in map. If this is unacceptable, pass in a defensive copy.
    */
   protected abstract Map<String, Object> getSessionAttributes(String realId, Map<Object, Object> distributedCacheData);
  
   protected void releaseCacheToManager(String cacheConfigName)
   {     
      try
      {
         this.cacheManager_.releaseCache(cacheConfigName);
      }
      catch (Exception e)
      {
         log_.error("Problem releasing cache to CacheManager -- config is " + cacheConfigName, e);
      }
      finally
      {
         this.plainCache_ = null;
      }
   }

   protected Object getMarshalledValue(Object value)
   {
      // JBAS-2920.  For now, continue using MarshalledValue, as
      // it allows lazy deserialization of the attribute on remote nodes
      // For Branch_4_0 this is what we have to do anyway for backwards
      // compatibility. For HEAD we'll follow suit for now.
      // TODO consider only using MV for complex objects (i.e. not primitives)
      // and Strings longer than X.
     
//      if (useTreeCacheMarshalling_)
//      {
//         return value;
//      }
//      else
//      {
        
         // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
         // to allow for switching between JBossSerialization and JavaSerialization using
         // system property -D=session.serialization.jboss=true / false
         // MarshalledValue mv = new MarshalledValue(value);
         if  (MarshalledValueHelper.isTypeExcluded(value.getClass()))
         {
            return value;              
         }
         else
         {
            try
            {
               return new SimpleCachableMarshalledValue((Serializable) value, SessionSerializationFactory.getObjectStreamSource(), true);
            }
            catch (ClassCastException e)
            {
               throw new IllegalArgumentException(value + " does not implement java.io.Serializable");
            }
         }
//      }
   }

   protected Object getUnMarshalledValue(Object obj) throws IOException, ClassNotFoundException
   {
      if (!(obj instanceof SimpleCachableMarshalledValue))
            return obj;
        
      // Swap in/out the tcl for this web app. Needed only for un marshalling.
      ContextClassLoaderSwitcher.SwitchContext switcher = null;
      try
      {
         switcher = getContextClassLoaderSwitcher().getSwitchContext();
         switcher.setClassLoader(webAppClassLoader_);
        
         SimpleCachableMarshalledValue mv = (SimpleCachableMarshalledValue) obj;
         mv.setObjectStreamSource(SessionSerializationFactory.getObjectStreamSource());
         return mv.get();
      }
      finally
      {
         if (switcher != null)
         {
            switcher.reset();
         }
      }
   }
  
   private List<Fqn<Object>> getBuddyBackupSessionFqns(String dataOwner, String contextHostPath, String sessionId)
   {
      List<Fqn<Object>> result = new ArrayList<Fqn<Object>>();
      // First, just assume a non-dead tree exists
      result.add(Fqn.fromElements((Object) BUDDY_BACKUP, dataOwner, SESSION, contextHostPath, sessionId));
     
      // Now append any "dead" trees associated with this owner
      String deadBuddy = dataOwner + DEAD_BUDDY_SUFFIX;
      Fqn<String> dead = Fqn.fromElements(BUDDY_BACKUP, deadBuddy);
      Set<Object> buddies = plainCache_.getChildrenNames(dead); // won't return null
      for (Object child : buddies)
      {
         result.add(Fqn.fromElements(BUDDY_BACKUP, deadBuddy, child, SESSION, contextHostPath, sessionId));
      }
      return result;
   }

   private void cleanWebappRegion(Fqn<String> regionFqn)
   {
      try {
         // Remove locally.
         plainCache_.getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
         plainCache_.removeNode(regionFqn);
      }
      catch (CacheException e)
      {
         String msg = e.getLocalizedMessage();
         log_.error("Can't clean content from the underlying distributed cache -- " +
               e.getClass().getName() + (msg == null ? "" : " " + msg));
      }
   }

   private boolean ensureBatchInProgress() throws Exception
   {
      boolean newBatch = false;
      if (batchingManager.isBatchInProgress() == false)
      {
         batchingManager.startBatch();
         newBatch = true;
      }
      return newBatch;
   }

   private RuntimeException handleBatchException(boolean ourBatch, String realId, Exception e)
   {
      try
      {
//          if(ourBatch)
         // Let's set it no matter what.
         batchingManager.setBatchRollbackOnly();
      }
      catch (Exception exn)
      {
         log_.error("Caught exception rolling back transaction", exn);
      }
     
      return Util.getRuntimeException("Failed to load session " + Util.maskId(realId), e);
   }

   private void finishBatch(String realId, boolean loadCompleted)
   {
      try
      {
         batchingManager.endBatch();
      }
      catch (Exception e)
      {
         if (loadCompleted)
         {
            // We read the data successfully but then failed in commit?
            // That indicates a JBC data gravitation where the replication of
            // the gravitated data to our buddy failed. We can ignore that
            // and count on this request updating the cache.                               //
            log_.warn("Problem ending batch after loading session " + Util.maskId(realId) + " -- " + e.getLocalizedMessage() + " However session data was successful loaded.");
            log_.debug("Failure cause", e);
         }
         else
         {
            throw Util.getRuntimeException("Failed to load session " + Util.maskId(realId), e);
         }
      }
   }

}
TOP

Related Classes of org.jboss.web.tomcat.service.session.distributedcache.impl.jbc.AbstractJBossCacheService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.