Getting an EhCache instance with Spring... intelligently - spring

I need to get a specific EhCache instance by name and I'd prefer to autowire if possible. Given the following automatically configured controller, how can I autowire in the cache instance I'm looking for?
#Controller
public class MyUniqueService {
...
}
<beans ...>
<ctx:component-scan base-package="my.controllers"/>
<mvc:annotation-driven />
</beans>
How do I configure EhCache in my application context? I don't see any log messages from EhCache about it loading the ehcache.xml file in my /WEB-INF/ directory. How do I make it load it?
How can I integrate EhCache with my Spring application to have it load the ehcache.xml file from my /WEB-INF/ directory and autowire a cache by a given name into my MyUniqueService controller?

First you need to create a Ehcache CacheManager singleton in you app context like this:
<bean id="myEhCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:my-ehcache.xml"/>
</bean>
Here configLocation is set to load from classpath or use value="/WEB-INF/my-ehcache.xml".
In your controller simply inject the CacheManager instance:
#Controller
public class MyUniqueService {
#Resource(name="myEhCacheManager")
private CacheManager cacheManager;
...
}
Alternatively, if you'd like to go the "entirely autowired" route, do:
<bean class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="/WEB-INF/ehcache.xml"/>
</bean>
</property>
</bean>
Setup your class like so:
#Controller
public class MyUniqueService {
#Autowired
private org.springframework.cache.CacheManager cacheManager;
public org.springframework.cache.Cache getUniqueObjectCache() {
return cacheManager.getCache("uniqueObjectCache");
}
}
uniqueObjectCache corresponds to this cache instance in your ehcache.xml cache definition:
<cache name="uniqueObjectCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"
transactionalMode="off"/>
There isn't a way to inject an actual cache instance, but as shown above, you can inject a cache manager and use it to get the cache you're interested in.

Assuming you have cacheManager defined:
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:/ehcache.xml"/>
</bean>
You can get/inject specific cache like this:
#Value("#{cacheManager.getCache('myCacheName')}")
private Cache myCache;
See also examples how to use Spring EL inside the #Value() http://www.mkyong.com/spring3/spring-el-method-invocation-example/ if you are interested.

You can also use autowire if the context can find a bean with the correct class. Here is how I configured my xml
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>WEB-INF/ehcache.xml</value>
</property>
</bean>
<bean id="cache" class="net.sf.ehcache.Cache" factory-bean="cacheManager" factory-method="getCache">
<constructor-arg value="CacheNameHere" />
</bean>
And my java class
#Autowired
private net.sf.ehcache.Cache cache;
This setup works for me.

Indeed! Or if you want to use a java config class:
#Inject
private ResourceLoader resourceLoader;
#Bean
public CacheManager cacheManager() {
EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
try {
ehCacheCacheManager.setCacheManager(ehcacheCacheManager().getObject());
} catch (Exception e) {
throw new IllegalStateException("Failed to create an EhCacheManagerFactoryBean", e);
}
return ehCacheCacheManager;
}
#Bean
public FactoryBean<net.sf.ehcache.CacheManager> ehcacheCacheManager() {
EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
bean.setConfigLocation(resourceLoader.getResource("classpath:ehcache.xml"));
return bean;
}

Related

Enabling JMX in Spring Boot

Here is my build.gradle...
dependencies {
compile('org.springframework.boot:spring-boot-starter-parent:2.0.1.RELEASE')
compile('org.springframework.boot:spring-boot-starter-batch')
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("org.springframework.boot:spring-boot-starter-web")
compile("com.h2database:h2")
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.batch:spring-batch-test')
}
Which I thought would enable JMX by default. I go to JConsole, connect to the application and expect to see a org.springframework.boot folder under java.util.logging, I see nothing.
So, now I pick a few of my custom beans and add #ManagedResource, I know see these.
However, what if I want to expose spring batch beans like #JobOperator how do I do this?
Pre Spring Boot, I could so something like:
<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="spring:service=batch,bean=jobOperator">
<bean class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="jobOperator"/>
<property name="interceptorNames" value="exceptionTranslator" />
</bean>
</entry>
</map>
</property>
<property name="assembler">
<bean class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
<property name="interfaceMappings">
<map>
<entry key="spring:service=batch,bean=jobOperator"
value="org.springframework.batch.core.launch.JobOperator"/>
</map>
</property>
</bean>
</property>
</bean>
When I define my JobOperator, in my #Configuration file in Spring Boot I do:
#Bean
public JobOperator jobOperator() throws Exception {
SimpleJobOperator simpleJobOperator = new SimpleJobOperator();
// the operator wraps the launcher
simpleJobOperator.setJobLauncher(this.jobLauncher);
...
}
I can't add #ManagedResource under the #Bean annotation. So how do I expose the JobOperator as a JMX bean?
Here is how i did it in my code -
#Bean
public MBeanExporter exporter(){
MBeanExporter m = new MBeanExporter();
Map<String,Object> map = new HashMap<String, Object>();
JmxBean testBean = (JmxBean)ctx.getBean("testBean");
map.put("testBean",testBean);
m.setBeans(map);
return m;
}
#Bean
public JmxBean testBean(){
return new JmxBean("test",100);
}
In your case since you want to register jobOperator, just replace testBean in exporter bean with it.

cutom ehcache evict policy with spring

