Maven Exposing properties files appassembler - spring

To load environment specific values, in my src/main/resources folder, I have some properties files in different subfolders i.e.
com/app/ws/webservices-dev.properties
com/app/ws/webservices-test.properties
com/app/jms/jms-dev.properties
com/app/jms/jms-test.properties
I am loading these properties through spring
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/com/app/jms/jms-${ENVIRONMENT:dev}.properties</value>
<value>classpath:/com/app/ws/webservices-${ENVIRONMENT:dev}.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="order" value="0" />
</bean>
ENVIRONMENT is environment variable.
I am using appassembler-maven-plugin to generate the executable .sh file.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.2.2</version>
<configuration>
<binFileExtensions>
<unix>.sh</unix>
</binFileExtensions>
<programs>
<program>
<mainClass>com.app.MainApp</mainClass>
<name>MainApp</name>
</program>
</programs>
</configuration>
</plugin>
As a result of this all my properties files become part of my generated jar file. I want to expose some of the properties to set their values at deployment time. I have tried following configuration
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.2.2</version>
<configuration>
<configurationSourceDirectory>src/main/resources/com/app/bootstrap</configurationSourceDirectory>
<configurationDirectory>conf</configurationDirectory>
<copyConfigurationDirectory>true</copyConfigurationDirectory>
<includeConfigurationDirectoryInClasspath>true</includeConfigurationDirectoryInClasspath>
<binFileExtensions>
<unix>.sh</unix>
</binFileExtensions>
<programs>
<program>
<mainClass>com.app.MainApp</mainClass>
<name>MainApp</name>
</program>
</programs>
</configuration>
</plugin>
But Spring does not load the properties; maybe due to the given path in spring config (see above).
What maven configuration should I use to move my properties files in appassembler\conf folder during package time and have spring load them from the classpath. I am after the configuration that works for both development(in eclipse, unit tests as maven builds) and in deployment.

Related

How to passing Maven Profiles value to Spring Bean XML

I'm new in Spring & Maven, I think my question is simple. But I cannot to figure and setup it. I have Maven POM like below:
<profiles>
<profile>
<id>qa</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<db.driverClassName>oracle.jdbc.driver.OracleDriver</db.driverClassName>
<db.url>jdbc:oracle:thin:#10.148.36.89:1521:mmki</db.url>
<db.username>APW</db.username>
<db.password>apw</db.password>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<db.driverClassName>oracle.jdbc.driver.OracleDriver</db.driverClassName>
<db.url>jdbc:oracle:thin:#10.148.36.88:1521:mmki</db.url>
<db.username>APW</db.username>
<db.password>apw</db.password>
</properties>
</profile>
</profiles>
My question is how I can passing the value from Maven profile to Spring bean property like below:
<!-- QA ENVIRONMENT -->
<!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#10.148.36.89:1521:mmki" />
<property name="username" value="APW" />
<property name="password" value="apw" />
</bean> -->
<!-- PRD ENVIRONMENT -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#10.148.36.88:1521:mmki" />
<property name="username" value="APW" />
<property name="password" value="apw" />
</bean>
I'm so dumb for this question, but please everyone to answer and explain with the simple method.
Many thanks.
First:
Do not write user and pass at pom.xml file
Second:
You can have one applicationContext for each environment like applicationContext-prod.xml and applicationContext-qa.xml
On your pom.xml you can have one property for each profile like:
pom.xml
<profile>
<id>qa</id>
<properties>
<appContext>classpath:applicationContext-qa.xml</appContext>
...
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<appContext>classpath:applicationContext-prod.xml</appContext>
...
</properties>
</profile>
In web.xml you can write you context param like:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>${appContext}</param-value>
</context-param>
If you write your passwords in the applicationContext files you do not need anything else. But, for security reasons I recommend you as best practice write your sensible environment values at external properties file:
You can have multiple *.properties on /etc/app
(production.properties and qa.properties)
And finally, on your applicationContext-prod.xml you can have your propertyConfigurer like:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>file:/etc/app/production.properties</value>
</property>
</bean>
....
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
....
<property name="password">
<value>${db.password}</value>
And on your applicationContext-qa.xml you can have your propertyConfigurer like:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>file:/etc/app/qa.properties</value>
</property>
</bean>
...
Obviously on your /etc/app/production.properties you have to write:
db.password=prodpass
Finally, on your /etc/app/qa.properties you have to write:
db.password=qapass

Reading properties from a file to a spring context in a Confluence plugin

