1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package net.sf.tinyjee.cache;
21  
22  import net.sf.tinyjee.cache.config.*;
23  import net.sf.tinyjee.collections.WeakIdentityHashMap;
24  import net.sf.tinyjee.concurrent.LockingMap;
25  import net.sf.tinyjee.concurrent.ObjectSource;
26  import net.sf.tinyjee.config.Configurable;
27  import net.sf.tinyjee.config.ConfigurationContext;
28  import net.sf.tinyjee.config.Resource;
29  import net.sf.tinyjee.util.Assert;
30  import org.eclipse.jetty.util.component.AbstractLifeCycle;
31  import org.infinispan.AdvancedCache;
32  import org.infinispan.Cache;
33  import org.infinispan.config.CacheLoaderManagerConfig;
34  import org.infinispan.config.Configuration;
35  import org.infinispan.config.GlobalConfiguration;
36  import org.infinispan.eviction.EvictionStrategy;
37  import org.infinispan.loaders.cluster.ClusterCacheLoaderConfig;
38  import org.infinispan.loaders.jdbm.JdbmCacheStoreConfig;
39  import org.infinispan.manager.DefaultCacheManager;
40  import org.infinispan.manager.EmbeddedCacheManager;
41  import org.infinispan.marshall.VersionAwareMarshaller;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  import java.io.File;
46  import java.io.IOException;
47  import java.lang.reflect.Field;
48  import java.lang.reflect.Method;
49  import java.util.*;
50  
51  import static net.sf.tinyjee.cache.JNDIJGroupsTransport.CHANNEL_JNDI_NAME;
52  import static net.sf.tinyjee.cache.config.CacheResource.KEY_CHANNEL_JNDI_NAME;
53  import static net.sf.tinyjee.cache.config.CacheResource.getRelatedJGroupConnectionName;
54  import static net.sf.tinyjee.cache.config.DistributionPolicy.STATE_VALUES_ON_DEMAND;
55  import static net.sf.tinyjee.cache.config.DistributionPolicy.STATE_VALUES_ON_FAILURE;
56  import static net.sf.tinyjee.cache.config.PersistentMode.OVERFLOW;
57  import static net.sf.tinyjee.cache.config.PersistentMode.TEMPORARY;
58  import static net.sf.tinyjee.config.Names.*;
59  import static org.infinispan.config.Configuration.CacheMode.*;
60  
61  /**
62   * Defines a shared context that may be used to access configured caches and run cache instance injection.
63   * <p/>
64   * Note: While the complete implementation expects that the cache is configured via the main configuration
65   * file and the code annotations, it is still possible to create an "cache.xml" config file that may
66   * build a base configuration that is applied before any code annotations are read.
67   *
68   * @author Juergen_Kellerer, 2010-03-17
69   * @version 1.0
70   */
71  public class CacheContext extends AbstractLifeCycle implements Configurable {
72  
73  	/**
74  	 * Defines the name of the a template cache region used as a basis for all other caches.
75  	 */
76  	public static final String DEFAULT_CACHE_REGION = "default-cache-region";
77  
78  	private static final Logger log = LoggerFactory.getLogger(CacheContext.class);
79  	private static final CacheContext instance = new CacheContext();
80  
81  	public static CacheContext getInstance() {
82  		return instance;
83  	}
84  
85  	static long calculateValue(String expression, long defaultValue) {
86  		if (expression != null && !expression.isEmpty()) {
87  			return ConfigurationContext.getInstance().
88  					createValueExpression(expression, Long.class).getNumber().longValue();
89  		}
90  		return defaultValue;
91  	}
92  
93  	private String defaultCacheManagerName;
94  	private Map<String, EmbeddedCacheManager> cacheManagers = new LockingMap<String, EmbeddedCacheManager>();
95  	private Map configuredBeans = new WeakIdentityHashMap();
96  
97  	private CacheContext() {
98  		configure();
99  	}
100 
101 	/**
102 	 * {@inheritDoc}
103 	 */
104 	public void configure() {
105 		for (Resource resource : CacheResource.getCacheResources()) {
106 			final EmbeddedCacheManager cacheManager;
107 			final String name = resource.getName();
108 
109 			String config = resource.getPropertyValue(CacheResource.KEY_CACHE_CONFIGURATION, "");
110 			if (!config.isEmpty()) {
111 				if (!config.endsWith(".xml"))
112 					config += ".xml";
113 				File configFile = new File(config);
114 				if (!configFile.exists())
115 					configFile = new File(Configurable.Util.getConfigDirectory(), config);
116 				if (!configFile.exists()) {
117 					try {
118 						try {
119 							Configurable.Util.copyIfMissing(config, configFile);
120 						} catch (IOException e) {
121 							Configurable.Util.copyIfMissing("/net/sf/tinyjee/cache/config/" + config, configFile);
122 						}
123 					} catch (IOException e) {
124 						throw new RuntimeException("Failed to create cache resource, the configuration '" +
125 								config + "' wasn't found inside the classpath, the config folder or the " +
126 								"current working directory.");
127 					}
128 				}
129 
130 				try {
131 					cacheManager = new DefaultCacheManager(configFile.getAbsolutePath(), false);
132 				} catch (Exception e) {
133 					throw new RuntimeException(e);
134 				}
135 			} else
136 				cacheManager = new DefaultCacheManager(false);
137 
138 			// Apply the JNDI name of the JGroups channel.
139 			configureTransport(resource, cacheManager);
140 
141 			final GlobalConfiguration globalConfiguration = cacheManager.getGlobalConfiguration();
142 
143 			// Adjust JMX domain, it must be unique per cache manager instance.
144 			globalConfiguration.setJmxDomain("Infinispan-" + name);
145 
146 			// Adjust marshaller, it fails on 'null' values in 4.2.1
147 			if (VersionAwareMarshaller.class.getName().equals(globalConfiguration.getMarshallerClass()))
148 				globalConfiguration.setMarshallerClass(NullAndVersionAwareMarshaller.class.getName());
149 
150 			resource.bindRelatedInstanceToJNDI(cacheManager);
151 
152 			if (cacheManagers.isEmpty())
153 				defaultCacheManagerName = name;
154 			cacheManagers.put(name, cacheManager);
155 
156 			// Call listeners
157 			for (CacheConfigurationListener listener : CacheConfigurationListener.LISTENERS)
158 				listener.cacheManagerCreated(cacheManager);
159 		}
160 
161 		log.info("TJEE-00630:Configured CacheManagers under {}", cacheManagers.keySet());
162 	}
163 
164 	private void configureTransport(Resource resource, EmbeddedCacheManager cacheManager) {
165 		final GlobalConfiguration configuration = cacheManager.getGlobalConfiguration();
166 		final String transportClass = configuration.getTransportClass();
167 
168 		final boolean replaceTransportClass =
169 				"org.infinispan.remoting.transport.jgroups.JGroupsTransport".equals(transportClass);
170 
171 		if (replaceTransportClass) {
172 			configuration.setTransportClass(JNDIJGroupsTransport.class.getName());
173 			log.info("TJEE-00930:Changing Infinispan transport class from '{}' to '{}'",
174 					transportClass, JNDIJGroupsTransport.class.getName());
175 		}
176 
177 		if (replaceTransportClass || transportClass != null) {
178 			final String jndiName = resource.getPropertyValue(
179 					KEY_CHANNEL_JNDI_NAME, getRelatedJGroupConnectionName(resource));
180 
181 			configuration.getTransportProperties().setProperty(CHANNEL_JNDI_NAME, jndiName);
182 			cacheManager.addListener(new ViewChangedLoggingListener());
183 
184 			if (log.isDebugEnabled())
185 				log.debug("Added Infinispan transport property '{}' => '{}'", CHANNEL_JNDI_NAME, jndiName);
186 		}
187 	}
188 
189 	/**
190 	 * {@inheritDoc}
191 	 */
192 	public synchronized void reconfigure() throws Exception {
193 		doStop();
194 		configure();
195 		doStart();
196 	}
197 
198 	/**
199 	 * {@inheritDoc}
200 	 */
201 	@Override
202 	protected void doStart() throws Exception {
203 		for (EmbeddedCacheManager manager : cacheManagers.values())
204 			try {
205 				manager.start();
206 			} catch (Exception e) {
207 				log.error("TJEE-00920:Failed starting cache manager '" + manager + "'", e);
208 			}
209 
210 		// Re-Inject caches in all beans that were configured with this context.
211 		for (Object configuredBean : configuredBeans.keySet())
212 			configure(configuredBean);
213 	}
214 
215 	/**
216 	 * {@inheritDoc}
217 	 */
218 	@Override
219 	protected void doStop() throws Exception {
220 		for (EmbeddedCacheManager manager : cacheManagers.values()) {
221 			try {
222 				try {
223 					for (String name : manager.getCacheNames()) {
224 						try {
225 							Cache c = manager.getCache(name);
226 							c.stop();
227 						} catch (Exception e) {
228 							log.error("TJEE-00940:Failed stopping cache '" + name + "'", e);
229 						}
230 					}
231 				} finally {
232 					manager.stop();
233 				}
234 			} catch (Exception e) {
235 				log.error("TJEE-00910:Failed stopping cache manager '" + manager + "'", e);
236 			}
237 		}
238 		cacheManagers.clear();
239 	}
240 
241 	/**
242 	 * Returns the system wide default cache manager.
243 	 *
244 	 * @return the system wide default cache manager.
245 	 */
246 	public EmbeddedCacheManager getDefaultCacheManager() {
247 		return cacheManagers.get(defaultCacheManagerName);
248 	}
249 
250 	/**
251 	 * Returns an unmodifiable map of all available cache managers.
252 	 *
253 	 * @return an unmodifiable map of all available cache managers.
254 	 */
255 	public Map<String, EmbeddedCacheManager> getCacheManagers() {
256 		return Collections.unmodifiableMap(cacheManagers);
257 	}
258 
259 	/**
260 	 * Returns the named cache manager if available or the default if no cache manager exists under the given name.
261 	 *
262 	 * @param name the name of the cache manager to look-up.
263 	 * @return the named cache manager if available or the default if no cache manager exists under the given name.
264 	 */
265 	public EmbeddedCacheManager getCacheManager(String name) {
266 		EmbeddedCacheManager manager = cacheManagers.get(name);
267 		return manager == null ? getDefaultCacheManager() : manager;
268 	}
269 
270 	/**
271 	 * Creates a pre-configured cache configuration instance.
272 	 *
273 	 * @param cm		The CacheManager that will be used to host the configuration.
274 	 * @param cacheName The name of the cache (cache region) to create the config for.
275 	 * @return a pre-configured cache configuration instance.
276 	 */
277 	Configuration createBaseConfiguration(EmbeddedCacheManager cm, String cacheName) {
278 		Configuration config = cm.defineConfiguration(cacheName, DEFAULT_CACHE_REGION, new Configuration());
279 		config.setExposeJmxStatistics(RecordCacheStatistics.getBoolean());
280 		return config;
281 	}
282 
283 	/**
284 	 * Creates a cache configuration for the given cacheName and corresponding CacheRegion.
285 	 *
286 	 * @param cacheName   The name of the cache (or cacheRegion).
287 	 * @param cacheRegion The cache region defining further config options of the cache.
288 	 * @return The configuration to use with the cache.
289 	 */
290 	Configuration createConfiguration(String cacheName, CacheRegion cacheRegion) {
291 		EmbeddedCacheManager cm = getCacheManager(cacheRegion.cacheManager());
292 		Configuration config = createBaseConfiguration(cm, cacheName);
293 		configure(cm, config, cacheRegion);
294 		return cm.defineConfiguration(cacheName, config);
295 	}
296 
297 	/**
298 	 * Configures a pre-configured CacheConfiguration instance using the values from CacheRegion.
299 	 *
300 	 * @param config The config instance to configure.
301 	 * @param cm	 The CacheManager that will be used to host the configuration.
302 	 * @param region The code annotation containing the values to apply.
303 	 */
304 	void configure(EmbeddedCacheManager cm, Configuration config, CacheRegion region) {
305 		// Call listeners
306 		for (CacheConfigurationListener listener : CacheConfigurationListener.LISTENERS)
307 			listener.cacheRegionPreConfigured(config, region);
308 
309 		configureEviction(config, region);
310 		configureExpiration(config, region);
311 		configureCacheLoadingAndPersistence(config, region);
312 
313 		if (region.isolationLevel() != IsolationLevel.DEFAULT)
314 			config.setIsolationLevel(region.isolationLevel().toString());
315 
316 		if (cm.getGlobalConfiguration().getTransportClass() != null)
317 			configureDistribution(config, region);
318 
319 		// Call listeners
320 		for (CacheConfigurationListener listener : CacheConfigurationListener.LISTENERS)
321 			listener.cacheRegionConfigured(config, region);
322 	}
323 
324 	private void configureExpiration(Configuration config, CacheRegion region) {
325 		long time = calculateValue(region.idleTimeExpression(), region.idleTime());
326 		if (time != -1L && config.getExpirationMaxIdle() == -1L)
327 			config.setExpirationMaxIdle(region.idleTimeUnit().toMillis(time));
328 
329 		time = calculateValue(region.defaultExpirationTimeExpression(), region.defaultExpirationTime());
330 		if (time != -1L && config.getExpirationLifespan() == -1L)
331 			config.setExpirationLifespan(region.defaultExpirationTimeUnit().toMillis(time));
332 
333 		if (log.isTraceEnabled()) {
334 			log.trace("Configured default cache expiration for region '{}' to max-idle {} ms, lifespan {} ms",
335 					new Object[]{region.name(), config.getExpirationMaxIdle(), config.getExpirationLifespan()});
336 		}
337 	}
338 
339 	private void configureDistribution(Configuration config, CacheRegion region) {
340 		Configuration.CacheMode cm = INVALIDATION_SYNC;
341 		switch (region.distributionPolicy()) {
342 			case NONE:
343 				cm = LOCAL;
344 				break;
345 			case REPLICATE_VALUES:
346 				cm = REPL_SYNC;
347 				break;
348 			case DISTRIBUTE_VALUES:
349 				cm = DIST_SYNC;
350 				break;
351 		}
352 
353 		if (region.distributionIsAsynchronous()) {
354 			cm = cm.toAsync();
355 			if (cm == REPL_ASYNC)
356 				config.setUseReplQueue(true);
357 		}
358 
359 		if (config.getCacheMode() == LOCAL)
360 			config.setCacheMode(cm);
361 		else if (log.isTraceEnabled()) {
362 			log.trace("Not setting cache mode for region '{}' to '{}' it was already set to '{}'",
363 					new Object[]{region.name(), cm, config.getCacheMode()});
364 		}
365 	}
366 
367 	private void configureCacheLoadingAndPersistence(Configuration config, CacheRegion region) {
368 		// If cache stores or loaders are already defined, we do not add our own.
369 		final CacheLoaderManagerConfig mc = config.getCacheLoaderManagerConfig();
370 		if (mc.getFirstCacheLoaderConfig() == null) {
371 			mc.setPreload(region.preloadOnStartup());
372 
373 			// Define whether we ask peers when the local cache doesn't have a key.
374 			PersistentMode persistentMode = region.persistentMode();
375 			if (UseDiskCaching.getBoolean() && persistentMode != PersistentMode.NONE) {
376 				// Using the JDBM disk-store by default as the built-in has severe limitations and bad performance.
377 				JdbmCacheStoreConfig clc = new JdbmCacheStoreConfig();
378 				clc.setLocation(DiskCacheStoragePath.getString());
379 				clc.getAsyncStoreConfig().setEnabled(persistentMode.isAsynchronous());
380 				clc.setPurgeOnStartup(TEMPORARY == persistentMode);
381 
382 				mc.addCacheLoaderConfig(clc);
383 				mc.setPassivation(EnumSet.of(OVERFLOW, TEMPORARY).contains(persistentMode));
384 
385 				if (log.isTraceEnabled()) {
386 					log.trace("Added a persistent cache store for region '{}', using mode '{}'",
387 							region.name(), persistentMode);
388 				}
389 			}
390 
391 			// Define whether we ask peers when the local cache doesn't have a key.
392 			if (EnumSet.of(STATE_VALUES_ON_DEMAND, STATE_VALUES_ON_FAILURE).contains(region.distributionPolicy())) {
393 				ClusterCacheLoaderConfig cclc = new ClusterCacheLoaderConfig();
394 				mc.addCacheLoaderConfig(cclc);
395 
396 				if (log.isTraceEnabled())
397 					log.trace("Added a cluster aware cache loader for region '{}'", region.name());
398 			}
399 		} else {
400 			if (log.isTraceEnabled()) {
401 				log.trace("Not adding cache loaders or stores to region '{}', as {} are already defined.",
402 						region.name(), mc.getCacheLoaderConfigs());
403 			}
404 		}
405 	}
406 
407 	private void configureEviction(Configuration config, CacheRegion region) {
408 		try {
409 			EvictionStrategy strategy = EvictionStrategy.valueOf(region.evictionPolicy().name());
410 			if (config.getEvictionStrategy() == EvictionStrategy.NONE)
411 				config.setEvictionStrategy(strategy);
412 		} catch (RuntimeException e) {
413 			log.warn("TJEE-00620:Not setting unsupported eviction strategy {} on cache region {}",
414 					region.evictionPolicy(), region.name());
415 		}
416 
417 		if (config.getEvictionMaxEntries() == -1) {
418 			int elementsInMem = (int) calculateValue(
419 					region.maxElementsInMemoryExpression(), region.maxElementsInMemory());
420 			config.setEvictionMaxEntries(elementsInMem);
421 		}
422 
423 		if (log.isTraceEnabled()) {
424 			log.trace("Keeping max {} entries in memory before evicting them with a {} strategy in cache region '{}'.",
425 					new Object[]{config.getEvictionMaxEntries(), config.getEvictionStrategy(), region.name()});
426 		}
427 	}
428 
429 	/**
430 	 * Configures the given instance by injecting Map or  instances into fields that are
431 	 * annotated with CacheRegion.
432 	 *
433 	 * @param bean The instance to configure.
434 	 */
435 	@SuppressWarnings("unchecked")
436 	public synchronized void configure(Object bean) {
437 		List<Class<?>> classHierarchy = new ArrayList<Class<?>>();
438 		for (Class<?> c = bean.getClass(); c != null; c = c.getSuperclass())
439 			classHierarchy.add(c);
440 
441 		// Ensure we process parents before children.
442 		Collections.reverse(classHierarchy);
443 
444 		// Process CacheRegions first
445 		for (Class<?> beanType : classHierarchy)
446 			processCacheRegions(beanType);
447 
448 		// Process CacheRegion entries and inject caches.
449 		boolean containsCacheRegion = false;
450 		for (Class<?> beanType : classHierarchy)
451 			if (processFields(beanType, bean))
452 				containsCacheRegion = true;
453 
454 		if (containsCacheRegion)
455 			configuredBeans.put(bean, this);
456 	}
457 
458 	/**
459 	 * Processes all annotated fields inside the given class instance.
460 	 *
461 	 * @param type	 The type to operate on.
462 	 * @param instance The instance implementing the type (may be a subclass of the type).
463 	 * @return true if a field was processed.
464 	 */
465 	boolean processFields(Class<?> type, Object instance) {
466 		boolean containsCacheRegion = false;
467 		if (type != null) {
468 			for (Field field : type.getDeclaredFields()) {
469 				CacheRegion region = field.getAnnotation(CacheRegion.class);
470 				if (region != null) {
471 					containsCacheRegion = true;
472 
473 					// Abort here if not started, instances will get recorded and configured when in starting phase.
474 					if (!isStarted() && !isStarting())
475 						break;
476 
477 					String cacheName = getCacheName(region, field);
478 					declareCacheIfNeeded(cacheName, region);
479 					Cache cache = get(cacheName, region);
480 
481 					Method setter = null;
482 					String fieldName = field.getName();
483 					String setterName = "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
484 					try {
485 						// If we use a setter we're looking from top to bottom to give implementations
486 						// the chance to override it (Note: only public setters are supported).
487 						setter = instance.getClass().getMethod(setterName, field.getType());
488 					} catch (NoSuchMethodException e) {
489 						if (log.isTraceEnabled()) {
490 							log.trace("Did not find setter for field (" + field.toGenericString() +
491 									"), setting the cache instance for '" + cacheName + "' directly.");
492 						}
493 					}
494 
495 					try {
496 						if (field.getType().isAssignableFrom(AdvancedCache.class))
497 							cache = cache.getAdvancedCache();
498 
499 						if (field.getType().isAssignableFrom(Cache.class)) {
500 							if (setter == null) {
501 								if (!field.isAccessible())
502 									field.setAccessible(true);
503 								field.set(instance, cache);
504 							} else
505 								setter.invoke(instance, cache);
506 						} else {
507 							String msg = "Found a field (" + field.toGenericString() + ") that was annotated with " +
508 									"CacheRegion but did not use a one of the supported types: " +
509 									"'org.infinispan.Cache', 'java.util.concurrent.ConcurrentMap<?,?>' or " +
510 									"'java.util.Map<?, ?>'.";
511 							throw new IllegalArgumentException(msg);
512 						}
513 					} catch (Exception e) {
514 						throw new RuntimeException();
515 					}
516 				}
517 			}
518 		}
519 		return containsCacheRegion;
520 	}
521 
522 	/**
523 	 * Processes the CacheRegions annotation on class and package of the given class.
524 	 *
525 	 * @param type The type to operate on.
526 	 */
527 	void processCacheRegions(Class<?> type) {
528 		if (type == null)
529 			return;
530 
531 		final Package typePackage = type.getPackage();
532 		CacheRegions[] allRegions = {
533 				type.getAnnotation(CacheRegions.class),
534 				typePackage == null ? null : typePackage.getAnnotation(CacheRegions.class)
535 		};
536 
537 		for (CacheRegions regions : allRegions) {
538 			if (regions != null)
539 				for (CacheRegion region : regions.value()) {
540 					if (region.name().isEmpty()) {
541 						String msg = "A CacheRegion that was specified inside a CacheRegions annotation " +
542 								"must have a non-empty region name because the default naming scheme " +
543 								"cannot be applied. Look at the regions annotation defined inside " +
544 								"'" + type + "' or its package info.";
545 						throw new IllegalArgumentException(msg);
546 					}
547 
548 					EmbeddedCacheManager cm = getCacheManager(region.cacheManager());
549 					if (cm == null)
550 						continue;
551 
552 					String cacheName = getCacheName(region, null, null);
553 					declareCacheIfNeeded(cm, cacheName, region);
554 				}
555 		}
556 	}
557 
558 	boolean declareCacheIfNeeded(String cacheName, CacheRegion region) {
559 		EmbeddedCacheManager cm = getCacheManager(region.cacheManager());
560 		return cm != null && declareCacheIfNeeded(cm, cacheName, region);
561 	}
562 
563 	boolean declareCacheIfNeeded(EmbeddedCacheManager cm, String cacheName, CacheRegion region) {
564 		if (!cm.getCacheNames().contains(cacheName))
565 			return declareCache(cm, cacheName, createConfiguration(cacheName, region), region);
566 		return false;
567 	}
568 
569 	/**
570 	 * Declares a new cache using the given cache configuration and region.
571 	 *
572 	 * @param cm			the cache manager to register the cache in.
573 	 * @param configuration The configuration to initialize the cache with.
574 	 * @param cacheRegion   The cache region to declare the cache with.
575 	 * @return True if the declaration was successful, false if the cache existed already.
576 	 */
577 	boolean declareCache(EmbeddedCacheManager cm, String cacheName,
578 	                     Configuration configuration, CacheRegion cacheRegion) {
579 		try {
580 			// Create a self-populating cache if an object source was given.
581 			if (cacheRegion.valueFactory() != ObjectSource.class) {
582 				ObjectSourceCacheLoaderConfig loaderConfig = new ObjectSourceCacheLoaderConfig();
583 				loaderConfig.setObjectSourceClass(cacheRegion.valueFactory().getName());
584 				CacheLoaderManagerConfig managerConfig = configuration.getCacheLoaderManagerConfig();
585 				managerConfig.addCacheLoaderConfig(loaderConfig);
586 			}
587 
588 			configuration = cm.defineConfiguration(cacheName, cacheRegion.inheritFrom(), configuration);
589 			cm.getCache(cacheName);
590 
591 			if (log.isTraceEnabled()) {
592 				log.trace("Declared new cache '{}' with configuration '{}'",
593 						cacheName, configurationToXmlString(configuration));
594 			} else {
595 				log.info("TJEE-01010:Configured cache region '{}' with '{}'",
596 						cacheName, configurationToString(configuration));
597 			}
598 
599 			return true;
600 		} catch (Throwable t) {
601 			log.error("TJEE-00950:Failed declaring cache '" + cacheName + "' with configuration '" +
602 					configurationToXmlString(configuration) + "', caused by ", t);
603 			return false;
604 		}
605 	}
606 
607 	private String configurationToString(Configuration configuration) {
608 		StringBuilder b = new StringBuilder(128);
609 		b.append("CacheConfiguration{").
610 				append("elementsInHeap=").append(configuration.getEvictionMaxEntries()).
611 				append(", eviction=").append(configuration.getEvictionStrategy()).
612 				append(", idleTime=").append(configuration.getExpirationMaxIdle()).
613 				append(", expireTime=").append(configuration.getExpirationLifespan()).
614 				append(", persistent=").append(configuration.getCacheLoaderManagerConfig().getFirstCacheLoaderConfig() != null).
615 				append(", cacheMode=").append(configuration.getCacheMode());
616 		return b.append(" }").toString();
617 	}
618 
619 	private String configurationToXmlString(Configuration configuration) {
620 		try {
621 			return new InfinispanNamedCacheFragment(configuration).toString();
622 		} catch (Exception e) {
623 			return configuration.toString();
624 		}
625 	}
626 
627 	/**
628 	 * Retrieves the cache of the given name and region.
629 	 *
630 	 * @param cacheName   The name of the cache to retrieve.
631 	 * @param cacheRegion The cacheRegion of the cache.
632 	 * @return An instance of Cache.
633 	 */
634 	Cache get(String cacheName, CacheRegion cacheRegion) {
635 		EmbeddedCacheManager cm = getCacheManager(cacheRegion.cacheManager());
636 		if (cm == null)
637 			return null;
638 
639 		return cm.getCache(cacheName);
640 	}
641 
642 	/**
643 	 * Resolves the cache name for the given region and target.
644 	 *
645 	 * @param cacheRegion The cacheRegion of the cache.
646 	 * @param field	   The annotated field used to inject the cache instance.
647 	 * @return A name to use for the cache.
648 	 */
649 	String getCacheName(CacheRegion cacheRegion, Field field) {
650 		return getCacheName(cacheRegion, field.getDeclaringClass(), field.getName());
651 	}
652 
653 	/**
654 	 * Resolves the cache name for the given region and target.
655 	 *
656 	 * @param cacheRegion The cacheRegion of the cache.
657 	 * @param target	  The target class declaring the field (may be empty if region defines a name).
658 	 * @param fieldName   The name of the annotated field used to inject the cache instance.
659 	 * @return A name to use for the cache.
660 	 */
661 	String getCacheName(CacheRegion cacheRegion, Class target, String fieldName) {
662 		if (cacheRegion.name().isEmpty()) {
663 			Assert.assertNotNull("target", target);
664 			Assert.assertNotNull("fieldName", fieldName);
665 			return target.getSimpleName() + "." + fieldName;
666 		}
667 		return cacheRegion.name();
668 	}
669 }