If we want to custom evict policy besides LRU LFU FIFO, the way docs recommanded is to implement interface Policy then set MemoryStoreEvictionPolicy like:
manager = new CacheManager(EHCACHE_CONFIG_LOCATION);
cache = manager.getCache(CACHE_NAME);
cache.setMemoryStoreEvictionPolicy(new MyPolicy());
but if I used spring, use #cacheable and xml files like
<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" ></property>
</bean>
<!-- cacheManager -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="cacheManagerFactory" />
</bean>
how can I inject my own policy in spring way?
thank you all
You may be best to implement your own class that sets the eviction policy on the cache when Spring initializes.
For example:
public class MyEvictionPolicySetter implements InitializingBean {
public static final String CACHE_NAME = "my_cache";
private CacheManager manager;
private Policy evictionPolicy;
#Override
public void afterPropertiesSet() {
Cache cache = manager.getCache(CACHE_NAME);
cache.setMemoryStoreEvictionPolicy(evictionPolicy);
}
public void setCacheManager(CacheManager manager) {
this.manager = manager;
}
public void setEvictionPolicy(Policy evictionPolicy) {
this.evictionPolicy = evictionPolicy;
}
}
And then in your Spring config:
<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" ></property>
</bean>
<!-- Specify your eviction policy as a Spring bean -->
<bean id="evictionPolicy" class="MyPolicy"/>
<!-- This will set the eviction policy when Spring starts up -->
<bean id="evictionPolicySetter" class="EvictionPolicySetter">
<property name="cacheManager" ref="cacheManagerFactory"/>
<property name="evictionPolicy" ref="evictionPolicy"/>
</bean>

#Autowired entityManagerFactory is null

My app can't autowire entityManagerFactory.
My applicationContext.xml:
<tx:annotation-driven/>
<context:component-scan base-package="top.level.package" />
<bean id="persistenceUnitManager"
class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocation">
<value>classpath:jpa-persistence.xml</value>
</property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="persistenceUnitManager" />
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
My java class:
#Component
public class Engine {
#Autowired
#Qualifier("entityManagerFactory")
private EntityManagerFactory entityManagerFactory;
......
}
Question:
Why entityManagerFactory is null?
For spring to do autowring using annotations you have to tell spring that. In your xml configuration (assuming you don't already have context:component-scan element) add a context:annotation-config. This will instruct the spring application context to scan for annotations (like #Autowired, #Inject, #Resource etc.) to do autowiring.
Also make sure that the class you want the EntityManagerFactory to be injected into (the Engine class) is a spring managed bean. Spring will only inject references into spring managed beans.
Have you tried with this ?
private EntityManagerFactory entityManagerFactory;
#Autowired
#PersistenceUnit(unitName = "myUnitName")
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
I had the same error some time ago, and I found this a solution.
If you craeate your Engine with new Engine() (as stated in one of your comments), it's not managed by Spring. #Component has no effect then and dependencies won't be injected. You need to inject your engine.

How to log properties loaded by Spring?

is it possible to simply log all the content of the properties file loaded by spring with <context:property-placeholder /> ?
Thanks
You can set the log level of org.springframework.core.env.PropertySourcesPropertyResolver to "debug". Then, you will be able to see the value of the properties during resolving.
You can do it this way:
<context:property-placeholder properties-ref="myProperties"/>
<bean id="myProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
.. locations
</list>
</property>
</bean>
and add a logging bean similar to one below (here annotation based and with slf4j api):
#Component
public class PropertiesLogger {
private static Logger logger = LoggerFactory.getLogger(PropertiesLogger.class);
#Resource("myProperties")
private Properties props;
#PostConstruct
public void init() {
for (Map.Entry<Object, Object> prop : props.entrySet()) {
logger.debug("{}={}", prop.getKey(), prop.getValue());
}
}
}

Inject a file resource into Spring bean

What is a good way to inject some file resource into Spring bean ?
Now i autowire ServletContext and use like below. Is more elegant way to do that in Spring MVC ?
#Controller
public class SomeController {
#Autowired
private ServletContext servletContext;
#RequestMapping("/texts")
public ModelAndView texts() {
InputStream in = servletContext.getResourceAsStream("/WEB-INF/file.txt");
// ...
}
}
Something like this:
#Controller
public class SomeController {
private Resource resource;
public void setResource(Resource resource) {
this.resource = resource;
}
#RequestMapping("/texts")
public ModelAndView texts() {
InputStream in = resource.getInputStream();
// ...
in.close();
}
}
In your bean definition:
<bean id="..." class="x.y.SomeController">
<property name="resource" value="/WEB-INF/file.txt"/>
</bean>
This will create a ServletContextResource using the /WEB-INF/file.txt path, and inject that into your controller.
Note you can't use component-scanning to detect your controller using this technique, you need an explicit bean definition.
Or just use the #Value annotation.
For single file:
#Value("classpath:conf/about.xml")
private Resource about;
For multiple files:
#Value("classpath*:conf/about.*")
private Resource[] abouts;
What do you intend to use the resource for? In you example you don't do anything with it.
From it's name, however, it looks like you are trying to load internationalisation / localisation messages - for which you can you a MessageSource.
If you define some beans (possibly in a separate messages-context.xml) similar to this:
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>WEB-INF/messages/messages</value>
</list>
</property>
</bean>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="en_GB" />
</bean>
Spring will load your resource bundle when you application starts. You can then autowire the MessageSource into your controller and use it to get localised messages:
#Controller
public class SomeController {
#Autowired
private MessageSource messageSource;
#RequestMapping("/texts")
public ModelAndView texts(Locale locale) {
String localisedMessage = messageSource.getMessage("my.message.key", new Object[]{}, locale)
/* do something with localised message here */
return new ModelAndView("texts");
}
}
NB. adding Locale as a parameter to your controller method will cause Spring to magically wire it in - that's all you need to do.
You can also then access the messages in your resource bundle in your JSPs using:
<spring:message code="my.message.key" />
Which is my preferred way to do it - just seems cleaner.

Resources