Camel read properties file - java

How do I configure the use of a properties file using Java DSL and the Main object?
According to this page I should be able to call something like:
main.setPropertyPlaceholderLocations("example.properties");
However that simply doesn't work. It seems that option wasn't added until Camel 2.18 and I'm running 2.17.1.
What was the original way to set a properties file to use when letting the application run in a standalone form?
Some backstory:
I'm trying to convert from Spring to Java DSL. During that conversion I was attempting to have my Camel application run on its own. I know that is achieved using main.run();.
I had things "functioning" when using the CamelContext, but that cannot run on its own. So I know using the following will work in that case:
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("classpath:/myProperties.properties");
context.addComponent("properties", pc);
Is there some way I can tell the main to use that setup? Or is there something else needed?

You can use the following snippet:
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("classpath:/myProperties.properties");
main.getCamelContexts().get(0).addComponent("properties", pc);
Also, if you are using camel-spring, you could use org.apache.camel.spring.Main class, it should use the property placeholder from your application context.

Since you are mentioning you are in the process to move from Spring XML to Java Config here's a minimum application that is using properties and injecting it into a Camel route (it's really properties management in Spring injected into our Camel route bean):
my.properties:
something=hey!
Main class:
package camelspringjavaconfig;
import org.apache.camel.spring.javaconfig.CamelConfiguration;
import org.apache.camel.spring.javaconfig.Main;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#ComponentScan("camelspringjavaconfig")
#PropertySource("classpath:my.properties")
public class MyApplication extends CamelConfiguration {
public static void main(String... args) throws Exception {
Main main = new Main();
main.setConfigClass(MyApplication.class); // <-- passing to the Camel Main the class serving as our #Configuration context
main.run(); // <-- never teminates
}
}
MyRoute class:
package camelspringjavaconfig;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class MyRoute extends RouteBuilder {
#Autowired
Environment env; //<-- we are wiring the Spring Env
#Override
public void configure() throws Exception {
System.out.println(env.getProperty("something")); //<-- so that we can extract our property
from("file://target/inbox")
.to("file://target/outbox");
}
}

Related

Setting "spring.config.name" property programmatically does not work

According to this blog post from December 2017, it is possible to change the name used to search for Spring Boot configuration files programmatically like this:
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.properties("spring.config.name:conf")
.build()
.run(args);
}
}
I have tried this using Spring Boot version 1.5.9-RELEASE, but this does not work. Setting spring.config.name as an argument does work:
mvn spring-boot:run -Dspring.config.name=conf
However, I do not have control over the arguments passed to my Spring Boot application when it is started so this is not an option.
Is it no longer possible to set spring.config.name programmatically, am I missing something, or is this a bug?
Doesn't directly answer the question, but I eventually discovered a workaround. Setting the spring.config.name by adding it to the arguments does work:
package com.ups.cep;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
List<String> arguments = new ArrayList<>(Arrays.asList(args));
arguments.add("-Dspring.config.name=conf");
SpringApplication.run(Application.class, arguments.toArray(new String[arguments.size()]));
}
}
Your example works for me (Spring Boot 2.4.3) so it could be bug at that time.
A similar SO answer also uses SpringApplicationBuilder.properties(String... defaultProperties) to define configuration name.
new SpringApplicationBuilder(Application.class)
.properties("spring.config.name:conf")
.build()
.run(args);
I checked both spring.config.name=conf and spring.config.name:conf and they work.

Execute Method on Spring start

