Override configuration prefix on IBM MQ Starter - change prefix from ibm.mq to infrastructure.ibm.mq - spring-boot

MQ starter has
#ConfigurationProperties(prefix = "ibm.mq")
public class MQConfigurationProperties {
I want to change the config prefix to infrastructure.ibm.mq and the rest of the hiearchy on the
config is the same
I want to avoid changing the MQConfiguration.java file, and recompiling, I just want to use the starter as is, but use a slightly different config prefix

This is one way I was able to override it. The #Primary means that this takes precedence. Otherwise you get errors about finding 2 beans where only a single is accepted.
#Bean
#Primary
#ConfigurationProperties(prefix = "my.local.prefix")
public MQConfigurationProperties localConfigurationProperties() {
return new MQConfigurationProperties();
}

Related

Load custom properties file in Spring Boot MVC Main

I have created a myApp.properties in resources folder location and mentioned the server.port in this file.
myApp.properties
myApp.server.port=8020
Now I want to read load this property into my application. But I have to read this before I actually a server.
Here I am trying to do like this
#SpringBootApplication
#ComponentScan(basePackages = {"com.myorg.myapp" })
#EnableConfigurationProperties
#PropertySource("classpath:myApp.properties")
#Component
public class MyAppApplication {
#Value("${myApp.server.port}")
private static String serverPort;
public static void main(String[] args) throws Exception{
try {
SpringApplication appCtxt = new SpringApplication(MyAppApplication.class);
appCtxt.setDefaultProperties(Collections
.singletonMap("server.port", serverPort));
appCtxt.run(args);
} catch (Exception e) {
e.printStackTrace();
}
}
But serverPort is coming as null.
I also tried to create a separate Config file like this but it can't be accessed in static main
#Configuration
#PropertySource("myApp.properties")
#ConfigurationProperties
public class MyAppConfig {
#Value("${myApp.server.port}")
private String serverPort;
/**
* #return the serverPort
*/
public String getServerPort() {
return serverPort;
}
}
Any suggestion would be helpful.
Spring boot injects properties during the initialization of the application context.
This happens (gets triggered) in the line:
appCtxt.run(args);
But you try to access the property before this line - that why it doesn't work.
So bottom line, using "#Value" in the main method doesn't work and it shouldn't.
Now from the code snippet, it looks like you could merely follow the "standards" of spring boot and create the file application.properties with:
server.port=1234
The process of starting the embedded web server in spring boot honors this property and bottom line it will have the same effect and Tomcat will be started on port 1234
Update 1
Based on OP's comment:
So, how can I have multiple application.properties.
In the Spring Boot's documentation it is written that application.properties are resolved from the classpath. So you can try the following assuming you have different modules A,B,C and web app D:
Create src/main/resources/application.properties inside each of 4 modules and pack everything together. The configuration values will be merged (hopefully they won't clash)
If you insist on naming properties A.properties, B.properties and C.properties for each of non-web modules, you can do the following (I'll show for module A, but B and C can do the same).
#Configuration
#PropertySource("classpath:A.properties")
public class AConfiguration {
}
Create in Module A: src/main/resources/A.properties
If you need to load the AConfiguration automatically - make the module A starter (using autoconfig feature of spring-boot):
Create src/resources/META-INF/spring.factories file with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
<package_of_AConfiguration>.AConfiguration
Also this has been the requirement to separate C from entire bundle where it might run as bundle for some and as a separate for some others
Although I haven't totally understood the requirement, but you can use #ConditionalOnProperty for configuration CConfiguration (that will be created just like AConfiguration.java in my previous example) but this times for module C.
If the conditional is met, configuration will run and load some beans / load its own properties or whatever. All in all conditionals (and in particular Profiles in spring) can help to reach the desired flexibility.
By default, the application.properties file can be used to store property pairs, though you can also define any number of additional property files.
If you save myApp.server.port=8020 in application.properties, it will work fine.
To register a custome property file, you can annotate a #Configuration class with the additional #PropertySource annotation:
#Configuration
#PropertySource("classpath:custom.properties")
#PropertySource("classpath:another.properties")
public class ConfigClass {
// Configuration
}
make sure, your class path is correct.

Spring: Configuration

In Spring Java configuration, suppose I want to re-use a #Bean in another #Bean definition. I can do this either in one file:
#Bean
public A buildA() {
return new A();
}
#Bean
public B buildB() {
return new B(buildA());
}
or I can configure A in one file and autowire it in another file like (field injection for brevity):
#Autowired
private A a;
#Bean
public B buildB() {
return new B(a);
}
I wonder, if the two possibilities are exactly the same? For me it looks as if, the first version might instatiate A twice, while the second doesn't.
I am asking this, since in my special use case, A is establishing a connection to a messaging broker and I have several Bs that consume the stream (I use .toReactivePublisher() from spring integration in A), and I don't want to connect twice or more to the broker.
Yes, they're exactly the same. Multiple calls to a #Bean annotated method will not create multiple instances of the same bean.
For an explanation on why it doesn't happen, please see this answer.

How do I point spring.freemarker.template-loader-path to the templates within a dependency jar?

I have 2 projects. One project (A) contains all my core functionality such as my entities/services/daos etc. It also contains many .ftl templates I wish to take advantage of and reuse in my other project (B). I have successfully gotten (B) to utilise the classes from (A) but I'm having no luck in reusing the freemarker templates.
Project (B) is a Spring Boot application (v2.1.3) and so I think I'm right in using the application.property: spring.freemarker.template-loader-path as opposed to defining a new #Bean.
Due to being a spring-boot application, by default and without this property the project will look in the projects own src/main/resources/templates location but my aim is for Project (A) to have none of its own templates and for my controller to return the templates found within Project (B).
Within my Maven Dependencies the hierachy is as thus:
projectA-0.0.1.jar
templates
folder1
exampleA.ftl
folder2
exampleB.ftl
Currently my controllers are set to return things like return new ModelAndView("folder1/exampleA") where the context prefix is src/main/resources/templates/
Does anyone know the correct format of the value I need to give to the spring.freemarker.template-loader-path property in order to point to the templates in my dependency jar instead of the local src/main/resources/templates?
So spring.freemarker.template-loader-path=classpath:/templates/ was the answer to my original question, however it didn't solve my problem.
Adding the following #Bean to my config class did, credit to #ddekany
#Bean
public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration() {
FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean();
bean.setTemplateLoaderPath("classpath:/templates/");
return bean;
}
Would appear that although I could use a property, due to other factors a #Bean was required in my scenario.
Setting into property:
spring.freemarker.cache=false
spring.freemarker.template-loader-path=file:src/main/resources/templates/
worked for me.
It didn't work for me.
But this approach does work:
#Bean
#Primary
public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration() throws IOException {
FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean();
ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), "/templates");
bean.setPostTemplateLoaders(ctl);
return bean;
}
I ended up with the following bean. If you are not using the spring-freemarker-starter maven dependency. I was using simpleJavamail so didn't need all the dependencies.
#Bean("freeMarkerConfiguration")
public freemarker.template.Configuration freeMarkerConfigurationFactoryBean() throws IOException {
freemarker.template.Configuration freeMarkerConfiguration =
new freemarker.template.Configuration(freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
freeMarkerConfiguration.setTemplateLoader(new ClassTemplateLoader(getClass(), "/templates"));
return freeMarkerConfiguration;
}

How do a I setup mongodb messagestore in aggregator using annotation

I am trying to add an aggregator to my code.
Couple of problems I am facing.
1. How do I setup a messagestore using annotations only.
2. Is there any design of aggregator works ? basically some picture explaining the same.
#MessageEndpoint
public class Aggregator {
#Aggregator(inputChannel = "abcCH",outputChannel = "reply",sendPartialResultsOnExpiry = "true")
public APayload aggregatingMethod(List<APayload> items) {
return items.get(0);
}
#ReleaseStrategy
public boolean canRelease(List<Message<?>> messages){
return messages.size()>2;
}
#CorrelationStrategy
public String correlateBy(Message<AbcPayload> message) {
return (String) message.getHeaders().get(RECEIVED_MESSAGE_KEY);
}
}
In the Reference Manual we have a note:
Annotation configuration (#Aggregator and others) for the Aggregator component covers only simple use cases, where most default options are sufficient. If you need more control over those options using Annotation configuration, consider using a #Bean definition for the AggregatingMessageHandler and mark its #Bean method with #ServiceActivator:
And a bit below:
Starting with the version 4.2 the AggregatorFactoryBean is available, to simplify Java configuration for the AggregatingMessageHandler.
So, actually you should configure AggregatorFactoryBean as a #Bean and with the #ServiceActivator(inputChannel = "abcCH",outputChannel = "reply").
Also consider to use Spring Integration Java DSL to simplify your life with the Java Configuration.

How do I add things to the /info endpoint in spring boot programmatically?

How do I add things to the /info endpoint in Spring Boot programmatically? The documentation states that this is possible for the /health endpoint through the use of HealthIndicator interface. Is there something for the /info endpoint as well?
I would like to add operating system name and version and other runtime info there.
In Spring Boot 1.4, you are able to declare InfoContributer beans to make this a whole lot easier:
#Component
public class ExampleInfoContributor implements InfoContributor {
#Override
public void contribute(Info.Builder builder) {
builder.withDetail("example",
Collections.singletonMap("key", "value"));
}
}
See http://docs.spring.io/spring-boot/docs/1.4.0.RELEASE/reference/htmlsingle/#production-ready-application-info-custom for more info.
The accepted answer actually clobbers the InfoEndpoint and does not add to it.
One way I found to add to the info is, in a #Configuration class, add an #Autowired method that adds extra properties following the info.* conventions to the environment. Then InfoEndpoint will pick them up when its invoked.
You can do something like the following:
#Autowired
public void setInfoProperties(ConfigurableEnvironment env) {
/* These properties will show up in the Spring Boot Actuator /info endpoint */
Properties props = new Properties();
props.put("info.timeZone", ZoneId.systemDefault().toString());
env.getPropertySources().addFirst(new PropertiesPropertySource("extra-info-props", props));
}
One way to do what you want (in the event that you have totally custom properties you need to display) is to declare a bean of type InfoEndpoint which will override the default.
#Bean
public InfoEndpoint infoEndpoint() {
final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.put("test", "value"); //put whatever other values you need here
return new InfoEndpoint(map);
}
As you can see from the code above, the map can contain whatever info you need.
In the event that the data you want to show can be retrieved by the environment and is not custom, you do not need to override the InfoEndpoint bean, but you can simply add properties to the properties file with a prefix of info. One example where the OS name is evaluated is the following:
info.os = ${os.name}
In the code above, Spring Boot will evaluate the right-hand expression before returning the property in the /info endpoint.
A final note is that there is a ton of environment information that is already available in the /env endpoint
Update
As pointed out by #shabinjo, in newer Spring Boot versions there is no InfoEndpoint constructor that accepts a map.
You can however use the following snippet:
#Bean
public InfoEndpoint infoEndpoint() {
final Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("test", "value"); //put whatever other values you need here
return new InfoEndpoint(new MapInfoContributor(map));
}
The code above will completely override the default info that would end-up in /info.
To overcome this issue one could add the following bean
#Bean
public MapInfoContributor mapInfoContributor() {
return new MapInfoContributor(new HashMap<String, Object>() {{
put("test", "value");
}});
}
It should be possible to add a custom PropertySource inside an ApplicationListener to add custom info.* properties to the environment (see this answer for an example: How to Override Spring-boot application.properties programmatically)

Resources