Required: Definitive Annotations and pom dependencies to test Apache Camel with Spring Boot and Java Config - spring-boot

I'm going round in circles with this one.
Because it uses Spring Test and Java Config, I copied the simple FilterTest example from here, which worked OK as was.
But as soon as I added an #Autowired field, I got an UnsatisfiedDependencyException [Unsatisfied dependency expressed through field ... No qualifying bean of type ... available].
After fixing that I now get a NullPointerException on resultEndpoint.expectedBodiesReceived(expectedBody); in the first #Test method. When this happens, there is absolutely no output in the console, so evidently Spring hasn't even started.
Here's the slightly modified test class:
package org.apache.camel.spring.javaconfig.patterns;
import org.apache.camel.EndpointInject;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.spring.javaconfig.SingleRouteCamelConfiguration;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import c10y.camel.converter.QbInvoiceConverter;
/**
* Tests filtering using Spring Test and Java Config
* page: https://camel.apache.org/manual/latest/testing.html
* code: https://github.com/apache/camel/blob/master/core/camel-core/src/test/java/org/apache/camel/processor/FilterTest.java
*/
#CamelSpringBootTest
#ContextConfiguration
#DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
public class FilterTest {
#EndpointInject("mock:result")
protected MockEndpoint resultEndpoint;
#Produce("direct:start")
protected ProducerTemplate template;
#Autowired
private QbInvoiceConverter converter;
#Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
resultEndpoint.expectedBodiesReceived(expectedBody);
template.sendBodyAndHeader(expectedBody, "foo", "bar");
resultEndpoint.assertIsSatisfied();
}
#Test
public void testSendNotMatchingMessage() throws Exception {
resultEndpoint.expectedMessageCount(0);
template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
resultEndpoint.assertIsSatisfied();
}
#Configuration
public static class ContextConfig extends SingleRouteCamelConfiguration {
#Bean
public RouteBuilder route() {
return new RouteBuilder() {
public void configure() {
from("direct:start")
.filter(header("foo")
.isEqualTo("bar"))
.to("mock:result")
;
}
};
}
}
}
I have tried to bring this up to date with the migration instructions here. Briefly, this states:
Migration steps linked to JUnit 5 support in Camel Test itself should have been applied first
Imports of org.apache.camel.test.spring.* should be replaced with org.apache.camel.test.spring.junit5.*
Usage of #RunWith(CamelSpringRunner.class) should be replaced with #CamelSpringTest
Usage of #BootstrapWith(CamelTestContextBootstrapper.class) should be replaced with #CamelSpringTest
Usage of #RunWith(CamelSpringBootRunner.class) should be replaced with #CamelSpringBootTest
The other part of my question relates to having the correct dependencies in pom.xml. I'm learning Camel from Camel in Action E2, so my pom has test dependencies from the book as well as others I have picked up on the way; so there may be clashes or incompatibilities in there:
<!-- from .../camel-in-action-2/chapter9/spring-boot-test-one-route/pom.xml -->
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test</artifactId>
<scope>test</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring</artifactId>
<scope>test</scope>
</dependency>
<!-- from elsewhere -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring-junit5</artifactId>
<scope>test</scope>
</dependency>
I'm using Apache Camel 3.4.0 LTS and Spring Boot 2.3.0.RELEASE.
As the title to this question states, I'd really like to have the definitive annotation and pom configuration to allow me to simply get on with creating test routes to unit test my Components, Converters, and so forth [without tearing my hair out!].
PS: What's the difference between org.junit.Test and org.junit.jupiter.api.Test annotations, and when should you use one over the other?

Related

Connect to both ActiveMQ and IBM MQ

