Spring Boot (XML Configuration) & Jasypt Integration - java

My application is merely to startup an ActiveMQ broker.
I want to use XML-based configuration in Spring Boot, to make use of the XML configuration for ActiveMQ broker (referenced here).
I'm using jasypt-spring-boot-starter for my encryption needs, but it seems that the encrypted values for my passwords are not being decrypted when the XML configuration is being initialised.
No errors during startup. Just that when I try to access the broker using admin/user it will fail with error "User name [user] or password is invalid."
Main Spring Boot App Class
#Configuration
#ComponentScan
#EnableAutoConfiguration
#SpringBootApplication
#RestController
#ImportResource({"classpath:activemq.xml"})
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Excerpt from Broker Config (activemq.xml)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd">
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="${activemq.broker.name}" dataDirectory="${activemq.broker.data}">
<plugins>
<runtimeConfigurationPlugin checkPeriod="1000" />
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="admin" password="${activemq.broker.admin.password}" groups="users,admins" />
<authenticationUser username="user" password="${activemq.broker.user.password}" groups="users" />
<authenticationUser username="guest" password="${activemq.broker.guest.password}" groups="guests" />
</users>
</simpleAuthenticationPlugin>
</plugins>
...more
application.properties
jasypt.encryptor.password=thisisnotapassword
jasypt.encryptor.algorithm=PBEWITHMD5ANDTRIPLEDES
activemq.broker.admin.password=ENC(OZRghRNXYpRiiw18KD7P6Uf2Y7fOieI7)
activemq.broker.user.password=ENC(yOiHeJlh6Z+VRVmSZe//Yw==)
activemq.broker.guest.password=guest
One thing I noticed from the startup logs is that activemq.xml gets loaded before jasypt related logs appear
Loading XML bean definitions from class path resource [activemq.xml]
...some logs
String Encryptor custom Bean not found with name 'jasyptStringEncryptor'. Initializing Default String Encryptor

This can be solved by using a custom environment, as described in https://github.com/ulisesbocchio/jasypt-spring-boot:
new SpringApplicationBuilder()
.environment(new StandardEncryptableEnvironment())
.sources(Application.class).run(args);
From the README.md:
This method is useful for early access of encrypted properties on
bootstrap. While not required in most scenarios could be useful when
customizing Spring Boot's init behavior or integrating with certain
capabilities that are configured very early, such as Logging
configuration.

Related

How can I pass spring application.yaml file to spring context created in Java project using spring boot project as dependency?

I am using spring boot project as a dependency in another non spring java project. The problem is I am getting error when spring processes #Value() annotation.
According to this article
In order to get instances of the beans managed in spring I created ClassPathXmlApplicationContext and created applicationcontext.xml,
but I am getting error from #Value("${proper}") anotation that
the resource cannot be resolved to URL because it does not exist
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring-application.xml"});
context.getBean("mybean");
applicationcontext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.example" />
</beans>
The application.yaml is located outside the project in /opt/myapp/config/application.yaml and contains:
proper: file:/path/to/some/file/that/exists
I am expecting that spring will initialize the value of proper in component using the default application.yaml. since I can run spring boot project stand alone and it works fine.
Any Ideas?

Camel Spring JavaConfig Maven-Camel-Plugin without any xml

How can I setup my camel projects to completely rely on the Spring Dependency injection system while being xml free using the maven camel run plugin. I have tried a ton of configurations, but I still seem to be stuck with a "shell context" file that just imports my java configuration file. Is there anyway to get rid of this? Note: also on latest camel version of 2.17.1
Camel Route
#Component
public class TestRoute extends SpringRouteBuilder {
#Override
public void configure() throws Exception {
from("timer://foo?repeatCount=1")
.log("Hello World");
}
}
Java Config
#Configuration
#ComponentScan("com.mcf.xml.free.route")
public class RouteJavaConfig extends CamelConfiguration {
}
Maven Camel Plugin
<plugin>
<groupId>org.apache.camel</groupId>
<artifactId>camel-maven-plugin</artifactId>
<version>${camel.version}</version>
<configuration>
<configClasses>com.mcf.xml.free.config.RouteJavaConfig</configClasses>
</configuration>
</plugin>
Plugin Context that lets it all work that I want to get rid of.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="com.mcf.xml.free.config.RouteJavaConfig"/>
</beans>
Is there some way to have the maven camel run plugin accept the Java Config instead of a Spring Context file? Also I haven't tested this yet, but If removing this file is going to cause an issue deploying to Apache Karaf is there a way to have it configured to use my Java Config as well?
You can start your Spring Java Config application using a main class you create yourself, and then use the java-maven-exec plugin to run it. This example has no XML file at all.
There is an example as part of Apache Camel at: https://github.com/apache/camel/tree/master/examples/camel-example-spring-javaconfig
I think your scenario is a great fit for using spring boot with camel. Spring quotes:
Absolutely no code generation and no requirement for XML configuration
on their page. Spring-Boot
Here is one Spring boot-camel example you can have a look at to check: Camel-Spring Boot example

Spring Framework Unable to Resolve References in IntelliJ

I set up a application configuration file for spring, added it to the facets, and set it up according to another configuration file that works perfectly.
All of the references to the spring components are visible and seem to work, but all references to items within the xml file fail to be found.
An example is with the tasks:
<task:scheduler id="taskScheduler" />
<task:executor id="taskExecutor" pool-size="1" />
<task:annotation-driven executor="taskExecutor" scheduler="taskScheduler" />
Both taskScheduler and taskExecutor cannot be resolved. As a result, the task bean never gets set up so all of my #Scheduled annotations never work.
Setting up the factory provider for services works just fine (all #Service and #Autowired annotations work), so I am completely certain the issue is with some configuration issue in the project.
What else needs to be configured in IntelliJ for the beans to work? Why does spring not rescan the file to find references to beans?
Here is where all of the springframework beans are referenced:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd"
default-lazy-init="true">
The issue is having multiple contexts for application configuration files. To fix it, go to the configuration file in IntelliJ, then there is a section at the top where you can select which application context you want to put the context file into (Reads something like "Spring Application Context in module [your module]. File is included in [n] contexts"). Try selecting each one until the references resolve; it will remove it from the other contexts. My particular issue was that it was in the MVC context and it needed to be in the Spring Application Context.

What happens behind the scenes when a property placeholder is defined in a Spring context (JNDI-wise)?

I have a Java web application using Spring and JSF, deployed in Tomcat.
For the record, in web.xml, only the Faces servlet is configured; there is no Spring servlet.
The entry point to my Spring context is applicationContext.xml, which looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<context:component-scan base-package="my.pack" annotation-config="true" />
<context:property-placeholder location="file:${admin.config.path}/database.properties"/>
<context:spring-configured />
<context:annotation-config />
<aop:aspectj-autoproxy />
</beans>
In the property-placeholder definition one can see that I'm injecting an external property, which, in this case, is an environment variable. This property is passed via Tomcat context configuration, like this:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/cntxt">
<Environment name="admin.config.path" value="d:/conf/"
type="java.lang.String" override="false"/>
</Context>
I'm also injecting this environment variable in one of my Spring-managed beans, like this:
#Value("${java:comp/env/admin.config.path}")
private String confDir;
(This is because I'm also using that directory for some other configuration files, which are not .properties files).
With the configuration above, everything works just fine - all the wirings are performed without any flaw.
But, at some moment, I stopped needing injecting properties via the property placeholder configurer from database.properties (but I still need the other files from the admin.config.path), therefore I removed the <context:property-placeholder> line from applicationContext.xml.
At that moment, the injection using #Value stopped working in my Spring-managed bean.
Does anybody know what actually happened? It seems like the JNDI injection fails if the property placeholder configurer is not defined in the Spring context.
Is there any way I can re-enable this kind of injection (without defining a dummy property placeholder pointing to an empty properties file)?
You need a property placeholder configurer instance, the sole purpose of it is to replace placeholders with actual values. If you remove it no placeholders will be replaced.
Either add a <context:property-placeholder /> and leave out the location field or add a bean of the type PropertySourcesPlaceholderConfigurer. Either way will re-enable your placeholder support.
Small suggestion remove the java:comp/env part of the property, by default it will do a JNDI lookup if the property cannot be resolved in other locations (servlet context, properties, system properties etc.).

Tomcat vs Weblogic JNDI Lookup

The Weblogic servers we are using have been configured to allow JNDI datasource names like "appds".
For development (localhost), we might be running Tomcat and when declared in the <context> section of server.xml, Tomcat will hang JNDI datasources on "java:comp/env/jdbc/*" in the JNDI tree.
Problem: in Weblogic, the JNDI lookup is "appds" whilst in Tomcat, it seems that that I must provide the formal "java:comp/env/jdbc/appds". I'm afraid the Tomcat version is an implicit standard but unfortunately, I can't change Weblogic's config ... so that means we end up with two different spring config files (we're using spring 2.5) to facilitate the different environments.
Is there an elegant way to address this. Can I look JNDI names up directly in Tomcat? Can Spring take a name and look in both places? Google searches or suggestions would be great.
How to use a single JNDI name in your web app
I've struggled with this for a few months myself. The best solution is to make your application portable so you have the same JNDI name in both Tomcat and Weblogic.
In order to do that, you change your web.xml and spring-beans.xml to point to a single jndi name, and provide a mapping to each vendor specific jndi name.
I've placed each file below.
You need:
A <resource-ref /> entry in web.xml for your app to use a single name
A file WEB-INF/weblogic.xml to map your jndi name to the resource managed by WebLogic
A file META-INF/context.xml to map your jndi name to the resource managed by Tomcat
This can be either in the Tomcat installation or in your app.
As a general rule, prefer to have your jndi names in your app like jdbc/MyDataSource and jms/ConnFactory and avoid prefixing them with java:comp/env/.
Also, data sources and connection factories are best managed by the container and used with JNDI. It's a common mistake to instantiate database connection pools in your application.
spring
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">
<jee:jndi-lookup jndi-name="jdbc/appds"
id="dataSource" />
</beans>
web.xml
<resource-ref>
<description>My data source</description>
<res-ref-name>jdbc/appds</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
weblogic.xml
<?xml version="1.0" encoding="UTF-8" ?>
<weblogic-web-app
xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://xmlns.oracle.com/weblogic/weblogic-web-app http://http://www.oracle.com/technology/weblogic/weblogic-web-app/1.1/weblogic-web-app.xsd">
<resource-description>
<jndi-name>appds</jndi-name>
<res-ref-name>jdbc/appds</res-ref-name>
</resource-description>
</weblogic-web-app>
META-INF/context.xml (for Tomcat)
<Context>
<ResourceLink global="jdbc/appds" name="jdbc/appds" type="javax.sql.DataSource"/>
</Context>
JndiLocatorSupport has a property resourceRef. When setting this true, "java:comp/env/" prefix will be prepended automatically. So I believe it would be correct to differentiate this parameter when moving from Tomcat to Weblogic.
I've managed the trick with Tomcat and WebLogic using Spring. Here is a description of how it worked for me.
The following config works in Tomcat and Weblogic for me.
In Spring:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<!-- This will prepend 'java:comp/env/' for Tomcat, but still fall back to the short name for Weblogic -->
<property name="resourceRef" value="true" />
<property name="jndiName" value="jdbc/AgriShare" />
</bean>
In Weblogic Admin Console create a JDBC resource named jdbc/AgriShare. Under 'Targets', MAKE SURE YOU TARGET THE DATASOURCE TO THE SERVER YOU ARE DEPLOYING YOUR APP TO!. This particular point cost me some time just now...
How about an evironment variable? Set developers machines with the tomcat name and production with the Weblogic name. You can even set your code to use a default one (WebLogic) in case the variable don't exist.
How are you referencing the resource in spring?
This is what we have for tomcat:
context:
<Resource name="jms/ConnectionFactory" auth="Container" type="org.apache.activemq.ActiveMQConnectionFactory" description="
JMS Connection Factory"
factory="org.apache.activemq.jndi.JNDIReferenceFactory" brokerURL="tcp://localhost:61615" brokerName="StandaloneAc
tiveMQBroker"/>
spring:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<jee:jndi-lookup jndi-name="jms/ConnectionFactory" id="connectionFactory" resource-ref="true"
expected-type="javax.jms.ConnectionFactory" lookup-on-startup="false"/>
The jee namespace comes from:
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd
Setting up DataSource in the application itself is not that crazy :) I would say that is even mandatory if application is meant to be deployed on a grid. River, GigaSpaces, or similar.
Note: I do not say connection settings have to be hardcoded inside of WAR, they need to be supplied at deployment time/runtime. This simplifies management of cloud instances since there is only on place to configure.
Configuring resources at the container makes sense only if multiple applications are deployed there and they can use shared resource.
Again, in cloud type of deployments there is only one application per servlet container instance.
My application also had a similar problem and this is how I solved it:
1) WEB-INF/classes/application.properties contains the entry:
ds.jndi=java:comp/env/jdbc/tcds
2) On the WLS machine, I have an entry in the /etc/sysenv file:
ds.jndi=wlsds
3) I configured spring to lookup the JNDI vis the property ${ds.jndi}, using a PropertyPlaceholderConfigurer bean with classpath:application.properties and file:/etc/sysenv as locations. I also set the ignoreResourceNotFound to true so that developers need not have /etc/sysenv on their machines.
4) I run an integration test using Cargo+Jetty and I could not properly set up a JNDI environment there. So I have a fallback BasicDataSource configured too using the defaultObject property of JndiObjectFactoryBean.

Categories

Resources