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
62
63
64
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
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
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
218
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
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 }