render multiple suffixes with one template engine in spring-boot - spring

I created two thymleaf resolvers and added them to a template engine, so I can process txt/svg templates in spring-boot.
#Configuration
public class TemplateEngineConfig {
#Bean
public SpringTemplateEngine textTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(textTemplateResolver());
templateEngine.addTemplateResolver(svgTemplateResolver());
return templateEngine;
}
private ITemplateResolver textTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setOrder(1);
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".txt");
templateResolver.setTemplateMode(TemplateMode.TEXT);
templateResolver.setCacheable(false);
return templateResolver;
}
private ITemplateResolver svgTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setOrder(2);
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".svg");
templateResolver.setTemplateMode(TemplateMode.TEXT);
templateResolver.setCacheable(false);
return templateResolver;
}
}
I use it like this.
#Component
public class TemplateRenderer {
#Autowired
#Qualifier("textTemplateEngine")
private TemplateEngine textTemplateEngine;
public String renderPlainText(String filename, Map<String, Object> params) {
Context context = new Context(LocaleContextHolder.getLocale(), params);
return textTemplateEngine.process(filename, context);
}
}
It didn't work. I tried to render templates/img.svg, but Thymleaf complained that it couldn't find templates/img.txt file, how can I make it work?

Just force your SpringTemplateEngine to check for the existence of the templates and not only use the defined patterns. Please force that by adding
templateResolver.setCheckExistence(true);
to each of your template resolvers.

I think you should marked ITemplateResolver with #bean and Qualifier so when
you will #autowire textTemplateEngine or svgTemplateEngine so spring will autowire them with Qualifier name.
#Bean
#Qualifier("textTemplateEngine")
private ITemplateResolver textTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setOrder(1);
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".txt");
templateResolver.setTemplateMode(TemplateMode.TEXT);
templateResolver.setCacheable(false);
return templateResolver;
}
#Bean
#Qualifier("svgTemplateEngine")
private ITemplateResolver svgTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setOrder(2);
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".svg");
templateResolver.setTemplateMode(TemplateMode.TEXT);
templateResolver.setCacheable(false);
return templateResolver;
}
Also you can configure Thymeleaf multiple resolvers by xml config like:
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8" />
<property name="templateEngine">
<bean class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="dialects">
<set>
<bean class="org.thymeleaf.spring4.dialect.SpringStandardDialect" />
</set>
</property>
<property name="templateResolvers">
<set>
<bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="cacheable" value="false" />
<property name="prefix" value="/fragments/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
</bean>
<bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="cacheable" value="false" />
<property name="prefix" value="/views/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
</bean>
</set>
</property>
</bean>
</property>
</bean>

Related

Spring property Injection using annotations

I want to configure below type of bean initialization to be performed by annotation.
Below is sample bean configuration in xml type and want to configure this kind of bean using annotations.
<bean id="Animal" class="aaa.type.Animal">
<property name="Animal" value="${Animal}" />
<property name="AnimalFamily" >
<bean class="aaa.type.AnimalFamily">
<property name="AnimalCharactertitic">
<list>
<bean class="aaa.type.AnimalColor">
<property name="name" value="Color" />
<property name="value" value="${color}" />
</bean>
<bean class="aaa.type.AnimalType">
<property name="name" value="Animal Type" />
<property name="value" value="${AnimalType}" />
</bean>
</list>
</property>
</bean>
</property>
</bean>
You can do something like this:
#Configuration
public class MyConfigurationClass {
#Bean
public AnimalCharactertitic animalColor(#Value("${color}") String color) {
AnimalCharactertitic animalCharactertitic = new AnimalCharactertitic();
animalCharactertitic.setName("Color");
animalCharactertitic.setValue(color);
return animalCharactertitic;
}
#Bean
public AnimalCharactertitic animalType(#Value("${AnimalType}") String animalType) {
AnimalCharactertitic animalCharactertitic = new AnimalCharactertitic();
animalCharactertitic.setName("Animal Type");
animalCharactertitic.setValue(animalType);
return animalCharactertitic;
}
#Bean
public AnimalFamily animalFamily(#Autowired AnimalCharactertitic animalColor,
#Autowired AnimalCharactertitic animalType) {
AnimalFamily animalFamily = new AnimalFamily();
List<AnimalCharactertitic> animalCharactertitics = new ArrayList<>();
animalCharactertitics.add(animalColor);
animalCharactertitics.add(animalType);
animalFamily.setAnimalCharactertitic(animalCharactertitics);
return animalFamily;
}
#Bean
public Animal animal(#Value("${Animal}") String animal, #Autowired AnimalFamily animalFamily) {
Animal animal = new Animal();
animal.setAnimal(animal);
animal.setAnimalFamily(animalFamily);
return animal;
}
}

