How to use spring boot 2 and ehcache 3 without xml? - java

For now I have following config:
#Configuration
#EnableCaching
public class EhcacheConfig {
#Bean
public CacheManager cacheManager() throws URISyntaxException {
return new JCacheCacheManager(Caching.getCachingProvider().getCacheManager(
getClass().getResource("/ehcache.xml").toURI(),
getClass().getClassLoader()
));
}
}
It refers to the following XML:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<cache alias="pow_cache">
<key-type>org.springframework.cache.interceptor.SimpleKey</key-type>
<value-type>java.lang.Double</value-type>
<expiry>
<ttl unit="seconds">15</ttl>
</expiry>
<listeners>
<listener>
<class>my.pack.CacheEventLogger</class>
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
<event-ordering-mode>UNORDERED</event-ordering-mode>
<events-to-fire-on>CREATED</events-to-fire-on>
<events-to-fire-on>EXPIRED</events-to-fire-on>
</listener>
</listeners>
<resources>
<heap unit="entries">2</heap>
<offheap unit="MB">10</offheap>
</resources>
</cache>
</config>
And service look like this:
#Cacheable(value = "pow_cache", unless = "#pow==3||#result>100", condition = "#val<5")
public Double pow(int val, int pow) throws InterruptedException {
System.out.println(String.format("REAL invocation myService.pow(%s, %s)", val, pow));
Thread.sleep(3000);
return Math.pow(val, pow);
}
It works properly but I want to get free of xml configuration.
I've read and tried to apply following answer(last piece of code) But it works only for Ehcache 2 but I am going to use Eehcache 3
How can I achieve it ?

As EhCache seems to be JSR-107 compliant, you'll need to use it this way to have a programatic configuration:
#Bean
public CacheManager cacheManager() throws URISyntaxException {
CachingProvider provider = Caching.getCachingProvider();
CacheManager cacheManager = provider.getCacheManager();
CacheConfigurationBuilder<SimpleKey, Double> configuration =
CacheConfigurationBuilder.newCacheConfigurationBuilder(org.springframework.cache.interceptor.SimpleKey.class,
java.lang.Double.class,
ResourcePoolsBuilder.heap(2).offheap(10, MemoryUnit.MB))
.withExpiry(Expirations.timeToLiveExpiration(new Duration(15, TimeUnit.SECONDS)));
Cache cache = cacheManager.createCache("pow_cache", configuration);
cache.getRuntimeConfiguration().registerCacheEventListener(listener, EventOrdering.UNORDERED,
EventFiring.ASYNCHRONOUS, EnumSet.of(EventType.CREATED, EventType.EXPIRED));
return cacheManager;
}
Haven't tested it myself, but this should work for you.
Check out this programatic sample with more configuration options from the EhCache repo and the docs part on how to register listeners programatically too.

Here is my solution
#Bean
CacheManager getCacheManager() {
CachingProvider provider = Caching.getCachingProvider();
CacheManager cacheManager = provider.getCacheManager();
CacheConfigurationBuilder<String, String> configurationBuilder =
CacheConfigurationBuilder.newCacheConfigurationBuilder(
String.class, String.class,
ResourcePoolsBuilder.heap(1000)
.offheap(25, MemoryUnit.MB))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofHours(3)));
CacheEventListenerConfigurationBuilder asynchronousListener = CacheEventListenerConfigurationBuilder
.newEventListenerConfiguration(new CacheEventLogger()
, EventType.CREATED, EventType.EXPIRED).unordered().asynchronous();
//create caches we need
cacheManager.createCache("productCatalogConfig",
Eh107Configuration.fromEhcacheCacheConfiguration(configurationBuilder.withService(asynchronousListener)));
return cacheManager;
}
equivalent of :
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<cache alias="productCatalogConfig">
<key-type>java.lang.String</key-type>
<value-type>java.lang.String</value-type>
<expiry>
<ttl unit="seconds">1600</ttl>
</expiry>
<listeners>
<listener>
<class>com.klarna.config.CacheEventLogger</class>
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
<event-ordering-mode>UNORDERED</event-ordering-mode>
<events-to-fire-on>CREATED</events-to-fire-on>
<events-to-fire-on>EXPIRED</events-to-fire-on>
</listener>
</listeners>
<resources>
<heap unit="entries">1000</heap>
<offheap unit="MB">25</offheap>
</resources>
</cache>

Related

How to get Jndi name in java class in Spring

