Package org.jboss.ha.framework.server.ispn

Source Code of org.jboss.ha.framework.server.ispn.DistributedState

/*
  * JBoss, Home of Professional Open Source
  * Copyright 2010, 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.ha.framework.server.ispn;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.infinispan.Cache;
import org.infinispan.CacheException;
import org.infinispan.atomic.AtomicMap;
import org.infinispan.atomic.AtomicMapLookup;
import org.infinispan.manager.CacheContainer;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.infinispan.remoting.transport.jgroups.JGroupsTransport;
import org.infinispan.util.SimpleImmutableEntry;
import org.jboss.ha.core.framework.server.ChannelSource;
import org.jboss.ha.core.framework.server.ManagedDistributedState;
import org.jboss.logging.Logger;
import org.jgroups.Channel;

/**
* This class manages distributed state across the cluster.
*
* The Cache layout will have two formats.  The value collection will combine the category
* + DS key for the composite key.  The DS value will be the value.  It will look like:
* Cache<SimpleImmutableEntry<String Category, Serializable DS_Key>, Serializable DS_Value>
*
* The other Cache format will be the DS keys per category.  It will look like:
* Cache<String, AtomicMap<Serializable, null>>
*
* If it ever becomes important to eliminate the "keys per category" (e.g. to save space),
* we would have to implement the getAllCategories() + getAllKeys() sequentially.
*
* @author  <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>.
* @author  <a href="mailto:bill@burkecentral.com">Bill Burke</a>.
* @author  Scott.Stark@jboss.org
* @author  Scott Marlow
* @author  Paul Ferraro
*/
@Listener
public class DistributedState implements ManagedDistributedState, ChannelSource
{
   private final ConcurrentMap<String, List<DSListener>> keyListeners = new ConcurrentHashMap<String, List<DSListener>>();
   private final CacheContainerSource cacheHandler;
  
   private volatile String cacheName = "DistributedState";
   private volatile Cache<Serializable, Serializable> cache;
   private static final Logger LOG = Logger.getLogger(DistributedState.class);

   public DistributedState(CacheContainerSource cacheHandler)
   {
      this.cacheHandler = cacheHandler;
   }

   // Public --------------------------------------------------------

   @Override
   public void createService() throws Exception
   {
   }

   @Override
   public void startService() throws Exception
   {
      CacheContainer container = this.cacheHandler.getCacheContainer();
      this.cache = (this.cacheName != null) ? container.<Serializable, Serializable>getCache(this.cacheName) : container.<Serializable, Serializable>getCache();
     
      if (!this.cache.getStatus().allowInvocations())
      {
         this.cache.start();
      }
     
      this.cache.addListener(this);
   }

   @Override
   public void stopService() throws Exception
   {     
      this.cache.removeListener(this);
     
      this.cache.stop();
   }

   @Override
   public void destroyService() throws Exception
   {
   }
  
   public void setCacheName(String cacheName)
   {
      this.cacheName = cacheName;
   }
/*
TODO:  jmx support, delete or bring mbean interface back.
   public String listContent() throws Exception
   {
      StringBuilder result = new StringBuilder();
      Collection<String> cats = this.getAllCategories();
      if (cats == null) return result.toString();

      Iterator<String> catsIter = cats.iterator();
      while (catsIter.hasNext())
      {
         String category = (String) catsIter.next();
         Iterator<Serializable> keysIter = this.getAllKeys(category).iterator();

         result.append("-----------------------------------------------\n");
         result.append("Logger : ").append(category).append("\n\n");
         result.append("KEY\t:\tVALUE\n");

         while (keysIter.hasNext())
         {
            Serializable key = (Serializable) keysIter.next();
            String value = this.get(category, key).toString();
            result.append("'").append(key);
            result.append("'\t:\t'");
            result.append(value);
            result.append("'\n");
         }
         result.append("\n");
      }
      return result.toString();
   }

   public String listXmlContent() throws Exception
   {
      StringBuilder result = new StringBuilder();
      result.append("<DistributedState>\n");
      Collection<String> cats = this.getAllCategories();
      if (cats != null)
      {
         Iterator<String> catsIter = cats.iterator();
         while (catsIter.hasNext())
         {
            String category = (String) catsIter.next();
            Iterator<Serializable> keysIter = this.getAllKeys(category).iterator();

            result.append("\t<Logger>\n");
            result.append("\t\t<LoggerName>").append(category).append("</LoggerName>\n");

            while (keysIter.hasNext())
            {
               Serializable key = (Serializable) keysIter.next();
               String value = this.get(category, key).toString();
               result.append("\t\t<Entry>\n");
               result.append("\t\t\t<Key>").append(key).append("</Key>\n");
               result.append("\t\t\t<Value>").append(value).append("</Value>\n");
               result.append("\t\t</Entry>\n");
            }
            result.append("\t</Logger>\n");
         }
      }

      result.append("</DistributedState>\n");

      return result.toString();
   }
*/

