Package org.jboss.web.tomcat.service.session.distributedcache.ispn

Source Code of org.jboss.web.tomcat.service.session.distributedcache.ispn.DistributedCacheManager

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

import java.io.Serializable;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.config.CacheLoaderManagerConfig;
import org.infinispan.context.Flag;
import org.infinispan.distribution.DistributionManager;
import org.infinispan.manager.CacheContainer;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryActivated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryActivatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged;
import org.infinispan.notifications.cachemanagerlistener.event.ViewChangedEvent;
import org.infinispan.remoting.transport.Address;
import org.jboss.ha.framework.server.lock.SharedLocalYieldingClusterLockManager;
import org.jboss.ha.framework.server.lock.TimeoutException;
import org.jboss.ha.ispn.invoker.CacheInvoker;
import org.jboss.logging.Logger;
import org.jboss.web.tomcat.service.session.distributedcache.impl.IncomingDistributableSessionDataImpl;
import org.jboss.web.tomcat.service.session.distributedcache.spi.BatchingManager;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
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.SessionOwnershipSupport;

/**
* Distributed cache manager implementation using Infinispan.
* @author Paul Ferraro
*/
@Listener
public class DistributedCacheManager<T extends OutgoingDistributableSessionData> implements org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager<T>, SessionOwnershipSupport
{
   static String mask(String sessionId)
   {
      if (sessionId == null) return null;
     
      int length = sessionId.length();
     
      if (length <= 8) return sessionId;
     
      return sessionId.substring(0, 2) + "****" + sessionId.substring(length - 6, length);
   }

   static RuntimeException getRuntimeException(String message, Exception e)
   {  
      if (e instanceof RuntimeException) return (RuntimeException) e;
     
      return new RuntimeException(message != null ? message : e.toString(), e);
   }
  
   static final Logger log = Logger.getLogger(DistributedCacheManager.class);
  
   private static Map<SharedLocalYieldingClusterLockManager.LockResult, LockResult> lockResultMap = lockResultMap();
   private static Map<SharedLocalYieldingClusterLockManager.LockResult, LockResult> lockResultMap()
   {
      Map<SharedLocalYieldingClusterLockManager.LockResult, LockResult> map = new EnumMap<SharedLocalYieldingClusterLockManager.LockResult, LockResult>(SharedLocalYieldingClusterLockManager.LockResult.class);
      map.put(SharedLocalYieldingClusterLockManager.LockResult.ACQUIRED_FROM_CLUSTER, LockResult.ACQUIRED_FROM_CLUSTER);
      map.put(SharedLocalYieldingClusterLockManager.LockResult.ALREADY_HELD, LockResult.ALREADY_HELD);
      map.put(SharedLocalYieldingClusterLockManager.LockResult.NEW_LOCK, LockResult.NEW_LOCK);
      return map;
   }
  
   private final LocalDistributableSessionManager manager;
   private final SharedLocalYieldingClusterLockManager lockManager;
   final SessionAttributeStorage<T> attributeStorage;
   private final Cache<String, Map<Object, Object>> sessionCache;
   private final CacheInvoker invoker;
   private final BatchingManager batchingManager;
   private final boolean passivationEnabled;
  
   public DistributedCacheManager(LocalDistributableSessionManager manager,
         Cache<String, Map<Object, Object>> sessionCache, Cache<Address, String> jvmRouteCache,
         SharedLocalYieldingClusterLockManager lockManager, SessionAttributeStorage<T> attributeStorage,
         BatchingManager batchingManager, CacheInvoker invoker)
   {
      this.manager = manager;
      this.lockManager = lockManager;
      this.sessionCache = sessionCache;
      this.attributeStorage = attributeStorage;
      this.batchingManager = batchingManager;
      this.invoker = invoker;
     
      CacheLoaderManagerConfig loaderManagerConfig = this.sessionCache.getConfiguration().getCacheLoaderManagerConfig();
     
      this.passivationEnabled = (loaderManagerConfig != null) ? loaderManagerConfig.isPassivation().booleanValue() && !loaderManagerConfig.isShared().booleanValue() : false;
   }
  
   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#start()
    */
   @Override
   public void start()
   {
      if (!this.sessionCache.getStatus().allowInvocations())
      {
         this.sessionCache.start();
      }
     
      this.sessionCache.addListener(this);
     
      if (this.sessionCache.getConfiguration().getCacheMode().isDistributed())
      {
         EmbeddedCacheManager container = (EmbeddedCacheManager) this.sessionCache.getCacheManager();
        
         container.addListener(new ViewChangeListener(this.manager));
        
         String jvmRoute = this.manager.getJvmRoute();
        
         if (jvmRoute != null)
         {
            getJvmRouteCache(container, this.manager).putIfAbsent(container.getAddress(), jvmRoute);
         }
      }
   }
  
