Spring configuration - java

Let's say we have a bean definition in spring configuration
<bean id="scanningIMAPClient" class="com.acme.email.incoming.ScanningIMAPClient" />
What I really want is the scanningIMAPClient to be of type com.acme.email.incoming.GenericIMAPClient if the configured email server is a normal IMAP server and com.acme.email.incoming.GmailIMAPClient incase it is a GMAIL server, (since gmail behaves in slightly different way) GmailIMAPClient is a subclass of GenericIMAPClient.
How can I accomplish that in spring configuration?
There is a properties file which contains configuration of the email server.

It's simple with Java configuration:
#Value("${serverAddress}")
private String serverAddress;
#Bean
public GenericIMAPClient scanningIMAPClient() {
if(serverAddress.equals("gmail.com"))
return new GmailIMAPClient();
else
return new GenericIMAPClient();
}
You can emulate this behaviour with custom FactoryBean.

You can use programatic configuration:
#Configuration
public class AppConfig {
#Bean(name="scanningIMAPClient")
public GenericIMAPClient helloWorld() {
...check config and return desired type
}
}
More info here.

Related

Java - Override #bean function to pick AWS Credentials from properties file

I already have a config file which is as below:
#Configuration
public class CloudConfig {
#Value("${aws.region}")
private String awsRegion;
#Value("${aws.accessKeyId}")
private String awsAccessKeyId;
#Value("${aws.awsSecretKeyId}")
private String awsSecretKeyId;
#Bean
public AmazonSNS amazonSnsClient() {
return AmazonSNSClientBuilder.standard().withRegion(awsRegion).withCredentials(AWSCredentials()).build();
}
#Bean
public AWSCredentialsProvider aWSCredentials() {
return new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsAccessKeyId, awsSecretKeyId));
}
}
I have an application properties file that has AWS properties of multiple environments in one file
#DEV
abc.env[0].aws.accessKeyId=xaw
abc.env[0].aws.awsSecretKeyId=yrt
#TEST
abc.env[1].aws.accessKeyId=abc
abc.env[1].aws.awsSecretKeyId=def
Based on some parameters I need to pick the AWS Credentials from a properties file
Is there any way I can override #bean values depending on some condition? ex. If x picks dev credentials if y picks test credentials in spring boot
Since I already have a config file, I'm not sure How can I override the #Bean in config so it can pick up the correct AWS credentials from the property file based on parameters.
Note: The requirement is that the properties of Dev and Test has to be in one file
Can someone please help?
Thank you for your time.

Spring Boot- set JmsTemplate Configuration Properties from Java class (not from application properties file)

Followed this tutorial https://developer.ibm.com/messaging/2018/04/03/mq-jms-spring-boot/ and developed a Spring Boot JMS applicatin which sends a message to IBM MQ. (used this dependency - mq-jms-spring-boot-starter).
As per the tutorial, the configuration properties (Queue Manager, Channel, Port etc) can be given in application.yml/ application.properties file as below, and JmsTemplate will automatically be configured with the properties.
ibm.mq.queueManager=QM1
ibm.mq.channel=SYSTEM.DEF.SVRCONN
ibm.mq.connName=server.example.com(1414)
ibm.mq.user=user1
ibm.mq.password=passw0rd
The application works perfect and it sends message to the MQ now this way.
But I want to set the properties inside the class, not from the properties file (reading from a database or something). How to set these values inside the class?
You can use a customizer method on the CF after the initial properties have been populated.
In the Application class, this code allows additional properties to be configured:
#Bean
public MQConnectionFactoryCustomizer myCustomizer() {
MQConnectionFactoryCustomizer c = new MQConnectionFactoryCustomizer() {
#Override
public void customize(MQConnectionFactory factory) {
factory.setXXXX(property, value);
}
};
return c;
}

SpEL not supported in Spring annotation #Entry.base