How can I create bean for adviceChain?

I'm having a hard time converting this spring xml into java config. How can I define an adviceChain bean base on the below spring file.
This's my xml file:
<util:list id="adviceChain">
<ref bean="retryInterceptor"/>
</util:list>
<bean id="retryInterceptor" class="org.springframework.amqp.rabbit.config.StatelessRetryOperationsInterceptorFactoryBean">
<property name="messageRecoverer">
<bean class="customDiscardingMessageRecoverer" />
</property>
<property name="retryOperations" ref="retryTemplate" />
</bean>
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.FixedBackOffPolicy">
<property name="backOffPeriod" value="3000" />
</bean>
</property>
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="3" />
</bean>
</property>
</bean>
Here is what I tried:
#Bean
StatelessRetryOperationsInterceptorFactoryBean retryInterceptor() {
StatelessRetryOperationsInterceptorFactoryBean retryInterceptor = new StatelessRetryOperationsInterceptorFactoryBean();
DiscardingMessageRecoverer messageRecoverer = new DiscardingMessageRecoverer();
retryInterceptor.setMessageRecoverer(messageRecoverer);
retryInterceptor.setRetryOperations(retryTemplate());
return retryInterceptor;
}
#Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(3000);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
#Bean
SimpleMessageListenerContainer listenerContainer(){
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setConcurrentConsumers(1);
listenerContainer.setAdviceChain(new Advice[] { (Advice) retryInterceptor() });
listenerContainer.setMessageListener(listenerAdapter());
listenerContainer.setQueues(queue());
return listenerContainer;
}
I receive org.springframework.amqp.rabbit.config.StatelessRetryOperationsInterceptorFactoryBean$$EnhancerBySpringCGLIB$$e3de830c cannot be cast to org.aopalliance.aop.Advice error at the line listenerContainer.setAdviceChain(new Advice[] { (Advice) retryInterceptor() });
I think this's because I didn't define AdviceChain bean like the spring xml file but so far I don't know how.
Edit StatelessRetryOperationsInterceptorFactoryBean like this :
#Bean
RetryOperationsInterceptor retryInterceptor() {
StatelessRetryOperationsInterceptorFactoryBean retryInterceptor = new StatelessRetryOperationsInterceptorFactoryBean();
DiscardingMessageRecoverer messageRecoverer = new DiscardingMessageRecoverer();
retryInterceptor.setMessageRecoverer(messageRecoverer);
retryInterceptor.setRetryOperations(retryTemplate());
return retryInterceptor.getObject();
}

Spring configuration split between xml resource and Java configuration

I am trying to mix both xml and Java Configuration.
I have a spring-security.xml resource that I import in my application boot.
Say this was a part of the initial xml:
<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="${ldap.url}" />
<property name="base" value="${ldap.base}" />
<property name="userDn" value="${ldap.user}" />
<property name="password" value="${ldap.password}" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="ldapContextSource" />
</bean>
Can I move just this part to be a Java config ? Or would the references be an issue.
Thank You
You can move it to Java Config
Declare configuration class
#Configuration
public class AppConfig {
#Bean
public LdapContextSource ldapContextSource(){
LdapContextSource lcontext = new LdapContextSource();
lcontext.setUrl("${ldap.url}");
lcontext.setBase("${ldap.base}");
lcontext.setUserDn("${ldap.user}");
lcontext.setPassword("${ldap.password}");
return lcontext;
}
#Bean
public LdapTemplate LdapTemplate(){
LdapTemplate lTemplate = new LdapTemplate(ldapContextSource());
return lTemplate;
}
}
In your XML add
<context:annotation-config/>
<bean class="com.mypackage.AppConfig"/>

Spring Batch- Xml based configuration performance over Java based