I am using the following Maven dependency and class in my Spring Boot application to send messages to ActiveMQ:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
#Component
#EnableJms
public class MQSender {
#Autowired
private JmsTemplate jmsTemplate;
public void sendMsgActiveMQ(String msg) {
jmsTemplate.convertAndSend("DEV.QUEUE1", msg);
}
/* public void sendMsgIBMMQ(String msg) {
jmsTemplate.convertAndSend("DEV.QUEUE1", msg);
}*/
}
How could I use the same class or any other class within the same application to send messages to IBM MQ as well? If I add the add the below dependency how will the #Autowired JmsTemplate behave?
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>mq-jms-spring-boot-starter</artifactId>
<version>2.5.0</version>
</dependency>
You can get the IBM MQ client jars file with Maven dependency:
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>com.ibm.mq.allclient</artifactId>
<version>9.1.0.6</version>
</dependency>
As to your #Autowired question, your problem is ambiguity by type. You will need JMS connections factories for the two brokers that have the same type. The article here has some good suggestions.
I prefer to only #Autowire in test cases, where the application context is small. For a practical application, I prefer explicit initialization. (maybe I'm just used to it). In any event, have a look here github for some examples that may have be useful.

Unit Testing Freemarker templates in SpringBoot - unable to initialize freemarker configuration