i want to execute some code during (or rather at the end of) application startup. I found a couple of resources doing this using #PostConstruct annotation, #EventListener(ContextRefreshedEvent.class), implementing InitializingBean, implementing ApplicationListener... All of them execute my code at startup, but the placeholder of the application properties are not replaced at that moment. So if my class has a member with an #Value("${my.property}") annotation, it returns "${my.property}" instead of the actual value defined in the yaml (or wherever).
How do i accomplish to execute my code after the replacement took place?
You can implement InitializingBean which has a method named afterPropertiesSet(). This method will be called after all properties placeholders are replaced.
#PostConstruct is called when bean is created. Ypu have to check if spring found file with properties.
If you have a config class, #Configuration, then you can try explicitly importing your properties file by adding the following annotation:
#PropertySource("classpath:your-properties-file.properties")
Any other non-config resources should load after your config classes and your #Value annotations should work fine.
You should implement ApplicationListener<ContextRefreshedEvent> like this:
#Component
public class SpringContextListener implements ApplicationListener<ContextRefreshedEvent> {
#Value("${my.property}")
private String someVal;
/**
* // This logic will be executed after the application has loded
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
// Some logic here
}
}
You can get it after spring boot start.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
#Component
#Order(0)
class ApplicationReadyInitializer implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
ResourceLoader resourceLoader;
#Value("${my.property}")
private String someVal;
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// App was started. Do something
}
}

How to find the Interface EmbeddedServletContainerCustomizer in spring boot 2.0

I want to change bind port when the application runningļ¼Œ
But Meet a error message 'EmbeddedServletContainerCustomizer cannot be resolved to a type'.
My Spring boot version is 2.0.0.BUILD-SNAPSHOT.
the following code:
import org.springframework.boot.context.embedded.*;
import org.springframework.stereotype.Component;
#Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setPort(9000);
}
}
thanks a lot
As far as the port is concerned, I'd use the configuration option as already answered.
However, you can still use a customizer, however, the types and location will change in Spring Boot 2.0, see:
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
#Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
#Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
SpringBoot has an easy configuration for binding port, just use server.port to set the customized port in application.properties
Use SpringApplicationBuilder to set the property server.port programatically. use this inside your spring boot main method.
HashMap<String, Object> properties = new HashMap<>();
properties.put("server.port", 9000);
new SpringApplicationBuilder()
.properties(properties)
.run(args);

spring boot: run different init code for prod and tests

I am converting an existing Java application to Spring Boot.
I'm a Spring Boot newbie. I know Spring Boot has support for application.properties, which is great for new apps. But this is a legacy app and I need an interim solution for now - without completely redesigning it. This app needs to call some initializer with a property file on startup. There is prod.properties and another one for tests - test.properties.
the prod file needs to be loaded from a specific location on disk (/mydir/prod.properties), while test.properties comes from tests' classpath.
also, may legacy classes require this "property class" to be initialized on their creation (they reference it in their static initializers, oh geez...) - so this init needs to happen before Spring Components are loaded.
what would be the easiest solution?
I came up with this overly-verbose-boiler-plate solution. it works, but maybe there is an easier way?
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.inject.Inject;
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
#Inject
private Initializer initializer;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
initializer.init();
}
}
public interface Initializer {
void init();
}
#Component
public class ProdInitializer implements Initializer {
#Override
public void init() {
System.out.println("PROD init - will load properties file from /myfolder/ and call some init class with it");
}
}
// and this is from TESTS section:
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
#Primary
#Component
public class TestInitializer implements Initializer {
#Override
public void init() {
System.out.println("TEST init - will load properties file from classpath and call some init class with it");
}
}
I tried having just one initializer with an additional test/resources/application.properties file with just 1 value in it (legacy property file location) hoping that Spring Boot would load application.properties from PROD code, then load the only value present in test/resources/application.properties and override the corresponding value in main/resources/application.properties, but no luck - apparently, Spring Boot ignores main/resources/application.properties for tests if test/resources/application.properties is present. so I would have to duplicate all settings from main/resources/application.properties in test/resources/application.properties with just one property being different.

Do annotations on a class still get called when only accessing static members?

I have the following class:
package hello;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
#Component
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
If I reference it in my main camel route, like so:
package com.example.integration;
import hello.*;
import org.apache.camel.ProducerTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestCamelSpring {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("camelspring.xml");
ProducerTemplate camelTemplate = context.getBean("camelTemplate", ProducerTemplate.class);
Application.main(args);
System.out.println("Message Sending started");
camelTemplate.sendBody("jms:queue:testQSource","Sample Message");
System.out.println("Message sent");
}
}
Do my annotations in Application.class still get accessed even though I only reference Application.main?
I ask because the #EnableAutoConfiguration is supposed to configure the application for Tomcat, but now that I am not running Application.class directly, the application is defaulting to jetty and then I get an error that WebSockets are only supported in Tomcat.
Has anyone had this issue before or know how to solve it?
Here is the stack trace. I can see from the console log that it never starts the Tomcat instance that it does when the whole class is accessed in the example. It seems to be continuing as if it is a jetty app rather than Tomcat. Please correct me if any of these assumptions are wrong:
Caused by: java.lang.IllegalStateException: Websockets are currently only supported in Tomcat (found class org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory).
at org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration$1.customize(WebSocketAutoConfiguration.java:74)
at org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor.postProcessBeforeInitialization(EmbeddedServletContainerCustomizerBeanPostProcessor.java:67)
at org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor.postProcessBeforeInitialization(EmbeddedServletContainerCustomizerBeanPostProcessor.java:54)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:407)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
... 16 more
First of all, annotations cannot be "called".
Annotations are data, not code. In your case Spring Boot reads your annotations when you call SpringApplication.run(Application.class, args); and performs necessary configurations, therefore it doesn't matter how you call Application.main().
I guess your problem is caused by the fact that you have Jetty in the classpath, and it forces Spring Boot to use Jetty rather than Tomcat as embedded servlet container.
So, try to do the following:
Find out how Jetty appeared in your classpath
Use mvn dependency:tree -Dverbose if you use Maven
If you don't need Jetty in the classpath, exclude it from dependencies
Otherwise, you need to force Spring Boot to ignore presence of Jetty
Something like exclude = EmbeddedServletContainerAutoConfiguration.EmbeddedJetty.class in #EnableAutoConfiguration may help

Categories