   /**
    * {@inheritDoc}
    * @see org.jboss.ha.core.framework.server.ChannelSource#getChannel()
    */
   @Override
   public Channel getChannel()
   {
      if (this.cache == null) return null;
     
      JGroupsTransport transport = (JGroupsTransport) this.cache.getAdvancedCache().getRpcManager().getTransport();
     
      return transport.getChannel();
   }

   // DistributedState implementation ----------------------------------------------
   @Override
   public void set(String category, Serializable key, Serializable value) throws Exception
   {
      this.set(category, key, value, true);
   }

   /*
    * (non-Javadoc)
    *
    * @see org.jboss.ha.framework.interfaces.DistributedState#set(java.lang.String,
    *      java.io.Serializable, java.io.Serializable, boolean) @param
    *      asynchronousCall is not supported yet. TreeCache cannot switch this
    *      on the fly. Will take value from TreeCache-config instead.
    */
   @Override
   public void set(String category, Serializable key, Serializable value, boolean asynchronousCall) throws Exception
   {
      if (LOG.isTraceEnabled())
      {
         LOG.trace("set: category=" + category +", key=" + key +", value=" + value);
      }

      //if (this.replAsync != asynchronousCall)
      //{
      //   if (asynchronousCall)
      //   {
      //      this.cache.put(this.buildFqn(category), key, value, Flag.FORCE_ASYNCHRONOUS);
      //   }
      //   else
      //   {
      //      this.cache.put(this.buildFqn(category), key, value, Flag.FORCE_SYNCHRONOUS);
      //   }
      //}
      this.cache.startBatch();
      boolean commit = false;
      try
      {
         this.cache.put(buildValueCollectionKey(category, key), value);
         AtomicMap<Serializable, Void> m = getKeysPerCategoryCollection(category);
         m.put(key, null);
         commit = true;
      }
      finally
      {
         this.cache.endBatch(commit);
      }
   }


   /*
     * (non-Javadoc)
     *
     * @see org.jboss.ha.framework.interfaces.DistributedState#remove(java.lang.String,
     *      java.io.Serializable) @return - returns null in case of
     *      CacheException
     */
   @Override
   public Serializable remove(String category, Serializable key) throws Exception
   {
      return this.remove(category, key, true);
   }

   /*
    * (non-Javadoc)
    *
    * @see org.jboss.ha.framework.interfaces.DistributedState#remove(java.lang.String,
    *      java.io.Serializable, boolean)
    */
   @Override
   public Serializable remove(String category, Serializable key, boolean asynchronousCall) throws Exception
   {
      if (LOG.isTraceEnabled())
      {
         LOG.trace("remove: category=" + category +", key=" + key);
      }
     
      Serializable retVal = this.get(category, key);

      if (retVal != null)
      {
      //   if (this.replAsync != asynchronousCall)
      //   {
      //      if (asynchronousCall)
      //      {
      //         this.cache.getInvocationContext().getOptionOverrides().setForceAsynchronous(true);
      //      }
      //      else
      //      {
      //         this.cache.getInvocationContext().getOptionOverrides().setForceSynchronous(true);
      //      }
      //   }
         cache.startBatch();
         boolean commit = false;
         try
         {
            cache.remove(buildValueCollectionKey(category, key));
            AtomicMap<Serializable, Void> m = getKeysPerCategoryCollection(category);
            m.remove(key);
            commit = true;
         }
         finally
         {
            cache.endBatch(commit);
         }

      }
      if (LOG.isTraceEnabled())
      {
         LOG.trace("remove: category=" + category +", key=" + key +", removed object=" + retVal);
      }
      return retVal;
   }

   /*
     * (non-Javadoc)
     *
     * @see org.jboss.ha.framework.interfaces.DistributedState#get(java.lang.String,
     *      java.io.Serializable)
     */
   @Override
   public Serializable get(String category, Serializable key)
   {
      try
      {
         return this.cache.get(buildValueCollectionKey(category, key));
      }
      catch (CacheException ce)
      {
         return null;
      }
   }