I'm trying to read in a property file to a spring context in a confluence plugin. I've added to META-INF/spring the following context file:
<?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:atlassian-spring="http://www.atlassian.com/schema/atlassian-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.atlassian.com/schema/atlassian-spring http://www.atlassian.com/schema/atlassian-spring/atlassian.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="db.properties"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="url" value="${db.url}" />
<property name="driverClassName" value="${db.driver}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.password}" />
</bean></beans>
Also to the pom.xml I've added the following dependency:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>2.5.6.SEC02</version>
<scope>provided</scope>
</dependency>
Unfortunately I keep getting the ClassNotFoundException for PropertyPlaceholderConfigurer.
As far as I can tell this is an OSGi problem - because the class isn't referenced from code it is not added by OSGi.
Here: https://developer.atlassian.com/display/DOCS/ClassNotFoundException I have found that I should add Import-Package to the spring beans package in the atlassian-plugin.xml but adding this:
<bundle-instructions>
<Import-Package>org.springframework.beans*</Import-Package>
</bundle-instructions>
Does not help.
Any suggestions?
The spring version which mentioned above has OSGI bundles? Can you confirm? Because for some spring versions does not have OSGI bundles. In that case, you need convert to bundles yourself. If its not a OSGI bundle. then package wont be exposed and class wont be able to find , hence your getting above error.
Some details about spring OSGI bundles.
http://ebr.springsource.com/repository/app/
http://www.srikanthugar.in/2014/04/sspring-framework-is-no-longer.html
http://www.srikanthugar.in/2014/04/how-to-convert-spring-beans-artifact-to.html
I found the answer to my question. In order to configure OSGI in confluence one should configure the maven plugin:
<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>maven-***-plugin</artifactId> <!-- *** is the name of the application (product) you're using -->
<version>3.2.3</version>
<extensions>true</extensions>
<configuration>
<productVersion>${product.version}</productVersion>
<instructions>
<!-- OSGi instructions go here -->
</instructions>
</configuration>
</plugin>
and put the Import-Package declarations there.

Integration tests using jetty, hsql, jndi, and spring

I am attempting to create some integration tests for my Spring web app using Jetty accessing a local HSQL database. The goal: run the tests using Selenium (or similar), mock/stub out all external systems, and setup a HSQL database to hit instead of our shared Oracle database. The tests are started during a maven build (the integration-test phase).
The database is initialized by Spring's "jdbc:initialize-database", and is registered as a JNDI datasource in Jetty.
After days of trying different configuration, I have finally gotten to the point where the database is created, initialized, and I think registered as a Jetty resource, but when the test cases run, it just hangs; I think because it is waiting for the database to become available.
Maven configuration
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.3.3</version>
<executions>
<execution>
<id>start</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
<configuration>
<container>
<containerId>jetty7x</containerId>
<dependencies>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
</dependency>
</dependencies>
</container>
<configuration>
<home>${project.build.directory}/cargo/configurations/jetty7x</home>
<properties>
<cargo.jetty.createContextXml>false</cargo.jetty.createContextXml>
<cargo.datasource.datasource>
cargo.datasource.url=jdbc:hsqldb:file:../../../myDB|
cargo.datasource.driver=org.hsqldb.jdbcDriver|
cargo.datasource.username=sa|
cargo.datasource.password=|
cargo.datasource.type=javax.sql.DataSource|
cargo.datasource.jndi=jdbc/myDataSource
</cargo.datasource.datasource>
</properties>
</configuration>
<deployables>
<deployable>
<location>target/myApp</location>
<properties>
<context>myApp</context>
</properties>
</deployable>
</deployables>
</configuration>
</plugin>
Spring configuration
<bean id="localDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.hsqldb.jdbcDriver"/>
<property name="jdbcUrl" value="jdbc:hsqldb:file:target/myDB"/>
<property name="user" value="sa"/>
<property name="password" value=""/>
</bean>
<jdbc:initialize-database data-source="mydataSource" ignore-failures="DROPS">
<jdbc:script location="classpath:/sql-scripts/schema/create-schema.sql"/>
<jdbc:script location="classpath:/sql-scripts/schema/create-tables.sql"/>
<jdbc:script location="classpath:/sql-scripts/testdata/data-load.sql"/>
</jdbc:initialize-database>
I am probably missing something, I tried to piece together the configuration through advice from many other posts. Any help would be appreciated.
The recommended method of using HSQLDB for tests, especially complex test setups, is running a Server.
Initially, you start an HSQLDB server using the shell, independently of your test setup. Use the Server property server.silent=false to see immediately the connections and statements on the console.
After some progress has been made, you can customize the server settings. See the Guide:
http://www.hsqldb.org/doc/2.0/guide/listeners-chapt.html
And a summary of different options for testing:
http://www.hsqldb.org/doc/2.0/guide/deployment-chapt.html#dec_app_dev_testing
You may need to use the MVCC transaction model. This reduces the locks and sometimes avoids the connections hanging as a result of on one waiting for the other to commit.