I use Spring Data LDAP and Spring Boot provides out of the box support for an embedded UnboundID server. However, when I use Spring Data LDAP's #Entry annotation, I need to specify a different base in the annotation based on whether I'm using the embedded UnboundID LDAP server, or a remote Active Directory server.
I was attempting to do this with SpEL and profile-based properties by specifying:
#Entry(base = "${ldap.person.base}", ...)
Then I have an application.propreties with ldap.person.base=OU=AD Person Base and an application-embedded.properties with ldap.person.base=OU=Embedded Person Base.
However, the #Entry annotation does not seem to support SpEL evaluation:
javax.naming.InvalidNameException: Invalid name: ${ldap.person.base}
There is an open issue in Spring LDAP to add support for this, but is there any workaround or some other way I can accomplish this until it is supported in Spring LDAP?
I'm not sure I'm following here, but assuming you're using the LDAP auto-configuration in Spring Boot, is it not enough to set the property spring.ldap.base to one or the other (OU=AD Person Base or OU=Embedded Person Base) based on the profile you're using?
Both EmbeddedLdapAutoConfiguration and LdapAutoConfiguration use an LdapProperties object to set various attributes on the LdapContextSource during bean creation, including its base. As far as I can tell, you won't have to define it for each #Entry in your codebase if LdapContextSource.base is set.
If you're not using the auto-configuration, and if I'm correct in my assumptions, you should still be able to create your own LdapContextSource bean and set its base to the desired value based on a Spring property.
Turns out the reason I needed a different base in the first place is because Spring was not setting the base on the ContextSource.
When you let Spring Boot autoconfigure the embedded LDAP server, it creates a ContextSource as such in EmbeddedLdapAutoConfiguration:
#Bean
#DependsOn("directoryServer")
#ConditionalOnMissingBean
public ContextSource ldapContextSource() {
LdapContextSource source = new LdapContextSource();
if (hasCredentials(this.embeddedProperties.getCredential())) {
source.setUserDn(this.embeddedProperties.getCredential().getUsername());
source.setPassword(this.embeddedProperties.getCredential().getPassword());
}
source.setUrls(this.properties.determineUrls(this.environment));
return source;
}
As you can see, nowhere in there does it call source.setBase(). So to solve this, I added a configuration file with #Profile("embedded") and manually created a ContextSource where I set the base myself (I leave off the credentials part because I don't use credentials for the embedded server):
#Configuration
#Profile("embedded")
#EnableConfigurationProperties({ LdapProperties.class })
public class EmbeddedLdapConfig {
private final Environment environment;
private final LdapProperties properties;
public EmbeddedLdapConfig(final Environment environment, final LdapProperties properties) {
this.environment = environment;
this.properties = properties;
}
#Bean
#DependsOn("directoryServer")
public ContextSource ldapContextSource() {
final LdapContextSource source = new LdapContextSource();
source.setUrls(this.properties.determineUrls(this.environment));
source.setBase(this.properties.getBase());
return source;
}
}
Now, I can leave the value of the base attribute in my #Entry the same for both the Active Directory server and the embedded UnboundID server and it works properly.

spring boot request endpoints return 404 [duplicate]