I want to get a JNDI value in my java conf Spring.
the context.xml file:
<Environment name="foo" type="java.lang.String" value="bar" />
the xml spring config:
<jee:jndi-lookup id="foobar" jndi-name="java:comp/env/foo" default-value="nothing"/>
the java spring config:
#Bean
public String foobar() {
???
}
If someone could you give me an example it'll be very useful. Thanks
Edit:
Try autowiring JndiObjectFactoryBean it will have a getJndiName() method, which is inherited from JndiObjectLocator will expose your jndi name
Try this
#Bean
public DataSource dataSource() {
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
DataSource dataSource = dsLookup.getDataSource("jdbc/yourJdbcGoesHere");
return dataSource;
}

Spring Batch hangs with no output

I have a Spring Batch job that I am launching from a Spring boot application, like so:
Main:
#SpringBootApplication
#ImportResource("jobApplicationContext.xml")
public class BatchJobRunner {
public static void main(String[] args) {
SpringApplication.run(BatchJobRunner.class, args);
}
}
In my job's application context, I have the following items:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:*.properties"/>
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry"/>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"/>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<batch:job id="myJob" job-repository="jobRepository">
<batch:split id="main" task-executor="simpleAsyncTaskExecutor" next="step3">
<batch:flow>
<batch:step id="flow1">
<!-- definition -->
</batch:step>
</batch:flow>
<batch:flow>
<batch:step id="flow2">
<!-- definition -->
</batch:step>
</batch:flow>
</batch:split>
<batch:step id="step3">
<batch:tasklet ref="someTasklet"/>
</batch:step>
</batch:job>
</beans>
And finally, I just run it like this:
java -jar my-module.jar
The job starts but:
It does not print out anything. Here is my log4j.properties:
log4j.rootLogger=INFO, stdout
log4j.logger.org.springframework.batch=INFO
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
The job hangs at the end. I put a Sys.out.print in the step3 and it does indeed print, but the spring boot application keeps running and never exits. I also tried to add an #AfterJob with a System.exit(..) and it didn't help either.
I am using Spring f/w 4.1.8, spring boot 1.2.8 and spring batch 3.0.6 (i cannot upgrade my spring-core as some dependencies use that version).
Any idea what I am doing wrong?
Edit:
Looks like beforeJob and afterJob listeners are not firing at all.
ClassCastExeption can be result of Spring different binding (early in xml and late in java). Try configure your batch fully in java. Result can look like this (this is with stored repository in DB, inmemory repository should looks similar):
#Configuration
#EnableBatchProcessing
#ComponentScan("my.batch.*")
#ImportResource("classpath:batch-config.xml")
#PropertySource(value = "classpath:batch.properties")
public class BatchConfiguration implements BatchConfigurer {
#Autowired
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
private JobRepository jobRepository;
private JobLauncher jobLauncher;
private JobExplorer jobExplorer;
#Override
public JobRepository getJobRepository() throws Exception {
return jobRepository;
}
#Override
public PlatformTransactionManager getTransactionManager() throws Exception {
return transactionManager;
}
#Override
public JobLauncher getJobLauncher() throws Exception {
return jobLauncher;
}
#Override
public JobExplorer getJobExplorer() throws Exception {
return jobExplorer;
}
#PostConstruct
public void initialize() {
try {
transactionManager = new DataSourceTransactionManager(dataSource);
jobRepository = createJobRepository();
jobExplorer = createJobExplorer();
jobLauncher = createJobLauncher();
} catch (Exception ex) {
throw new BatchConfigurationException(ex);
}
}
private JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean repoFactory = new JobRepositoryFactoryBean();
repoFactory.setDataSource(dataSource);
repoFactory.setTransactionManager(transactionManager);
repoFactory.setTablePrefix(PREFIX);
repoFactory.afterPropertiesSet();
return repoFactory.getObject();
}
private JobExplorer createJobExplorer() throws Exception {
JobExplorerFactoryBean explorerFactory = new JobExplorerFactoryBean();
explorerFactory.setDataSource(dataSource);
explorerFactory.setTablePrefix(PREFIX);
explorerFactory.afterPropertiesSet();
return explorerFactory.getObject();
}
private JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
}
You miss #EnableBatchProcessing docs.

spring integration with activemq

