View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
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  
17  package net.sf.ehcache.terracotta;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.Map;
23  
24  import net.sf.ehcache.CacheException;
25  import net.sf.ehcache.config.CacheConfiguration;
26  import net.sf.ehcache.config.TerracottaClientConfiguration;
27  import net.sf.ehcache.config.TerracottaConfiguration;
28  import net.sf.ehcache.config.TerracottaConfiguration.StorageStrategy;
29  import net.sf.ehcache.util.ClassLoaderUtil;
30  
31  /***
32   * A small helper class that knows how to create terracotta store factories
33   *
34   * @author teck
35   * @author Abhishek Sanoujam
36   */
37  class TerracottaClusteredInstanceHelper {
38  
39      /***
40       * Singleton instance
41       */
42      private static TerracottaClusteredInstanceHelper instance = new TerracottaClusteredInstanceHelper();
43  
44      /***
45       * Private constructor
46       */
47      private TerracottaClusteredInstanceHelper() {
48          lookupTerracottaRuntime();
49      }
50  
51      /***
52       * Returns the singleton instance
53       *
54       * @return
55       */
56      public static TerracottaClusteredInstanceHelper getInstance() {
57          return instance;
58      }
59  
60      /*
61       * THIS METHOD IS NOT FOR PUBLIC USE
62       * Used in tests only via reflection
63       *
64       * @param testHelper
65       */
66      private static void setTestMode(TerracottaClusteredInstanceHelper testHelper) {
67          instance = testHelper;
68      }
69  
70      /***
71       * Enum for type of Terracotta runtime
72       */
73      static enum TerracottaRuntimeType {
74          /***
75           * Enum representing Enterprise Express mode
76           */
77          EnterpriseExpress(ENTERPRISE_EXPRESS_FACTORY),
78          /***
79           * Enum representing Express mode
80           */
81          Express(EXPRESS_FACTORY),
82          /***
83           * Enum representing Enterprise Custom mode
84           */
85          EnterpriseCustom(ENTERPRISE_CUSTOM_FACTORY),
86          /***
87           * Enum representing Custom mode
88           */
89          Custom(CUSTOM_FACTORY);
90  
91          private final String factoryClassName;
92  
93          /***
94           * Private constructor
95           *
96           * @param factoryClassName
97           */
98          private TerracottaRuntimeType(String factoryClassName) {
99              this.factoryClassName = factoryClassName;
100         }
101 
102         /***
103          * Returns the factory class for this mode or null if class is not present in classpath
104          *
105          * @return the factory class for this mode or null if class is not present in classpath
106          */
107         public Class getFactoryClassOrNull() {
108             try {
109                 return ClassLoaderUtil.loadClass(factoryClassName);
110             } catch (ClassNotFoundException e) {
111                 return null;
112             }
113         }
114     }
115 
116     /***
117      * Boolean indicating if TC is running or not.
118      * Can be stored in a static final field as required only in DSO mode.
119      */
120     private static final boolean TC_DSO_MODE = Boolean.getBoolean("tc.active");
121 
122     private static final String ENTERPRISE_EXPRESS_FACTORY =
123         "net.sf.ehcache.terracotta.ExpressEnterpriseTerracottaClusteredInstanceFactory";
124     private static final String ENTERPRISE_CUSTOM_FACTORY =
125         "org.terracotta.modules.ehcache.store.EnterpriseTerracottaClusteredInstanceFactory";
126     private static final String EXPRESS_FACTORY = "net.sf.ehcache.terracotta.StandaloneTerracottaClusteredInstanceFactory";
127     private static final String CUSTOM_FACTORY = "org.terracotta.modules.ehcache.store.TerracottaClusteredInstanceFactory";
128 
129     private volatile TerracottaRuntimeType terracottaRuntimeType;
130 
131     /***
132      * Lookup the current terracotta runtime
133      *
134      * @return the current terracotta runtime
135      */
136     private TerracottaRuntimeType lookupTerracottaRuntime() {
137         if (terracottaRuntimeType == null) {
138             final TerracottaRuntimeType[] lookupSequence = {TerracottaRuntimeType.EnterpriseExpress,
139                     TerracottaRuntimeType.EnterpriseCustom, TerracottaRuntimeType.Express, TerracottaRuntimeType.Custom, };
140             for (TerracottaRuntimeType type : lookupSequence) {
141                 if (type.getFactoryClassOrNull() != null) {
142                     terracottaRuntimeType = type;
143                     break;
144                 }
145             }
146         }
147         return terracottaRuntimeType;
148     }
149 
150     /***
151      * Locate and decide which terracotta ClusteredInstanceFactory should be used. If the standalone factory class is available
152      * it is preferred (ie. if ehcache-terracotta-xxx.jar is present)
153      *
154      * @param cacheConfigs
155      * @return the selected terracotta store factory
156      */
157     ClusteredInstanceFactory newClusteredInstanceFactory(Map<String, CacheConfiguration> cacheConfigs,
158             TerracottaClientConfiguration terracottaConfig) {
159         lookupTerracottaRuntime();
160         if (terracottaRuntimeType == null) {
161             throw new CacheException("Terracotta cache classes are not available, you are missing jar(s) most likely");
162         }
163 
164         if (terracottaRuntimeType == TerracottaRuntimeType.EnterpriseExpress || terracottaRuntimeType == TerracottaRuntimeType.Express) {
165             assertExpress(cacheConfigs, terracottaConfig);
166         } else if (terracottaRuntimeType == TerracottaRuntimeType.EnterpriseCustom
167                 || terracottaRuntimeType == TerracottaRuntimeType.Custom) {
168             assertCustom(terracottaConfig);
169         } else {
170             throw new CacheException("Unknown Terracotta runtime type - " + terracottaRuntimeType);
171         }
172 
173         Class factoryClass = terracottaRuntimeType.getFactoryClassOrNull();
174         if (factoryClass == null) {
175             throw new CacheException("Not able to get factory class for: " + terracottaRuntimeType.name());
176         }
177         try {
178             return (ClusteredInstanceFactory) ClassLoaderUtil.createNewInstance(factoryClass.getName(),
179                     new Class[] {TerracottaClientConfiguration.class }, new Object[] {terracottaConfig });
180         } catch (CacheException ce) {
181             if (ce.getCause() instanceof NoClassDefFoundError) {
182                 throw new CacheException("Could not create ClusteredInstanceFactory due to missing class." +
183                         " Please verify that terracotta-toolkit is in your classpath.", ce.getCause().getCause());
184             } else {
185                 throw ce;
186             }
187         }
188     }
189 
190     private static void assertCustom(TerracottaClientConfiguration terracottaConfig) {
191         if (!TC_DSO_MODE) {
192             // required tim jars found in classpath but tc is not running.
193             throw new CacheException("When not using standalone deployment, you need to use full install of Terracotta"
194                     + " in order to use Terracotta Clustered Caches.");
195         }
196 
197         if (terracottaConfig != null) {
198             throw new CacheException("The ehcache configuration specified Terracotta configuration information, "
199                     + "but when using the full install of Terracotta, you must specify the Terracotta configuration "
200                     + "only with an external tc-config.xml file, not embedded or referenced from the ehcache " + "configuration file.");
201         }
202     }
203 
204     private static void assertExpress(Map<String, CacheConfiguration> cacheConfigs, TerracottaClientConfiguration terracottaConfig) {
205         // verify no identity caches if standalone will be used
206         List<String> identityCaches = new ArrayList<String>();
207         for (CacheConfiguration config : cacheConfigs.values()) {
208             TerracottaConfiguration tcConfig = config.getTerracottaConfiguration();
209             if (tcConfig != null && tcConfig.getValueMode() == TerracottaConfiguration.ValueMode.IDENTITY) {
210                 identityCaches.add(config.getName());
211             }
212         }
213         if (!identityCaches.isEmpty()) {
214             throw newExceptionIdentityNotSupportedInExpress(identityCaches);
215         }
216 
217         // This is required in standalone but in non-standalone, this stuff is picked up through
218         // the normal tc-config mechanisms instead
219         if (terracottaConfig == null) {
220             throw new CacheException("Terracotta caches are defined but no <terracottaConfig> element was used "
221                     + "to specify the Terracotta configuration.");
222         }
223     }
224 
225     private static CacheException newExceptionIdentityNotSupportedInExpress(List<String> identityCaches) {
226         return new CacheException("One or more caches are configured for identity value "
227                 + "mode which is not permitted with standalone deployment " + identityCaches.toString());
228     }
229 
230     /***
231      * Returns the default {@link StorageStrategy} type for the current Terracotta runtime.
232      * @param cacheConfiguration the cache configuration
233      *
234      * @return the default {@link StorageStrategy} type for the current Terracotta runtime.
235      */
236     StorageStrategy getDefaultStorageStrategyForCurrentRuntime(CacheConfiguration cacheConfiguration) {
237         lookupTerracottaRuntime();
238         if (terracottaRuntimeType == null) {
239             throw new CacheException("Terracotta cache classes are not available, you are missing jar(s) most likely");
240         }
241         switch (cacheConfiguration.getTerracottaConfiguration().getValueMode()) {
242             case SERIALIZATION:
243                 // default dcv2 for all 4 terracotta runtime types
244                 switch (terracottaRuntimeType) {
245                     case Express:
246                     case Custom:
247                     case EnterpriseCustom:
248                     case EnterpriseExpress:
249                         return StorageStrategy.DCV2;
250                     default:
251                         throw new CacheException("Unknown Terracotta runtime type - " + terracottaRuntimeType);
252                 }
253             case IDENTITY:
254                 switch (terracottaRuntimeType) {
255                     case Custom:
256                     case EnterpriseCustom:
257                         return StorageStrategy.CLASSIC;
258                     case Express:
259                     case EnterpriseExpress:
260                         throw newExceptionIdentityNotSupportedInExpress(Collections.singletonList(cacheConfiguration.getName()));
261                     default:
262                         throw new CacheException("Unknown Terracotta runtime type - " + terracottaRuntimeType);
263                 }
264             default:
265                 throw new CacheException("Unknown value mode - " + cacheConfiguration.getTerracottaConfiguration().getValueMode());
266         }
267     }
268 
269     /***
270      * Returns the terracotta runtime type or null if no runtime could be found
271      *
272      * @return the terracotta runtime type or null if no runtime could be found
273      */
274     TerracottaRuntimeType getTerracottaRuntimeTypeOrNull() {
275         return terracottaRuntimeType;
276     }
277 
278 }