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);
Related
Tomcat architecture is comprised of the following elements: Server => Service => Engine => Host => Context
When configuring a standard Tomcat server, we can configure a custom thread pool by specifying the following in our server.xml file: (below is pseudo-code)
<Server>
<Service name="Catalina">
<Connector port="8080"/>
<Executor name="custom-pool" className="my.package.poolImplementation" />
<Engine name="Catalina" defaultHost="localhost">
<Here be more elements />
</Engine>
</Service>
</Server>
(specifically, the Executor name="custom-pool" className="my.package.poolImplementation")
How do I configure Spring Boot to allow the same behaviour programmatically ?
(WITHOUT using Spring configuration files)
No matter where i searched, or how hard I tried, I couldn't find any answer or example.
Thanks in advance
I looked up some source code (see TomcatServletWebServerFactory.java/ServletWebServerFactoryConfiguration.java) and found a way to do that.
#Bean
public TomcatProtocolHandlerCustomizer<?> tomcatProtocolHandlerCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(...);
};
}
I needed to customize Tomcat also.
I ended up with a code like this:
#Component
public class TomcatCustomizer extends TomcatServletWebServerFactory {
#Override
protected void postProcessContext(Context context) {
Engine engine = (Engine) context.getParent().getParent();
Service service = engine.getService();
Server server = service.getServer();
Connector connector = service.findConnectors()[0];
}
}
You can then set different properties of the server, service, engine, connector.
From the object service you can also access the executor and change it.
This part I never tried.
Whatever you change it will override and complete the spring-boot configuration, you will not loose the spring-boot config.
As Yonatan mentioned one can add an executor with
service.addExecutor(...)
there is also a method from removing an executor.
I needed this kind of detailed access to the configuration because I needed to configure the server.getGlobalNamingResources().
Also to add a JAASRealm and a Valve.
I do not see how one could achieve complete config access with just the customize method.
Considering two and a half years have passed since I originally asked this question, I think it is time that I shared our solution for the benefit of anyone that might read this in the future.
We ended up writing a custom component that implements WebServerFactoryCustomizer. Spring Boot will scan for all beans before starting its embedded Tomcat server. If it detects a bean that implements this interface, it will invoke the customize() method and pass the server factory as an argument to the function.
From there, it was straightforward:
package your.pack.age.name;
import org.apache.catalina.core.StandardThreadExecutor;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
#Component
public class TomcatServerConfig implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
private final StandardThreadExecutor customExecutor;
public TomcatServerConfig() {
this.customExecutor = YourExecutorImplementation();
}
#Override
public void customize(TomcatServletWebServerFactory factory) {
/*This web server is the Tomcat server embedded in Spring Boot*/
TomcatWebServer webServer = (TomcatWebServer)factory.getWebServer()
webServer.getTomcat().getService().addExecutor(this.customExecutor);
}
}
(The actual code we used was simplified here, for the sake of a clear answer)
It is also worth noting that similar code needs to be written for the Tomcat Connectors, using TomcatConnectorCustomizer:
package your.pack.age.name;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.stereotype.Component;
#Component
public class TomcatConnectorConfig implements TomcatConnectorCustomizer {
private final StandardThreadExecutor customExecutor;
public TomcatConnectorConfig() {
this.customExecutor = YourExecutorImplementation();
}
#Override
public void customize(Connector connector) {
connector.getProtocolHandler().setExecutor(this.customExecutor);
}
}
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.
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 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");
}
}
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.