I'm utilizing spring for about a year and everything was pretty obvious and simple until I faced spring integration. Ashamed, I can't create Outbound Gateway to send message to remote JMS (ActiveMQ) channel. Before integration I just used JmsTemplates and #JmsListeners directly, there were no problems at all.
What's the difference between request-destination and request-channel?
Here are my configs:
#Configuration
#EnableJms
public class JmsConfig {
...
#Value("${activemq.url}")
private String brokerUrl;
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate(queueConnectionFactory());
jmsTemplate.setDefaultDestinationName(bookingChannelName);
return jmsTemplate;
}
/*
#Bean(name = "bookingChannel")
public Queue activeMQQueue() {
return new ActiveMQQueue(bookingChannelName);
}
*/
#Bean
public JmsListenerContainerFactory jmsListenerContainerFactory() {
SimpleJmsListenerContainerFactory listenerFactory = new SimpleJmsListenerContainerFactory();
listenerFactory.setConnectionFactory(queueConnectionFactory());
return listenerFactory;
}
...
}
and an xml one, where all I've got in root beans element is:
<jms:outbound-gateway request-destination-name="reqDestination" request-channel="bookingChannel" />
Then, there is a gateway code:
#MessagingGateway
public interface BookingGateway<T> {
#Gateway
void bookTicket(T ticket);
}
And finally, here is how I use a gateway:
#Component
public class BookingGatewayImpl<T> {
#Autowired
private BookingGateway bookingGateway;
public <U> void bookTicket(T ticket, BiConsumer<T, U> onStatusReceived) {
bookingGateway.bookTicket(ticket); // second param is not utilized yet
}
}
And when ticket is about to be booked, I get:
send is not supported, because no request channel has been configured
Also, I am not able to uncomment ActiveMQQueue bean from the first listing because spring says it is not compatible with MessageChannel:
Bean named 'bookingChannel' must be of type [org.springframework.messaging.MessageChannel], but was actually of type [org.apache.activemq.command.ActiveMQQueue]
Why oh why should the destination be of MessageChannel type? What exactly am I doing wrong and how to get this ticket message to be sent?
bookingChannel is a MessageChannel between your MessagingGateway and the jms gateway. The destination is the AMQP Queue.
Use
#Gateway(requestChannel="bookingChannel")
#Bean(name = "bookingDestination")
public Queue activeMQQueue() {
return new ActiveMQQueue(bookingChannelName);
}
#Bean(name = "bookingChannel")
public MessageChannel bookingChannel() {
return new DirectChannel();
}
<jms:outbound-gateway request-destination-name="bookingDestination" request-channel="bookingChannel" />
Finally, came up with following configuration. Almost understood the way it's working now:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:jms="http://www.springframework.org/schema/integration/jms"
xmlns:id="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="nio://127.0.0.1:61616"/>
</bean>
<bean id="requestQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="bookingChannel"/>
</bean>
<id:channel id="requestChannel"/>
<bean id="channelInterceptor" class="c.e.mentoring.integration.gateway.RequestChannelInterceptor"/>
<jms:outbound-channel-adapter channel="requestChannel" destination="requestQueue"/>
<int:channel-interceptor ref="channelInterceptor"/>
</beans>

Configuring liquibase for spring project

I have next db.changelog-master.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.7"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.7
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.7.xsd">
<changeSet id="19082014-1" author="autor">
<sql>
CREATE TABLE testings (
id character varying(80) NOT NULL
)
</sql>
</changeSet>
</databaseChangeLog>
My Spring configuration file is looking like this:
#Configuration
#PropertySource("classpath:user.properties")
public class LiquibaseConfig {
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.drivername"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Bean
public SpringLiquibase liquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource());
liquibase.setChangeLog("classpath:db.changelog-master.xml");
return liquibase;
}
}
So I'm expecting that new table should be created. But my database is still empty. I don't see any errors in logs, what am I doing wrong? Should I change my spring configuration class?
Okay, I think I fixed this problem. Everytime when you run your script you should change id tag in db.changelog-master.xml file.

Spring Using Redis #Cacheable

Can any one suggest a Redis (NoSQL DB) example for a Collection type?
Generally we use the following (in Spring):
#Cacheable(value = "PRODUCT", key = "#productId" )
public Map<String,Object> findProduct(String productId, String productName)
{ return map; }
which stores the key and value as String, but I need for:
public Map<RestaurantId,Set<Order>>find(String RestaurantId, String productName){ return map; }
Make sure your objects are Serializable, and then simply use the Spring Data Redis cache abstraction (2015 link)
Example copied from the documentation above:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:use-pool="true"/>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"/>
<!-- turn on declarative caching -->
<cache:annotation-driven />
<!-- declare Redis Cache Manager -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" c:template-ref="redisTemplate"/>
</beans>
You could use Redisson Redis Java client which provides Spring Cache manager as well. It supports both TTL and maxIdleTime parameters.
Code example:
#Configuration
#ComponentScan
#EnableCaching
public static class Application {
#Bean(destroyMethod="shutdown")
RedissonClient redisson() throws IOException {
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://127.0.0.1:7004", "redis://127.0.0.1:7001");
return Redisson.create(config);
}
#Bean
CacheManager cacheManager(RedissonClient redissonClient) {
Map<String, CacheConfig> config = new HashMap<String, CacheConfig>();
// create "testMap" cache with ttl = 24 minutes and maxIdleTime = 12 minutes
config.put("testMap", new CacheConfig(24*60*1000, 12*60*1000));
return new RedissonSpringCacheManager(redissonClient, config);
}
}

Categories

Resources