1   /*
2    * Copyright 2003 - 2013 The eFaps Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Revision:        $Rev$
17   * Last Changed:    $Date$
18   * Last Changed By: $Author$
19   */
20  
21  package org.efaps.util.cache;
22  
23  import java.io.IOException;
24  
25  import javax.naming.InitialContext;
26  import javax.naming.NamingException;
27  import javax.xml.stream.FactoryConfigurationError;
28  
29  import org.efaps.init.INamingBinds;
30  import org.infinispan.AdvancedCache;
31  import org.infinispan.Cache;
32  import org.infinispan.context.Flag;
33  import org.infinispan.lifecycle.ComponentStatus;
34  import org.infinispan.manager.CacheContainer;
35  import org.infinispan.manager.DefaultCacheManager;
36  import org.infinispan.manager.EmbeddedCacheManager;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  /**
41   * TODO comment!
42   *
43   * @author The eFaps Team
44   * @version $Id$
45   */
46  public final class InfinispanCache
47  {
48      /**
49       * Logger for this class.
50       */
51      private static final Logger LOG = LoggerFactory.getLogger(InfinispanCache.class);
52  
53      /**
54       * Sequence used to search for the CacheManager inside JNDI.
55       */
56      private static final String[] KNOWN_JNDI_KEYS = new String[] {
57          "java:/" + INamingBinds.INFINISPANMANGER,
58          "java:jboss/" + INamingBinds.INFINISPANMANGER,
59          "java:comp/env/" + INamingBinds.INFINISPANMANGER };
60  
61      /**
62       * Key to the Counter Cache.
63       */
64      private static String COUNTERCACHE = InfinispanCache.class.getName() + ".InternalCounter";
65  
66      /**
67       * The instance used for singelton.
68       */
69      private static InfinispanCache CACHEINSTANCE;
70  
71      /**
72       * The manager for Infinspan.
73       */
74      private CacheContainer container;
75  
76      /**
77       * Singelton is wanted.
78       */
79      private InfinispanCache()
80      {
81      }
82  
83      /**
84       * init this instance.
85       */
86      private void init()
87      {
88          this.container = InfinispanCache.findCacheContainer();
89          if (this.container == null) {
90              try {
91                  this.container = new DefaultCacheManager(this.getClass().getResourceAsStream(
92                                  "/org/efaps/util/cache/infinispan-config.xml"));
93                  if (this.container instanceof EmbeddedCacheManager) {
94                      ((EmbeddedCacheManager) this.container).addListener(new CacheLogListener(InfinispanCache.LOG));
95                  }
96                  InfinispanCache.bindCacheContainer(this.container);
97                  final Cache<String, Integer> cache = this.container
98                                  .<String, Integer>getCache(InfinispanCache.COUNTERCACHE);
99                  cache.put(InfinispanCache.COUNTERCACHE, 1);
100             } catch (final FactoryConfigurationError e) {
101                 InfinispanCache.LOG.error("FactoryConfigurationError", e);
102             } catch (final IOException e) {
103                 InfinispanCache.LOG.error("IOException", e);
104             }
105         } else {
106             final Cache<String, Integer> cache = this.container
107                             .<String, Integer>getCache(InfinispanCache.COUNTERCACHE);
108             Integer count = cache.get(InfinispanCache.COUNTERCACHE);
109             if (count == null) {
110                 count = 1;
111             } else {
112                 count = count + 1;
113             }
114             cache.put(InfinispanCache.COUNTERCACHE, count);
115         }
116     }
117 
118     /**
119      * Terminate the manager.
120      */
121     private void terminate()
122     {
123         if (this.container != null) {
124             final Cache<String, Integer> cache = this.container
125                             .<String, Integer>getCache(InfinispanCache.COUNTERCACHE);
126             Integer count = cache.get(InfinispanCache.COUNTERCACHE);
127             if (count == null || count < 2) {
128                 this.container.stop();
129             } else {
130                 count = count - 1;
131                 cache.put(InfinispanCache.COUNTERCACHE, count);
132             }
133         }
134     }
135 
136     /**
137      * @param _cacheName cache wanted
138      * @param <K> Key
139      * @param <V> Value
140      * @return a cache from Infinspan
141      */
142     public <K, V> Cache<K, V> getCache(final String _cacheName)
143     {
144         return this.container.getCache(_cacheName);
145     }
146 
147     /**
148      * An advanced cache that does not return the value of the previous value in
149      * case of replacement.
150      *
151      * @param _cacheName cache wanted
152      * @param <K> Key
153      * @param <V> Value
154      * @return an AdvancedCache from Infinispan with Ignore return values
155      */
156     public <K, V> AdvancedCache<K, V> getIgnReCache(final String _cacheName)
157     {
158         return this.container.<K, V>getCache(_cacheName).getAdvancedCache()
159                         .withFlags(Flag.IGNORE_RETURN_VALUES, Flag.SKIP_REMOTE_LOOKUP, Flag.SKIP_CACHE_LOAD);
160     }
161 
162     /**
163      * @param _cacheName cache wanted
164      * @return true if cache exists
165      */
166     public boolean exists(final String _cacheName)
167     {
168         final boolean ret;
169         if (this.container instanceof EmbeddedCacheManager) {
170             ret = ((EmbeddedCacheManager) this.container).cacheExists(_cacheName);
171         } else {
172             ret = this.container.getCache(_cacheName) != null;
173         }
174         return ret;
175     }
176 
177     /**
178      * @return the InfinispanCache
179      */
180     public static InfinispanCache get()
181     {
182         if (InfinispanCache.CACHEINSTANCE == null) {
183             InfinispanCache.CACHEINSTANCE = new InfinispanCache();
184             InfinispanCache.CACHEINSTANCE.init();
185         }
186         if (InfinispanCache.CACHEINSTANCE.container instanceof DefaultCacheManager) {
187             final ComponentStatus status = ((DefaultCacheManager) InfinispanCache.CACHEINSTANCE.container).getStatus();
188             if (status.isStopping() || status.isTerminated()) {
189                 InfinispanCache.CACHEINSTANCE = new InfinispanCache();
190                 InfinispanCache.CACHEINSTANCE.init();
191             }
192         }
193         return InfinispanCache.CACHEINSTANCE;
194     }
195 
196     /**
197      * Stop the manager.
198      */
199     public static void stop()
200     {
201         if (InfinispanCache.CACHEINSTANCE != null) {
202             InfinispanCache.CACHEINSTANCE.terminate();
203         }
204     }
205 
206     /**
207      * @param _container container to be binded in jndi
208      */
209     private static void bindCacheContainer(final CacheContainer _container)
210     {
211         InitialContext context = null;
212         try {
213             context = new InitialContext();
214             context.bind(InfinispanCache.KNOWN_JNDI_KEYS[0], _container);
215         } catch (final NamingException ex) {
216             InfinispanCache.LOG.error("Could not initialise JNDI InitialContext", ex);
217         }
218     }
219 
220     /**
221      * @return the CacheContainer
222      */
223     private static CacheContainer findCacheContainer()
224     {
225         CacheContainer ret = null;
226         InitialContext context = null;
227         try {
228             context = new InitialContext();
229         } catch (final NamingException ex) {
230             InfinispanCache.LOG.error("Could not initialise JNDI InitialContext", ex);
231         }
232         for (final String lookup : InfinispanCache.KNOWN_JNDI_KEYS) {
233             if (lookup != null) {
234                 try {
235                     ret = (CacheContainer) context.lookup(lookup);
236                     InfinispanCache.LOG.info("CacheContainer found in JNDI under '{}'", lookup);
237                 } catch (final NamingException e) {
238                     InfinispanCache.LOG.debug("CacheContainer not found in JNDI under '{}'", lookup);
239                 }
240             }
241         }
242         if (ret == null) {
243             InfinispanCache.LOG.debug("No CacheContainer found under known names");
244         }
245         return ret;
246     }
247 }