we are using Freemarker for generating the HTML code for the emails our application is going to be sending.
Our usage and configuration is based off of https://github.com/hdineth/SpringBoot-freemaker-email-send
Particularly:
package com.example.techmagister.sendingemail.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean;
import java.io.IOException;
#Configuration
public class FreemarkerConfig {
#Bean(name="emailConfigBean")
public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration(ResourceLoader resourceLoader) {
FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean();
bean.setTemplateLoaderPath("classpath:/templates/");
return bean;
}
}
However, there is absolutely no information or documentation anywhere, about how to run Unit Tests for this using JUnit 5.
When I added the relevant dependencies
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
versions:
<junit.jupiter.version>5.3.1</junit.jupiter.version>
<mockito.version>2.23.0</mockito.version>
And made a test class:
package com.example.techmagister.sendingemail;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.io.IOException;
#ExtendWith({SpringExtension.class, MockitoExtension.class})
#Import(com.example.techmagister.sendingemail.config.FreemarkerConfig.class)
public class EmailTestTest {
private static final Logger LOGGER = LogManager.getLogger(EmailTestTest.class);
#Autowired
#Qualifier("emailConfigBean")
private Configuration emailConfig;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void test() throws Exception {
try {
Template template = emailConfig.getTemplate("email.ftl");
} catch (IOException e) {
e.printStackTrace();
}
}
}
When I run that in debug mode, emailConfig is null.
Why is that?
Their test example https://github.com/hdineth/SpringBoot-freemaker-email-send/blob/master/src/test/java/com/example/techmagister/sendingemail/SendingemailApplicationTests.java
works if I add the same autowired property, but it is a full SprintBoot context that is slow to boot, and I need to test just template usage, without actually sending out the email.
In our actual code (which is large, multi module project), I have another error org.springframework.beans.factory.UnsatisfiedDependencyException
caused by:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'freemarker.template.Configuration' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=emailConfigBean)}
But that is just for context, first I want to get it working in the simple, sample project then worry about getting it working in our complex one.
You cannot autowire your emailConfigBean directly as a freemarker.template.Configuration
FreeMarkerConfigurationFactoryBean is a factorybean.
To get the Confuguration you need to call factorybean.getObject()
so instead of
#Autowired
#Qualifier("emailConfigBean")
private Configuration emailConfig;
just autowire your factorybean FreeMarkerConfigurationFactoryBean and load your template with emailConfig.getObject().getTemplate("email.ftl")
#Autowired
#Qualifier("emailConfigBean")
private FreeMarkerConfigurationFactoryBean emailConfig;
#Test
void testFreemarkerTemplate(){
Assertions.assertNotNull(emailConfig);
try {
Template template =
emailConfig
.getObject() // <-- get the configuration
.getTemplate("email.ftl"); // <-- load the template
Assertions.assertNotNull(template);
} catch (Exception e) {
e.printStackTrace();
}
working test on github
On the other hand...
In a Spring Boot application the Freemarker configuration can be simplified by using the spring-boot-starter-freemarker dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
This starter for building MVC web applications using FreeMarker views adds the necessary auto-configuration. All you need to do is placing your template files in the resources/templates folder.
Then you just can autowire the freemarkerConfig (or use constructor injection):
#Autowired
private Configuration freemarkerConfig;
There is a nice example here, in the attached github code. I was able to use it as a starting point to test my freeMarker code: https://cleantestcode.wordpress.com/2014/06/01/unit-testing-freemarker-templates/

Can't run JUnit test with Spring

I have a test class looking like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class myTests {
#Autowired
private MyComponent myComponent;
#Test
public void test1() { ... }
}
I get an initialization error and an exception:
java.lang.Exception: No tests found matching...
Take a look at your imports; for your code to work in the way you intend it to, your imports should include:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
but depending on how your IDE is configured you can sometimes end up with a different version of junit by default which doesn't play nicely with Spring Boot.
If the above imports don't work for you you may need to add the following maven dependency to your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

How to configure Java8TimeDialect manually in a Spring Boot application

Since I'm using Thymeleaf only for email template processing (not for view creation), I have the following exclusion in my application class (i.e. a class extending SpringBootServletInitializer):
#SpringBootApplication(exclude=org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration.class)
Absent Spring Boot's autoconfiguration, I need to configure the Java8TimeDialect class manually, so I've added the following bean to my ThymeleafConfig class:
#Bean
public Java8TimeDialect java8TimeDialect() {
return new Java8TimeDialect();
}
Nonetheless, I'm still getting the following exception:
Attempted to call method
format(java.time.LocalDateTime,java.lang.String) on null context
object
Clearly the temporals object isn't being added to the context. How can I resolve this?
I've included Thymeleaf in my POM file as follows:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
Add pom.xml
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
then:
package com.users.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
import nz.net.ultraq.thymeleaf.LayoutDialect;
#Configuration
public class Thymeleaf {
//TemplateEngine templateEngine = new TemplateEngine();
#Bean
public Java8TimeDialect java8TimeDialect() {
return new Java8TimeDialect();
}
#Bean
public LayoutDialect layoutDialect() {
return new LayoutDialect();
}
Finnally:
The time is: <strong th:text="${#temporals.format(now, 'dd/MMM/yyyy HH:mm')}">31/12/2015 15:00</strong>

How to enable basic caching with Spring Data JPA

I am trying to enable basic caching with Spring Data JPA. But I cannot understand why the DAO methods are still querying the database instead of using the cache.
Given the following Spring Boot 1.5.1 application
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class Server{
public static void main(String[] args) {
SpringApplication.run(Server.class, args);
}
}
Controller
#Controller
public class PasswordsController {
#Autowired
private PasswordService service;
#SuppressWarnings("unchecked")
#RequestMapping("/passwords.htm")
public void passwords(Map model,
HttpServletRequest request) {
model.put("passwords", service.getPasswords(request));
}
...
Service
#Service
#Transactional
public class PasswordService extends BaseService {
#Autowired
private PasswordJpaDao passwordDao;
public Collection<Password> getPasswords(HttpServletRequest request) {
Collection<Password> passwords = passwordDao.getPasswords(params);
return passwords;
}
...
Interface
#Transactional
public interface PasswordJpaDaoCustom {
public Collection<Password> getPasswords(PasswordSearchParameters params);
}
and implementation
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;
import com.crm.entity.Password;
import com.crm.search.PasswordSearchParameters;
#Transactional
#Repository
public class PasswordJpaDaoImpl implements PasswordJpaDaoCustom {
#PersistenceContext
private EntityManager em;
#Override
#Cacheable("passwords")
public Collection<Password> getPasswords(PasswordSearchParameters params) {
System.err.println("got here");
return em.createQuery(hql, Password.class);
}
...
Maven Dependencies
<!-- Spring Boot start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Spring Boot end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
I understand that Spring Boot will implicitly use ConcurrentHashMap for caching without any specific configuration necessary?
But the getPasswords() dao method is always called instead of using the cache. Why is this?
Yes, spring boot by default uses ConcurrentHashMap for caching and the issue with your code is that you did not set any key for your passwords cache, so it is calling the database every time for fetching the data.
So you need to the key (any unique identifier) using the params object variables as shown below:
#Cacheable(value="passwords", key="#params.id")//any unique identifier
public Collection<Password> getPasswords(PasswordSearchParameters params) {
System.err.println("got here");
return em.createQuery(hql, Password.class);
}

Resources