The application uses JDK 8, Spring Boot & Spring Boot Jersey starter and is packaged as a WAR (although it is locally run via Spring Boot Maven plugin).
What I would like to do is to get the documentation I generate on the fly (at build time) as a welcome page.
I tried several approaches:
letting Jersey serving the static contents by configuring in application.properties the proper init parameter as described here
introduce a metadata-complete=false web.xml in order to list the generated HTML document as a welcome-file.
None of that worked out.
I would like to avoid having to enable Spring MVC or creating a Jersey resource just for serving a static file.
Any idea?
Here is the Jersey configuration class (I unsuccessfully tried to add a ServletProperties.FILTER_STATIC_CONTENT_REGEX there):
#ApplicationPath("/")
#ExposedApplication
#Component
public class ResourceConfiguration extends ResourceConfig {
public ResourceConfiguration() {
packages("xxx.api");
packages("xxx.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
And here is Spring Boot application class (I tried adding an application.properties with spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=/.*html but it didn't work, I'm not exactly sure what the property key should be here):
#SpringBootApplication
#ComponentScan
#Import(DataConfiguration.class)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Let me just first state, that the reason the static content won't be served is because of the default servlet mapping of the Jersey servlet, which is /*, and hogs up all the requests. So the default servlet that serves the static content can't be reached. Beside the below solution, the other solution is to simply change the servlet mapping. You can do that by either annotating your ResourceConfig subclass with #ApplicationPath("/another-mapping") or set the application.properties property spring.jersey.applicationPath.
In regards to your first approach, take a look at the Jersey ServletProperties. The property you are trying to configure is FILTER_STATIC_CONTENT_REGEX. It states:
The property is only applicable when Jersey servlet container is configured to run as a Filter, otherwise this property will be ignored
Spring Boot by default configures the Jersey servlet container as a Servlet (as mentioned here):
By default Jersey will be set up as a Servlet in a #Bean of type ServletRegistrationBean named jerseyServletRegistration. You can disable or override that bean by creating one of your own with the same name. You can also use a Filter instead of a Servlet by setting spring.jersey.type=filter (in which case the #Bean to replace or override is jerseyFilterRegistration).
So just set the property spring.jersey.type=filter in your application.properties, and it should work. I've tested this.
And FYI, whether configured as Servlet Filter or a Servlet, as far as Jersey is concerned, the functionality is the same.
As an aside, rather then using the FILTER_STATIC_CONTENT_REGEX, where you need to set up some complex regex to handle all static files, you can use the FILTER_FORWARD_ON_404. This is actually what I used to test. I just set it up in my ResourceConfig
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("...");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
For anyone who still can't get this to work, I followed the answer provided by #peeskillet, and had to make an additional change.
Previously I had created the following method in Application.java.
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
return registration;
}
The problem is that this registered the servlet for the /* path, and then setup the Jersey ResourceConfig configuration file.
Once I removed the above method, and placed the #Configuration annotation on my ResourceConfig class, I noticed the static resource could be retrieved via Spring Boot.
For completeness, this is a snippet of my ResourceConfig now.
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// Application specific settings
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
This blog post was helpful in determining the difference approach for the ResourceConfig.
Below setup worked for me
Set
spring .jersey.type: filter
set FILTER_FORWARD_ON_404
#Configuration
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig () {
try {
register(XXX.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
} catch (Exception e) {
LOGGER.error("Exception: ", e);
}
}
}
Note: Use #Configuration instead of #component

Spring Looking at Default Value Before Resolving Placeholder

I've been searching forever for an issue similar to mine, but wasn't able to find one. Thus, I hope it's not a duplicate post.
Well,
I'm using spring integration to search for documents in my mongodb. Once it finds one, it sends the payload to another method.
I have a property file that I want to be resolved on this find and send configuration, so I use placeholders instead of static values.
Here's my xml:
<int:inbound-channel-adapter id="sendClientMailInboundAdapter"
expression="#repository.findClient()"
channel="sendClientMailChannel"
auto-startup="${send.mail.active:false}">
<int:poller fixed-rate="${send.mail.poller.time:60}" time-unit="SECONDS" max-messages-per-poll="1" />
</int:inbound-channel-adapter>
<int:channel id="sendClientMailChannel" />
<int:service-activator input-channel="sendClientMailChannel" expression="#service.sendClientMail(payload)" />
Right.
Now.. I've got an AppConfig class which loads the property file.
#Configuration
#PropertySource("file://${appconfig.root}/appconfig.properties")
public class AppConfig {
public static Environment env;
...
#Bean
public static PropertySourcesPlaceholderConfigurer appConfigConfigurer(Environment env) {
AppConfig.env = env;
PropertySourcesPlaceholderConfigurer appConfigConfigurer = new PropertySourcesPlaceholderConfigurer();
appConfigConfigurer.setIgnoreUnresolvablePlaceholders(true);
appConfigConfigurer.setIgnoreResourceNotFound(true);
return appConfigConfigurer;
}
}
So.. My problem is the default value.
If I do NOT specify the default value, spring resolves the placeholder. If it's specified, though, it seems spring ignores the placeholder (maybe it doesn't wait for it to resolve) and set the default value instead!
I could use context:property-placeholder location. I've done that and it worked, but since I'm already loading the file on my configuration class, I'd rather have spring read properties from there so I would not have to remember to adjust two files in case property file changes its folder, for instance.
My question: Is there a way to tell spring to wait for the placeholder to resolve before looking at the default value?
edit: Just so people know.. I changed xml to java config, using placeholder / default value like before and it worked perfectly.
Here is the equivalent snippet:
#InboundChannelAdapter(value = "sendClientMailChannel", autoStartup="${send.mail.active:false}",
poller = #Poller(fixedRate="${send.mail.poller.time.in.millis:60000}", maxMessagesPerPoll="1"))
public Client findClient() {
return repository.findClient();
}
#ServiceActivator(inputChannel="sendClientMailChannel")
public void sendToClient(Client payload) {
service.sendClientMail(payload);
}
#Bean
public DirectChannel sendClientMailChannel() {
return new DirectChannel();
}
note: I didn't have to ref the property file.

Categories

Resources