   @Override
   public Collection<String> getAllCategories()
   {
      try
      {
         Set<Serializable> keys = this.cache.keySet();
         if (keys.isEmpty()) return null;

         List<String> categories = new LinkedList<String>();
         for (Serializable key: keys)
         {
            if (key instanceof String// only the keys that are Strings, are categories
            {
               categories.add((String) key);
            }
         }
         return categories;
      }
      catch (CacheException ce)
      {
         return null;
      }
   }

   /*
     * (non-Javadoc)
     *
     * @see org.jboss.ha.framework.interfaces.DistributedState#getAllKeys(java.lang.String)
     *      @return - returns null in case of CacheException
     */
   @Override
   public Collection<Serializable> getAllKeys(String category)
   {
      try
      {
         AtomicMap<Serializable, Void> m = getKeysPerCategoryCollection(category);
         return m.keySet()// the keyset are the DS keys
      }
      catch (CacheException ce)
      {
         return null;
      }
   }

   /*
     * (non-Javadoc)
     *
     * @see org.jboss.ha.framework.interfaces.DistributedState#getAllValues(java.lang.String)
     *      @return - returns null in case of CacheException
     */
   @Override
   public Collection<Serializable> getAllValues(String category)
   {
      try
      {
         Collection<Serializable> keys = getAllKeys(category);
        
         if (keys == null) return null;

         List<Serializable> values = new ArrayList<Serializable>();
         for (Serializable key: keys)
         {
            values.add(cache.get(buildValueCollectionKey(category, key)));
         }
         return values;
      }
      catch (CacheException ce)
      {
         return null;
      }
   }

   @Override
   public void registerDSListenerEx(String category, @SuppressWarnings("deprecation") DSListenerEx subscriber)
   {
      this.registerDSListener(category, subscriber);
   }

   @Override
   public void unregisterDSListenerEx(String category, @SuppressWarnings("deprecation") DSListenerEx subscriber)
   {
      this.unregisterDSListener(category, subscriber);
   }

   @Override
   public void registerDSListener(String category, DSListener subscriber)
   {
      List<DSListener> listeners = this.keyListeners.get(category);
      if (listeners == null)
      {
         listeners = new CopyOnWriteArrayList<DSListener>();
         List<DSListener> existing = this.keyListeners.putIfAbsent(category, listeners);
         if (existing != null )
         {
            listeners = existing;   // use the listeners added by other thread
         }
      }
      listeners.add(subscriber);
   }

   @Override
   public void unregisterDSListener(String category, DSListener subscriber)
   {
      List<DSListener> listeners = this.keyListeners.get(category);
      if (listeners != null)
      {
         listeners.remove(subscriber);
         this.keyListeners.remove(category, Collections.emptyList());
      }
   }

   // ChannelSource -------------------------------------------------

//   public Channel getChannel()
//   {
//      Channel result = null;
//      if (cache != null)
//      {
//         result = cache.getConfiguration().getRuntimeConfig().getChannel();
//      }
//      return result;
//   }
  
   // Package protected ---------------------------------------------

   protected void notifyKeyListeners(String category, Serializable key, Serializable value, boolean locallyModified)
   {
      List<DSListener> listeners = this.keyListeners.get(category);
      if (listeners == null) return;

      for (DSListener listener: listeners)
      {
         listener.valueHasChanged(category, key, value, locallyModified);
      }
   }

   protected void notifyKeyListenersOfRemove(String category, Serializable key, Serializable oldContent,
         boolean locallyModified)
   {
      List<DSListener> listeners = this.keyListeners.get(category);
      if (listeners == null) return;

      for (DSListener listener: listeners)
      {
         listener.keyHasBeenRemoved(category, key, oldContent, locallyModified);
      }
   }

   protected void cleanupKeyListeners()
   {
      keyListeners.clear();
   }

   /** ExtendedTreeCacheListener methods */

   // Private -------------------------------------------------------

   // @CacheListener  -------------------------------------------------

   @CacheEntryModified
   public void nodeModified(CacheEntryModifiedEvent event)
   {
      if (event.isPre()) return;

      Object key = event.getKey();
     
      if (key instanceof SimpleImmutableEntry)
      {
         @SuppressWarnings("unchecked")
         SimpleImmutableEntry<String, Serializable> entry = (SimpleImmutableEntry<String, Serializable>) key;
         notifyKeyListeners(entry.getKey(), entry.getValue(), (Serializable) event.getValue(), event.isOriginLocal());
      }
   }

   @CacheEntryRemoved
   public void nodeRemoved(CacheEntryRemovedEvent event)
   {
      if (event.isPre()) return;
      Object key = event.getKey();

      if (key instanceof SimpleImmutableEntry)
      {
         @SuppressWarnings("unchecked")
         SimpleImmutableEntry<String, Serializable> entry = (SimpleImmutableEntry<String, Serializable>) key;
         notifyKeyListenersOfRemove(entry.getKey(), entry.getValue(), (Serializable) event.getValue(), event.isOriginLocal());
      }
   }

   private SimpleImmutableEntry<String, Serializable> buildValueCollectionKey(String category, Serializable dskey)
   {
      return new SimpleImmutableEntry<String, Serializable>(category, dskey);
   }

   private AtomicMap<Serializable, Void> getKeysPerCategoryCollection(String category)
   {
      return AtomicMapLookup.getAtomicMap(this.cache, category, true);
   }
}
TOP

Related Classes of org.jboss.ha.framework.server.ispn.DistributedState

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.