I am trying to convert the spring batch configuration from xml based to annotation based.
Below is my xml based configuration.
<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>
<!-- Step will need a transaction manager -->
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="dbMapper" class="org.test.DBValueMapper">
</bean>
<bean id="dbMapperFlatfile" class="org.test.FlatFileRowMapper">
</bean>
<bean id="paramSetter" class="org.test.DBParamSetter">
</bean>
<bean id="dbReader" class="org.test.DBValueReader"
scope="step">
<property name="paramSetter" ref="paramSetter"/>
<property name="verifyCursorPosition" value="false" />
<property name="dataSource" ref="dataSource" />
<property name="sql" value="#{jobParameters['SQL_QUERY']}" />
<property name="rowMapper" ref="dbMapper" />
<property name="fetchSize" value="5000" />
</bean>
<bean id="dbWriterIO" class="org.test.TemplateWritterIO"
scope="step">
<property name="velocityEngine" ref="velocityEngine" />
<!-- <property name="rptConfig" value="#{jobParameters['RPT_CONFIGVAL']}" /> -->
<property name="headerCallback" ref="dbWriterIO" />
<property name="footerCallback" ref="dbWriterIO" />
</bean>
<batch:job id="fileGenJobNio">
<batch:step id="fileGenJobStempNio">
<batch:tasklet>
<batch:chunk reader="dbReader" writer="dbWriterNIO"
commit-interval="5000">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
Below is the equivalent Java based configuration:
#EnableBatchProcessing
#Import({ServiceConfiguration.class})
public class SRBatchGenerator extends DefaultBatchConfigurer{
#Autowired
private JobBuilderFactory jobBuilders;
#Autowired
private StepBuilderFactory stepBuilders;
#Autowired
private VelocityEngine velocityEngine;
#Autowired
private DBValueMapper mapper;
#Autowired
private DbHelper dbhelper;
#Autowired
private DataSource datasource;
#Bean
public Step step(){
return stepBuilders.get("step")
.chunk(5000)
.reader(reader())
//.processor(processor())
.writer(writer())
//.listener(logProcessListener())
.faultTolerant()
//.skipLimit(10)
//.skip(UnknownGenderException.class)
//.listener(logSkipListener())
.build();
}
#Bean
public Job fileGeneratorJob(){
return jobBuilders.get("fileGeneratorJob")
//.listener(protocolListener())
.start(step())
.build();
}
#Bean
public DBValueMapper mapper(){
return new DBValueMapper();
}
#Bean
#StepScope
public DBValueReader3 reader(){
String query="Select Test1,Test2,test3,test4 from RPt_TEST";
DBValueReader3 dbread = new DBValueReader3();
dbread.setSql(query);
dbread.setRowMapper(mapper);
dbread.setDataSource(datasource);
return dbread;
}
#Bean
#StepScope
public TemplateWritterIO writer(){
TemplateWritterIO writer=new TemplateWritterIO();
writer.setVelocityEngine(velocityEngine);
return writer;
}
#Override
protected JobRepository createJobRepository() throws Exception {
MapJobRepositoryFactoryBean factory =
new MapJobRepositoryFactoryBean();
factory.afterPropertiesSet();
return (JobRepository) factory.getObject();
}
}
When I execute my Job using xml based it took 27sec to write 1 Million record into Flat file.
But to write same 1 million record, Java based job took about 2 hours to write.
I am not sure what I am missing here. Can anyone help me or guide me why it is slow in Java based configuration.

Spring Jms #JmsListener annotation doesnt work

I have such method
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQName")
public void rceive(MySerializableObject message) {
log.info("received: {}", message);
}
and such config on xml
<jms:annotation-driven />
<bean id="jmsListenerContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrency" value="3-10" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${brokerURL}" />
</bean>
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="maxConnections" value="5" />
<property name="maximumActiveSessionPerConnection" value="500" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsContainerFactory" class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrency" value="3-10" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="pooledConnectionFactory" />
Seems consumer was not created. I can send messages but cannot receive them.
Any idea whats wrong here?
Just tested your config and it works well. Only difference that I make a class with #JmsListener as a <bean> in that context:
<bean class="org.springframework.integration.jms.JmsListenerAnnotationTests$TestService"/>
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class JmsListenerAnnotationTests {
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
private TestService testService;
#Test
public void test() throws InterruptedException {
this.jmsTemplate.convertAndSend("myQName", "foo");
assertTrue(this.testService.receiveLatch.await(10, TimeUnit.SECONDS));
assertNotNull(this.testService.received);
assertEquals("foo", this.testService.received);
}
public static class TestService {
private CountDownLatch receiveLatch = new CountDownLatch(1);
private Object received;
#JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "myQName")
public void receive(String message) {
this.received = message;
this.receiveLatch.countDown();
}
}
}
<jms:annotation-driven /> makes the #JmsListener infrastructure available, but to force Spring to see those methods your classes should be beans anyway.
For example <component-scan> for the package with #Service classes.
Cheers!

Resources