Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controlling a project with Maven and Spring: How to set Spring config file using Maven profiles?

I am trying to configure a Spring configuration file with database information based on whether a certain Maven profile is active. I've seen pieces of answers to this but I'm having trouble putting it all together.

I have a Maven profile like this:

<profiles>
    <profile>
        <id>production</id>
        <activation>
            <property>
                <name>environment.type</name>
                <value>prod</value>
            </property>
        </activation>
    </profile>

    <profile>
        <id>development</id>
        <activation>
            <property>
                <name>environment.type</name>
                <value>dev</value>
            </property>
        </activation>

        <!-- Database properties for Spring -->
        <properties>
            <db.driver>oracle.jdbc.driver.OracleDriver</db.driver>
            <db.type>oracle</db.type>
            <db.host>192.168.0.0</db.host>
            <db.port>1521</db.port>
            <db.name>myDb</db.name>
            <db.url>jdbc:${db.type}:thin:@${db.host}:${db.port}:${db.name}</db.url>
        </properties>

And a settings.xml file like this:

<servers>
  <server>
    <id>development</id>
    <username>jsmith</username>
    <password>secret</password>
  </server>
</servers>

....

<profiles>
  <profile>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>

    <properties>
      <environment.type>dev</environment.type>
    </properties>
  </profile>
</profiles>

And in servlet-context.xml:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
    <property name="driverClassName">
        <value>${db.driver}</value>
    </property>

    <property name="url">
        <value>${db.url}</value>
    </property>

    <property name="username">
        <value>${db.username}</value>
    </property>

    <property name="password">
        <value>${db.password}</value>
    </property>

    <property name="maxActive">
        <value>10</value>
    </property>

    <property name="maxIdle">
        <value>1</value>
    </property>
</bean>

My question is basically, how do I get the Maven properties into the servlet-context.xml file? Do I need a .properties file? I know a little about filtering in Maven and PropertyPlaceholderConfigurer in Spring but I don't know how to put them together -- or do they go together? Or is there a simpler way?

like image 437
ksnortum Avatar asked Aug 08 '12 22:08

ksnortum


3 Answers

Do I need a .properties file?

Generally speaking, YES, you need to use .properties file, this is what we do usually, especially for handling database connection configuration in spring context file.

The purpose of .properties file is to provide the capability of configuring database connections at application runtime (for web application, usually require restarting application container/server after .properties file changes). This is usually done at application deployment/installation step in different environment (DEV/TEST/UAT/PROD).

It is not a good practice to store those database connection settings in pom.xml, as the purpose of pom.xml is for project description and only used once at application build time (e.g. mvn deploy). And for most of time, even though it is packed into the final jar/war file, we don't really care and touch it after application is built.

To use .properties file in spring context, define a propertyConfigurer bean in your applicationContext, for example:

<bean id="propertyConfigurer"
  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations">
    <list>
      <!-- Default location inside war file -->
      <value>classpath:db.properties</value>
      <!-- Environment specific location, a fixed path on server -->
      <value>file:///opt/my-web-app/conf/db.properties</value>
    </list>
  </property>
  <property name="ignoreResourceNotFound" value="true"/>
</bean>

Hope this make sense.

like image 136
yorkw Avatar answered Sep 30 '22 10:09

yorkw


Using what I learned from the two answers and my research, I was able to get a development/production system, controlled by the pom, that sets the correct database values.

First, in the pom, I created two profiles.

<!-- Production and Development Profiles -->

<profiles>
    <!-- Nike profile needs go here -->
    <profile>
        <id>production</id>
        <activation>
            <property>
                <name>environment.type</name>
                <value>prod</value>
            </property>
        </activation>
    </profile>

    <!-- Catalyst profile needs go here -->
    <profile>
        <id>development</id>
        <activation>
            <property>
                <name>environment.type</name>
                <value>dev</value>
            </property>
        </activation>

        <build>

            <!-- This holds the properties for the Spring context file.
                 Database values will be changes to development. -->
            <filters>
                <filter>src/main/resources/dev.database.properties</filter>
            </filters>

            <resources>

                <!-- This is the directory that holds the Spring context
                     file.  The entire folder is searched, so either no
                     other file should have place holders or you should
                     exclude other files from being filtered. -->
                <resource>
                    <directory>src/main/webapp/WEBINF/</directory>
                    <filtering>true</filtering>
                </resource>
            </resources>
    </profile>
</profiles>

In the servlet-context.xml, in the WEBINF directory, I put place holders:

<!-- For database, uses maven filtering to fill in placeholders -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${db.driver}" />
    <property name="url"             value="${db.url}" />
    <property name="username"        value="${db.username}" />
    <property name="password"        value="${db.password}" />
    <property name="maxActive">
        <value>10</value>
    </property>
    <property name="maxIdle">
        <value>1</value>
    </property>
</bean>

Then I created a properties file to sit in src/main/resources

#
# Development database properties file
#
db.driver=oracle.jdbc.driver.OracleDriver
db.url=jdbc:oracle:thin:[USER/PASSWORD]@[HOST][:PORT]:SID
db.username=jsmith
db.password=s3cr3t

I can then start Maven with

mvn -P developement clean install

or have a settings.xml that sets the correct profile for me:

<settings>
  <profiles>
    <profile>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>

      <properties>
        <environment.type>dev</environment.type>
      </properties>
    </profile>
  </profiles>   
</settings>
like image 20
ksnortum Avatar answered Sep 30 '22 11:09

ksnortum


The main question is: how are you going to deploy your WAR file to the different environemnts?, via a RPM, a jenkins build, by hand?, also do you want the same WAR file to de deployed to all the environments?

a) you want your WAR to be deployed vi a JENKINS job (or manually through maven), just process your resources and use the profile in the build process of the jar (mvn -P production clean deploy), the maven pom should include code like this:

    <filters>
        <filter>${config.maven.plattform.resources}/environment/${config.environment.dir}/your_proyect.properties</filter>
    </filters>
    <resources>
        <resource>
            <directory>resources/servlet-context.xml</directory>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>target/generated-resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>

You should define your properties in the file your_proyect.properties (one per environment) and also define config.environment.dir for all your different profiles.

b) you want the same WAR/RPM, etc. for all your projects. Then you have to define the environemt as a property in the java startup of the application servert: -DENVIRONMENT=production and then load all the properties with a parametrized PropertyPlaceholderConfigurer as yorkw pointed out:

<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
        <value>classpath:environment/${ENVIRONMENT}/your_project.properties</value>
        </list>
    </property>
</bean>

Also remember to put all the properties in the WAR, the pom for the WAR build should include code like this:

                <execution>
                    <id>copy-env-resources</id>
                    <!-- here the phase you need -->
                    <phase>validate</phase>
                    <goals>
                        <goal>copy-resources</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${basedir}/src/main/webapp/WEB-INF/classes/environment/</outputDirectory>
                        <overwrite>true</overwrite>
                        <resources>
                            <resource>
                                <directory>${basedir}/${build_to_all_your_environments}</directory>
                                <includes>
                                    <include>**/*.properties</include>
                                </includes>
                            </resource>
                        </resources>
                    </configuration>
                </execution>

c) A mixed one: you can manually choose an enviroment over the one defined as a property in the server (-D), you can get this by using default properties, if not found then resort to the one for that environemt, this step is quite convoluted as it requires another set of properties, if you're interested check my post: deployment for different environments with maven and spring, i'm also interested in a better solution for c)

like image 37
user311174 Avatar answered Sep 30 '22 11:09

user311174