Configuring Camel-Spring for standalone Camel microservice in a multi-module Gradle project - spring

Goal: Adapting Prototype microservice example from Ch7 of Camel in Action by Claus Ibsen into a Gradle multi-module project.
I followed the Spring Guide to 'Creating a Multi Module Project' among other resources.
Project Structure:
+ main-mm-build
|--+ src
|--+ main
|--+ java // Spring Boot microservice (A) in this tree
|--+ build.gradle
|--+ settings.gradle
|--+ contact-manager // standalone-camel-spring microservice (B)
|--+ src
|--+ main
|--+ java // standalone-camel-spring microservice (B) here
|--+ build.gradle
Got as far as:
microservice A (Spring Boot Rest controller) can invoke microservice B (standalone Camel exposing rest() on jetty, without Spring DI).
But what I really want is to use Spring DI (without Spring Boot) in microservice B.
After making changes for camel-spring I get an error.
It's probably mucked up Gradle config, but I could use some help.
main-mm-build/contact-manager$ ../gradlew build
main-mm-build/contact-manager$ java -jar build/libs/contact-manager-1.0.jar
.
.
.
Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: rest://get:/contact-manager?consumerComponentName=jetty&consumes=application%2Fjson&outType=...dto.ContactDto%5B%5D&produces=application%2Fjson&routeId=route2 due to: null
at ...impl.engine.AbstractCamelContext.getEndpoint(AbstractCamelContext.java:801)
Caused by: java.lang.NullPointerException
at ...camel.spring.spi.ApplicationContextBeanRepository.lookupByNameAndType(Ap..j:45)
Root project Gradle file:
plugins {
id 'org.springframework.boot' version '2.3.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
sourceCompatibility = '11'
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-tomcat'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
allprojects {
group = 'el.cam'
repositories {
jcenter()
}
}
subprojects {
version = '1.0'
}
settings.gradle:
rootProject.name = 'main-mm-build'
include 'contact-manager'
Microservice B (contact-manager) build.gradle:
plugins {
id 'org.springframework.boot'
id 'io.spring.dependency-management'
id 'java'
id 'application'
}
sourceCompatibility = '11'
mainClassName = 'el.cam.contacts.ContactManagerApplication'
task fatJar(type: Jar) {
manifest {
attributes (
'Main-Class': mainClassName
)
}
baseName = 'contact-manager' + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
dependencies {
implementation(platform("org.springframework.boot:spring-boot-dependencies:2.3.1.RELEASE"))
implementation( platform("org.apache.camel:camel-spring-boot-dependencies:3.0.0-RC3") )
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.apache.camel:camel-spring-boot-starter'
implementation 'org.apache.camel:camel-rest-starter'
implementation 'org.apache.camel:camel-jetty-starter'
implementation 'org.apache.camel:camel-jackson-starter'
implementation 'org.apache.camel:camel-swagger-java-starter'// '3.0.0-RC3'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
ContactManagerApplication.java ( I wonder if I'm configuring Spring Camel incorrectly here. All the examples I found were based on Spring boot autoconfiguration, so I just figured it out as I went.)
import el.cam.contacts.configuration.ContactManagerConfiguration;
import org.apache.camel.spring.Main;
public class ContactManagerApplication {
private static final Logger LOG = LoggerFactory.getLogger(ContactManagerApplication.class);
public static void main(String[] args) throws Exception {
Main main = new Main();
main.setApplicationContext(createSpringApplicationContext());
// main.addRoutesBuilder(contactManagerController); // DI using Spring Autowiring
main.run();
}
private static AbstractApplicationContext createSpringApplicationContext() {
AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext();
appContext.register(ContactManagerConfiguration.class);
appContext.refresh();
return appContext;
}
}
Configuration class:
#Configuration
#ComponentScan(basePackages = "el.cam.contacts")
public class ContactManagerConfiguration {
#Autowired
ContactManagerController contactManagerController;
#Bean
public CamelContext camelContext() throws Exception {
SpringCamelContext camelContext = new SpringCamelContext();
camelContext.addRoutes(contactManagerController);
camelContext.setPropertiesComponent(properties());
camelContext.addComponent("rest", rest());
camelContext.addComponent("rest-api", restApi());
camelContext.addComponent("jetty", jetty());
return camelContext;
}
#Bean
public PropertiesComponent properties() throws Exception {
PropertiesComponent properties = new PropertiesComponent();
properties.setLocation("classpath:application.properties");
return properties;
}
#Bean
public RestComponent rest() {
RestComponent rest = new RestComponent();
return rest;
}
#Bean
public RestApiComponent restApi() {
RestApiComponent restApi = new RestApiComponent();
return restApi;
}
#Bean
public JettyHttpComponent jetty() {
JettyHttpComponent jettyHttpComponent = new JettyHttpComponent9();
return jettyHttpComponent;
}
Controller class:
#Component
public class ContactManagerController extends RouteBuilder {
#Autowired
ContactManagerService contactManagerService;
#Override
public void configure() throws Exception {
// before Camel-Spring, was using this to bind serviceBean in camel registry
// getContext().getRegistry().bind("contactManagerService", new ContactManagerService());
// TODO using default. camel property sources not picking up application.properties!
restConfiguration("jetty").port("{{port:8282}}").contextPath("api")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("disableFeatures", "FAIL_ON_EMPTY_BEANS")
.apiContextPath("api-doc")
.enableCORS(true);
// define the rest service
rest("/contact-manager").consumes("application/json").produces("application/json")
.get().outType(ContactDto[].class)
.to("bean:contactManagerService?method=getContacts(${header.contactType})")
;
}
}

Related

Added Vaadin to Java Spring project in IntelliJ and getting build error with add()

I have created a new project with Spring Initializr:
Project: Gradle Project
Language: Java
Spring Boot: 2.7.4
Packaging: JAR
Java: 8
Dependencies:
Spring Boot Actuator, Spring Data JPA, Spring Web, H2 Database, PostgresSQL Driver, Spring Configuration Processor
After this I added some code to be able to interact with REST APIs (GET & POST). I was able to build, run, & test the project.
The next step was add Vaading, so I did the following:
Created a new package "views.main" package under the source section.
Added a MainView.java class with the following contents:
package io.enfuse.demo.fundemo.views.main;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
#Route("")
public class MainView {
public MainView() {
VerticalLayout todosList = new VerticalLayout();
TextField taskField = new TextField();
Button addButton = new Button("Add");
addButton.addClickListener(click -> {
Checkbox checkbox = new Checkbox(taskField.getValue());
todosList.add(checkbox);
});
addButton.addClickShortcut(Key.ENTER);
add(
new H1("Vaadin Todo"),
todosList,
new HorizontalLayout(
taskField,
addButton
)
);
}
}
I also updated the build.gradle file to include Vaadin items:
plugins {
id 'org.springframework.boot' version '2.7.4'
id 'io.spring.dependency-management' version '1.0.14.RELEASE'
id 'com.vaadin' version '23.2.1'
id 'java'
}
group = 'io.enfuse.demo'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('vaadinVersion', "23.2.1")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.vaadin:vaadin-spring-boot-starter'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "com.vaadin:vaadin-bom:${vaadinVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}
Restart IntelliJ
At this point when I go to build I get the following error:
```
C:\Temp\fundemo_v2\fundemo\src\main\java\io\enfuse\demo\fundemo\views\main\MainView.java:24: error: cannot find symbol
add(
^
symbol: method add(H1,VerticalLayout,HorizontalLayout)
location: class MainView
```
I can look at the dependecies that show Vaadin is included:
What is missing exactly is not setup correctly?
Your MainView does not extend a Vaadin component and that's why there is no add method.
Try this:
public class MainView extends Div() {

spring integration RedisLockRegistry with spring-context

We have decided to use RedisLockRegistry by adding spring-integration to my project developed with Spring boot 1.5.7.
But when we add spring-integration-starter and spring-integration-redis to the project, quartz gives an error.
If I change the spring-context with spring-context-support or if I add spring-context-support, it is not an error.
Why should I add spring-context-support instead of spring-context or change it either?
gradle
buildscript {
ext {
springBootVersion = '1.5.7.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
allprojects {
repositories {
mavenCentral()
mavenLocal()
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: "io.spring.dependency-management"
apply plugin: 'maven'
apply plugin: 'idea'
sourceCompatibility = 1.8
idea {
module {
sourceDirs += file('build/classes/main/generated')
generatedSourceDirs += file('build/classes/main/generated')
}
}
dependencies {
compileOnly 'org.projectlombok:lombok'
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'com.h2database:h2'
testCompile 'org.springframework.security:spring-security-test'
}
}
group = 'com.example.redislock'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
compileJava.dependsOn(processResources)
dependencies {
compile 'org.springframework.boot:spring-boot-starter-aop'
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-integration'
compile 'org.quartz-scheduler:quartz:2.3.0'
compile 'org.springframework:spring-core'
//compile 'org.springframework:spring-context'
compile 'org.springframework:spring-context-support'
compile 'org.springframework.integration:spring-integration-redis'
compile 'org.hibernate:hibernate-java8'
compile 'io.springfox:springfox-swagger2:2.6.1'
compile 'io.springfox:springfox-swagger-ui:2.6.1'
compile 'org.springframework.boot:spring-boot-starter-actuator'
compile 'org.flywaydb:flyway-core:4.2.0'
compileOnly 'org.springframework.boot:spring-boot-configuration-processor'
runtime 'org.postgresql:postgresql:42.2.1'
}
This is error giving class:
package com.example.redislock.DemoRedisLockGradle.scheduler;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
#Configuration
public class ExampleScheduler {
#Bean(name = "asdTrigger")
public CronTriggerFactoryBean triggerFactoryBean(#Qualifier("asdJobFactory") JobDetailFactoryBean jobFactory) {
return createTriggerFactoryBean(jobFactory.getObject());
}
#Bean(name = "asdJobFactory")
public JobDetailFactoryBean asd() {
return createJobDetailFactoryBean(ExampleJob.class);
}
CronTriggerFactoryBean createTriggerFactoryBean(JobDetail jobDetail) {
CronTriggerFactoryBean ctFactory = new CronTriggerFactoryBean();
ctFactory.setName("Example Factory");
ctFactory.setCronExpression("0 0/1 * * * ?");
ctFactory.setJobDetail(jobDetail);
return ctFactory;
}
JobDetailFactoryBean createJobDetailFactoryBean(Class<? extends Job> jobClass) {
JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(jobClass);
return factory;
}
}
It cant find quartz.CronTriggerFactoryBean and quartz.JobDetailFactoryBean
Thanks in advance
I found the solution. If ı exclude spring-data-redis on spring-integration-redis it's not need adding spring-context-support
compile('org.springframework.boot:spring-boot-starter-integration')
compile('org.springframework.integration:spring-integration-redis') {
exclude group: 'org.springframework.data', module: 'spring-data-redis'
}

New Functional Web Framework with jetty

I wanted to setup an example for New in Spring 5: Functial Web Framework
So I set up a RouteConfiguration:
#Configuration
public class RouteConfiguration {
#Autowired
private MyService myService;
#Bean
public RouterFunction<?> routerFunction() {
return route(
GET("/first")
, myService::getItemsFirst)
.and(route(
GET("/second")
, myService::getItemsSecond));
}
}
I started my application using jetty and at first it seemed to work... until I wanted to call one of my methods: localhost:8080/first and it returned a 404.
Did I define my route configuration wrong or why arent the routes accessible?
EDIT
With netty you need to provide a Server Configuration Like the following:
#Configuration
public class HttpServerConfiguration {
#Autowired
private Environment environment;
#Bean
public HttpServer httpServer(final RouterFunction<?> routerFunction) {
final HttpHandler httpHandler = RouterFunctions.toHttpHandler(routerFunction);
final ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
final HttpServer server = HttpServer.create("localhost", Integer.valueOf(this.environment.getProperty("server.port")));
server.newHandler(adapter);
return server;
}
}
But I could not find something like this for jetty.
EDIT 2
My Dependencies:
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencyManagement {
dependencies {
dependency (group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '2.0.0.M1')
dependencySet (group: 'org.hibernate', version: '5.2.8.Final') {
entry 'hibernate-core'
entry 'hibernate-entitymanager'
entry 'hibernate-spatial'
}
}
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-hateoas')
compile('org.springframework.boot:spring-boot-starter-jetty')
compile('org.springframework.boot:spring-boot-starter-webflux') {
exclude module: 'spring-boot-starter-reactor-netty'
}
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-autoconfigure')
compile('org.springframework.boot:spring-boot-actuator')
compile('org.springframework.cloud:spring-cloud-starter-consul')
compile('org.springframework.cloud:spring-cloud-starter-consul-discovery')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('junit:junit')
}
Spring-Boot Version: 2.0.0.M3
Reading the comments, it seems this was an issue with dependencies bringing spring-boot-starter-web; if it is present, a Spring MVC application is started by Spring Boot.
There's a way to explicitly tell Spring Boot the type of the application, in the main Application class:
public static void main(String[] args) {
SpringApplication application = new SpringApplication(AgentApplication.class);
application.setWebApplicationType(WebApplicationType.REACT‌​IVE);
application.run(args);
}

Gradle build with fatJar plugin and SpringBoot application gives 'Application startup failed'

Everything was working fine when starting my app using Intellij. But when I made fatJar (with gradle plugin: eu.appsatori.fatjar) and execute:
java -jar myapp.jar
I'm getting something like this:
11:41:01.224 [main] ERROR org.springframework.boot.SpringApplication - Application startup failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [my.testing.MyAppMain]; nested exception is java.lang.IllegalArgumentException: No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.
at org.springframework.context.annotation.ConfigurationClassParser.processDeferredImportSelectors(ConfigurationClassParser.java:482)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:184)
...
It looks like it didn't found auto configuration classes in META-INF/spring.factories.
How to add this file? And what should be the content of it?
I've got following build script:
apply plugin: "java";
apply plugin: "idea";
apply plugin: 'application'
apply plugin: 'eu.appsatori.fatjar'
apply plugin: 'org.springframework.boot'
repositories {
mavenCentral()
}
buildscript {
ext {
springBootVersion = '1.4.3.RELEASE'
}
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
}
}
sourceSets {
main {
java {
srcDir 'src/main/java'
}
resources {
srcDir 'src/main/resources'
}
}
test {
java {
srcDir 'src/test/java'
}
}
}
fatJar {
manifest {
attributes("Main-Class": 'my.testing.MyAppMain')
}
exclude 'META-INF/*.DSA'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.RSA'
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-jdbc'
runtime 'mysql:mysql-connector-java'
testCompile 'org.springframework.boot:spring-boot-starter-test'
}
And my example code is:
package my.testing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
#SpringBootApplication
public class MyAppMain {
private ConfigurableApplicationContext springContext;
#Autowired
private SimpleDao dao;
public static void main(String[] args) throws Exception {
MyAppMain test = new MyAppMain();
try {
test.init();
test.doWhatYouGotToDo();
} finally {
test.stop();
}
}
private void doWhatYouGotToDo() {
System.out.println("Auto-wired dao: " + dao.hashCode());
System.out.println("Auto-wired jdbcTemplate: " + dao.jdbcTemplate.hashCode());
}
private void init() throws Exception {
springContext = SpringApplication.run(MyAppMain.class);
springContext.getAutowireCapableBeanFactory().autowireBean(this);
}
private void stop() throws Exception {
springContext.close();
}
}
#Component
class SimpleDao {
#Autowired
JdbcTemplate jdbcTemplate;
}
application.properties file:
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/some_db?useSSL=false&serverTimezone=UTC
spring.datasource.username = some_user
spring.datasource.password = some_pass
NOTE: This question is based on SpringBoot - making jar files - No auto configuration classes found in META-INF/spring.factories
where are all answers are referring to building with Maven. Please put only answers related to Gradle here.
Although I mostly use Maven for Spring and Gradle for Android, but here is the gradle way for a Spring project:
gradle clean build
gradle bootRepackage
Result:
Here is my build.gradle file:

The dreadful "Unable to start EmbeddedWebApplicationContext" error

I have the following exception when trying to run integration test:
org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
I read many forum entries but not found any solution. My files are as follows:
Integration test
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = WebInitializer.class)
#DataJpaTest
#Sql("/db/data.sql")
public class ReportEventIntTest {
#Autowired
private TestRestTemplate restTemplate;
#Test
public void reportEvent() {
Map<String, String> eventMap = new HashMap<>();
this.restTemplate.postForEntity("/worker/event", eventMap, String.class);
}
}
Spring Boot config
#Configuration
#ComponentScan(basePackages = {"org.reaction.engine.collector.controller",
"org.reaction.engine.persistence.service",
"org.reaction.engine.persistence.converter",
"org.reaction.engine.service"})
#EnableAutoConfiguration
#ImportResource("classpath:applicationContext.xml")
#Profile("threadPool") // define the default profile: it can be overridden by -Dspring.profiles.active=...
public class WebInitializer extends SpringBootServletInitializer implements WebApplicationInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(WebInitializer.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(WebInitializer.class, args);
}
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
return factory;
}
}
Gradle file
apply plugin: 'org.springframework.boot'
apply plugin: 'war'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.4.3.RELEASE'
}
}
dependencies {
compile project(':common')
compile 'org.springframework:spring-context-support'
compile 'org.springframework.boot:spring-boot-starter'
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
compile 'org.springframework.boot:spring-boot-starter-integration'
compile 'org.springframework.boot:spring-boot-starter-actuator'
compile 'org.apache.httpcomponents:httpclient:4.5.2'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
providedCompile 'javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0'
runtime 'mysql:mysql-connector-java'
// ---------------------- TESTING ----------------------
testCompile 'com.jayway.restassured:rest-assured:2.9.0'
testCompile 'org.springframework.boot:spring-boot-starter-test'
//testRuntime 'org.hsqldb:hsqldb'
testRuntime 'com.h2database:h2'
testRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}
Any idea?
I would greatly appreciate any thought!
Regards,
V.
I read many stackoverflow entries but I missed the following one:
#Profile cause Unable to start EmbeddedWebApplicationContext
The problem is that I defined the profile in my spring boot config class but I have to do that in the test class.
#ActiveProfiles("threadPool")

Resources