   static Cache<Address, String> getJvmRouteCache(CacheContainer container, LocalDistributableSessionManager manager)
   {
      return container.getCache(manager.getEngineName());
   }
  
   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#stop()
    */
   @Override
   public void stop()
   {
      if (this.sessionCache.getConfiguration().getCacheMode().isDistributed())
      {
         EmbeddedCacheManager container = (EmbeddedCacheManager) this.sessionCache.getCacheManager();
        
         container.removeListener(this);
      }
     
      this.sessionCache.removeListener(this);
     
      this.sessionCache.stop();
   }
  
   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#getBatchingManager()
    */
   @Override
   public BatchingManager getBatchingManager()
   {
      return this.batchingManager;
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#sessionCreated(java.lang.String)
    */
   @Override
   public void sessionCreated(String sessionId)
   {
      trace("sessionCreated(%s)", sessionId);
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#storeSessionData(org.jboss.web.tomcat.service.session.distributedcache.spi.OutgoingDistributableSessionData)
    */
   @Override
   public void storeSessionData(final T sessionData)
   {
      final String sessionId = sessionData.getRealId();
     
      trace("storeSessionData(%s)", sessionId);
     
      Operation<Void> operation = new Operation<Void>()
      {
         @Override
         public Void invoke(Cache<String, Map<Object, Object>> cache)
         {
            Map<Object, Object> map = cache.putIfAbsent(sessionId, null);
           
            SessionMapEntry.VERSION.put(map, Integer.valueOf(sessionData.getVersion()));
            SessionMapEntry.METADATA.put(map, sessionData.getMetadata());
            SessionMapEntry.TIMESTAMP.put(map, sessionData.getTimestamp());
           
            DistributedCacheManager.this.attributeStorage.store(map, sessionData);
            return null;
         }
      };

      this.batch(operation);
   }
  
   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#getSessionData(java.lang.String, boolean)
    */
   @Override
   public IncomingDistributableSessionData getSessionData(String sessionId, boolean initialLoad)
   {
      trace("getSessionData(%s, %s)", sessionId, initialLoad);
     
      return this.getData(sessionId, true);
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#getSessionData(java.lang.String, java.lang.String, boolean)
    */
   @Override
   public IncomingDistributableSessionData getSessionData(final String sessionId, String dataOwner, boolean includeAttributes)
   {
      trace("getSessionData(%s, %s, %s)", sessionId, dataOwner, includeAttributes);
     
      return (dataOwner == null) ? this.getData(sessionId, includeAttributes) : null;
   }
  
   private IncomingDistributableSessionData getData(final String sessionId, final boolean includeAttributes)
   {
      Operation<IncomingDistributableSessionData> operation = new Operation<IncomingDistributableSessionData>()
      {
         @Override
         public IncomingDistributableSessionData invoke(Cache<String, Map<Object, Object>> cache)
         {
            Map<Object, Object> map = cache.get(sessionId);
           
            // If requested session is no longer in the cache; return null
            if (map == null) return null;
           
            Integer version = SessionMapEntry.VERSION.get(map);
            Long timestamp = SessionMapEntry.TIMESTAMP.get(map);
            DistributableSessionMetadata metadata = SessionMapEntry.METADATA.get(map);
            IncomingDistributableSessionDataImpl result = new IncomingDistributableSessionDataImpl(version, timestamp, metadata);
           
            if (includeAttributes)
            {
               try
               {
                  result.setSessionAttributes(DistributedCacheManager.this.attributeStorage.load(map));
               }
               catch (Exception e)
               {
                  throw getRuntimeException("Failed to load session attributes for session: " + mask(sessionId), e);
               }
            }
           
            return result;
         }
      };
     
      try
      {
         return this.batch(operation);
      }
      catch (Exception e)
      {
         String message = String.format("Problem accessing session [%s]: %s", mask(sessionId), e.toString());
         log.warn(message);
         log.debug(message, e);
        
         // Clean up
         this.removeSessionLocal(sessionId);
        
         return null;
      }
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#removeSession(java.lang.String)
    */
   @Override
   public void removeSession(final String sessionId)
   {
      trace("removeSession(%s)", sessionId);
     
      this.removeSession(sessionId, false);
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#removeSessionLocal(java.lang.String)
    */
   @Override
   public void removeSessionLocal(final String sessionId)
   {
      trace("removeSessionLocal(%s)", sessionId);
     
      this.removeSession(sessionId, true);
   }

   private void removeSession(final String sessionId, final boolean local)
   {
      Operation<Map<Object, Object>> operation = new Operation<Map<Object, Object>>()
      {
         @Override
         public Map<Object, Object> invoke(Cache<String, Map<Object, Object>> cache)
         {
            if (local)
            {
               cache.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL);
            }
           
            return cache.remove(sessionId);
         }
      };
     
      this.invoker.invoke(this.sessionCache, operation);
   }
  
   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#removeSessionLocal(java.lang.String, java.lang.String)
    */
   @Override
   public void removeSessionLocal(String sessionId, String dataOwner)
   {
      trace("removeSessionLocal(%s, dataOwner)", sessionId, dataOwner);
     
      if (dataOwner == null)
      {
         this.removeSession(sessionId, true);
      }
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#evictSession(java.lang.String)
    */
   @Override
   public void evictSession(final String sessionId)
   {
      trace("evictSession(%s)", sessionId);
     
      Operation<Void> operation = new Operation<Void>()
      {
         @Override
         public Void invoke(Cache<String, Map<Object, Object>> cache)
         {
            cache.evict(sessionId);
            return null;
         }
      };
     
      this.invoker.invoke(this.sessionCache, operation);
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#evictSession(java.lang.String, java.lang.String)
    */
   @Override
   public void evictSession(String sessionId, String dataOwner)
   {
      trace("evictSession(%s, %s)", sessionId, dataOwner);
     
      if (dataOwner == null)
      {
         this.evictSession(sessionId);
      }
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#getSessionIds()
    */
   @Override
   public Map<String, String> getSessionIds()
   {
      Map<String, String> result = new HashMap<String, String>();

      for (Object key: this.sessionCache.keySet())
      {
         result.put((String) key, null);
      }
     
      return result;
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#isPassivationEnabled()
    */
   @Override
   public boolean isPassivationEnabled()
   {
      return this.passivationEnabled;
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#setForceSynchronous(boolean)
    */
   @Override
   public void setForceSynchronous(boolean forceSynchronous)
   {
      this.invoker.setForceSynchronous(forceSynchronous);
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#getSessionOwnershipSupport()
    */
   @Override
   public SessionOwnershipSupport getSessionOwnershipSupport()
   {
      return (this.lockManager != null) ? this : null;
   }
  
   @Override
   public LockResult acquireSessionOwnership(String sessionId, boolean newLock) throws TimeoutException, InterruptedException
   {
      trace("acquireSessionOwnership(%s, %s)", sessionId, newLock);
     
      EmbeddedCacheManager container = (EmbeddedCacheManager) this.sessionCache.getCacheManager();
     
      LockResult result = lockResultMap.get(this.lockManager.lock(this.getSessionLockKey(sessionId), container.getGlobalConfiguration().getDistributedSyncTimeout(), newLock));
     
      return (result != null) ? result : LockResult.UNSUPPORTED;
   }

   @Override
   public void relinquishSessionOwnership(String sessionId, boolean remove)
   {
      trace("relinquishSessionOwnership(%s, %s)", sessionId, remove);
     
      this.lockManager.unlock(this.getSessionLockKey(sessionId), remove);
   }
  
   private Serializable getSessionLockKey(String sessionId)
   {
      return this.sessionCache.getName() + "/" + sessionId;
   }
  
   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#isLocal(java.lang.String)
    */
   @Override
   public boolean isLocal(String sessionId)
   {
      DistributionManager manager = this.sessionCache.getAdvancedCache().getDistributionManager();
     
      return (manager != null) ? manager.isLocal(sessionId) : true;
   }

   /**
    * {@inheritDoc}
    * @see org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager#locate(java.lang.String)
    */
   @Override
   public String locate(String sessionId)
   {
      AdvancedCache<?, ?> cache = this.sessionCache.getAdvancedCache();
      DistributionManager manager = cache.getDistributionManager();
     
      if ((manager != null) && !manager.isRehashInProgress())
      {
         EmbeddedCacheManager container = (EmbeddedCacheManager) cache.getCacheManager();
         Cache<Address, String> jvmRouteCache = getJvmRouteCache(container, this.manager);
        
         for (Address address: manager.locate(sessionId))
         {
            String jvmRoute = jvmRouteCache.get(address);
           
            if (jvmRoute != null)
            {
               // We need to force synchronous invocations to guarantee
               // session replicates before subsequent request.
               cache.withFlags(Flag.FORCE_SYNCHRONOUS);
               return jvmRoute;
            }
         }
      }
     
      return this.manager.getJvmRoute();
   }

   @CacheEntryRemoved
   public void removed(CacheEntryRemovedEvent<String, Map<Object, Object>> event)
   {
      if (event.isPre() || event.isOriginLocal()) return;
     
      this.manager.notifyRemoteInvalidation(event.getKey());
   }
  
   @CacheEntryModified
   public void modified(CacheEntryModifiedEvent<String, Map<Object, Object>> event)
   {
      if (event.isPre() || event.isOriginLocal()) return;
     
      Map<Object, Object> map = event.getValue();
     
      if (map.isEmpty()) return;
     
      String sessionId = event.getKey();
     
      Integer version = SessionMapEntry.VERSION.get(map);
      Long timestamp = SessionMapEntry.TIMESTAMP.get(map);
      DistributableSessionMetadata metadata = SessionMapEntry.METADATA.get(map);
     
      if ((version != null) && (timestamp != null) && (metadata != null))
      {
         boolean updated = this.manager.sessionChangedInDistributedCache(sessionId, null, version.intValue(), timestamp.longValue(), metadata);
        
         if (!updated)
         {
            log.warn(String.format("Possible concurrency problem: Replicated version id %d is less than or equal to in-memory version for session %s", version, mask(sessionId)));
         }
      }
   }
  
   @CacheEntryActivated
   public void activated(CacheEntryActivatedEvent<String, Map<Object, Object>> event)
   {
      if (event.isPre()) return;

      this.manager.sessionActivated();
   }
  
   @Listener(sync = false)
   public static class ViewChangeListener
   {
      private final LocalDistributableSessionManager manager;
     
      ViewChangeListener(LocalDistributableSessionManager manager)
      {
         this.manager = manager;
      }
     
      @ViewChanged
      public void viewChanged(ViewChangedEvent event)
      {
         Cache<Address, String> cache = getJvmRouteCache(event.getCacheManager(), this.manager);
        
         Collection<Address> oldMembers = event.getOldMembers();
         Collection<Address> newMembers = event.getNewMembers();
        
         // Remove jvm route of crashed member
         for (Address member: oldMembers)
         {
            if (!newMembers.contains(member))
            {
               if (cache.remove(member) != null)
               {
                  log.info("Removed stale jvm route entry from web session cache on behalf of member " + member);
               }
            }
         }
        
         // Restore our jvm route in cache if we are joining (result of a split/merge)
         String jvmRoute = this.manager.getJvmRoute();
         if (jvmRoute != null)
         {
            Address localAddress = event.getLocalAddress();
            if (!oldMembers.contains(localAddress) && newMembers.contains(localAddress))
            {
               String oldJvmRoute = cache.put(localAddress, jvmRoute);
               if (oldJvmRoute == null)
               {
                  log.info("Adding missing jvm route entry to web session cache");
               }
               else if (!oldJvmRoute.equals(jvmRoute))
               {
                  log.info("Updating jvm route entry in web session cache.  old = " + oldJvmRoute + ", new = " + jvmRoute);
               }
            }
         }
      }
   }
  
   private <R> R batch(Operation<R> operation)
   {
      boolean started = this.sessionCache.startBatch();
      boolean success = false;
     
      try
      {
         R result = this.invoker.invoke(this.sessionCache, operation);
        
         success = true;
        
         return result;
      }
      finally
      {
         if (started)
         {
            this.sessionCache.endBatch(success);
         }
      }
   }
  
   private static void trace(String message, Object... args)
   {
      if (log.isTraceEnabled())
      {
         log.trace(String.format(message, args));
      }
   }
  
   // Simplified CacheInvoker.Operation using assigned key/value types
   static interface Operation<R> extends CacheInvoker.Operation<String, Map<Object, Object>, R>
   {
   }
}
TOP

Related Classes of org.jboss.web.tomcat.service.session.distributedcache.ispn.DistributedCacheManager

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.