Starting jetty with spring xml as a background process/thread

My goal is to set up a jetty test server and inject a custom servlet to test some REST classes in my project. I was able to launch the server with spring xml and run tests against that server. The issue I'm having is sometimes after the server started, the process stopped at the point before running the tests. It seems jetty didn't go to background. It works every time on my computer. But when I deployed to my CI server, it doesn't work. It also doesn't work when I'm on VPN. (Strange.)
The server should be completed initialized as when the tests stuck, I was able to access the server using a browser.
Here is my spring context xml:
....
<bean id="servletHolder" class="org.eclipse.jetty.servlet.ServletHolder">
<constructor-arg ref="courseApiServlet"/>
</bean>
<bean id="servletHandler" class="org.eclipse.jetty.servlet.ServletContextHandler"/>
<!-- Adding the servlet holders to the handlers -->
<bean id="servletHandlerSetter" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="servletHandler"/>
<property name="targetMethod" value="addServlet"/>
<property name="arguments">
<list>
<ref bean="servletHolder"/>
<value>/*</value>
</list>
</property>
</bean>
<bean id="httpTestServer" class="org.eclipse.jetty.server.Server" init-method="start" destroy-method="stop" depends-on="servletHandlerSetter">
<property name="connectors">
<list>
<bean class="org.eclipse.jetty.server.nio.SelectChannelConnector">
<property name="port" value="#{settings['webservice.server.port']}" />
</bean>
</list>
</property>
<property name="handler">
<ref bean="servletHandler" />
</property>
</bean>
Running latest Jetty 8.1.8 server and Spring 3.1.3. Any idea?
I figured it out. My bad. The IP address for the testing web server (jetty) that my REST client connect to is set to a internal IP address (not localhost) that only available to my local host. So that why the tests couldn't start when I'm on VPN or on CI server.
The xml is actually working and I think it is better than starting jetty in separate ant task. Because spring manage the jetty lifecycle. When the tests finish, spring will shutdown jetty automatically.
If you are using Maven, you can let the jetty-maven-plugin start and stop Jetty as part of your build process. You create your Spring project as usual, add the plugin to your .pom file. mvn jetty:run allows you to run the web app unassembled, and mvn jetty:run-war allows you to run a .war file. I guess that what you really want is to have Jetty started in the pre-integration test phase and stopped in the post integration test phase (ref):
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<stopKey>foo</stopKey>
<stopPort>9999</stopPort>
</configuration>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<scanIntervalSeconds>0</scanIntervalSeconds>
<daemon>true</daemon>
</configuration>
</execution>
<execution>
<id>stop-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>

Load different jndi.properties file in spring application context depending on profile

I have a jms connection settings that is defined in jndi.properties file on my classpath which I used to connect to ActiveMQ in my local development environment. I would like to rename this file to "activemq.jndi.properties" as I am planning to have another jms connection settings to WebsphereMQ ( say webshperemq.jndi.properties ). However I have no success so far in telling spring in my applicationContext.xml to look at activemq.jndi.properties.
Here is a snippet of my applicationContext.xml which works for jndi.properties
<!-- Define how to connect to the Message Queueing system -->
<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="${jms.connectionFactory}" />
<property name="resourceRef" value="true" />
</bean>
<bean id="defaultDestination" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="${jms.topic}" />
<property name="resourceRef" value="true" />
</bean>
<!-- Define a connection template that links to the factory -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultDestination" ref="defaultDestination" />
<property name="receiveTimeout" value="6000" />
</bean>
Both ${jms.connectionFactory} and ${jms.topic} are being filtered from maven. Any input on what needs to be changed in my applicationContext.xml to make it load from activemq.jndi.properties would be much appreciated.
Thanks!
Well, Ithink you should configure maven resources so use one of another configuration file depending of the profile instead change anything in your Spring configuration file.
For example:
<profiles>
<profile>
<id>local</id>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>jndi.properties</exclude>
</excludes>
</resource>
</resources>
</build>
</profile>
<profile>
<id>webshpere</id>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>webshperemq.jndi.properties</exclude>
</excludes>
</resource>
</resources>
</build>
</profile>
</profiles>

Resources