java - Build once and deploy for many environments -
we have legacy system 50 -60 maven modules, of these modeules have used maven resource plugin filter properties (replace tokens @ build time in property files). painful when comes build different environments, because have build application each time when need deploy different environments.
we have new requirement build application once , deploy many environments. best solution this? think of externalizing filter properties, biggest issue replace token of existing property files of application (see application.properties) file below. want keep existing property files , pick values external config file.
any appreciate it.
e.g filter.properties injected maven.
generic.sharepoint.host=xxxxx generic.deploy.apps.host=xxxxx generic.deploy.apps.url=xxxx generic.deploy.trusted.host=xxxx generic.deploy.trusted.url=xxxx generic.deploy.orderentry=xxxxx
application.properties
generic.sharepoint.host=${generic.sharepoint.host} generic.deploy.apps.host=${generic.deploy.apps.host} generic.deploy.apps.url=${generic.deploy.apps.url} generic.deploy.trusted.host=${generic.deploy.trusted.host} generic.deploy.trusted.url=${generic.deploy.trusted.url} generic.deploy.orderentry=${generic.deploy.orderentry}
in experience had build spring based web application once , deploy on many different environments, i'm trying show solution worked fine me.
i couldn't find way hand off task maven, decided externalize configuration , found exploiting spring manage achieve performing 2 steps:
- make application 'aware' of environment deployed to, reading file containing environment's name (e.g. dev, test, production)
- accordingly environment value found,set system property , load config file placed in correspondent folder.
having tomcat, set configuration folder structure under shared/classes
in way this:
and put in application-config folder
application-config.properties
file containing descripton of environment:
application-config.env=dev
and same configuration files under every single child folder configured related environment required. after had add spring application context application-config-context.xml
bean definitions in order interact org.springframework.beans.factory.config.propertiesfactorybean
:
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="environmentdetector" class="com.mycompany.myapp.spring.util.propertiesbasedenvironmentdetector" init-method="init" > <property name="properties" ref="environmentdefinitionproperties" /> <property name="environmentdefinitionpropertyname" value="application-config.env"/> </bean> <bean id="environmentdefinitionproperties" class="org.springframework.beans.factory.config.propertiesfactorybean"> <property name="ignoreresourcenotfound" value="true" /> <property name="locations"> <list> <value>classpath:application-config/application-config.properties</value> </list> </property> </bean> <bean class="org.springframework.beans.factory.config.propertyplaceholderconfigurer" > <property name="systempropertiesmodename" value="system_properties_mode_override" /> <property name="ignoreresourcenotfound" value="false" /> <property name="ignoreunresolvableplaceholders" value="true" /> <property name="properties" ref="applicationexternalproperties" /> </bean> <bean id="applicationexternalproperties" class="org.springframework.beans.factory.config.propertiesfactorybean" > <property name="location" value="${application-config.prefix}application-config/${application-config.env}/application.properties" /> </bean> </beans>
i had code class override spring's postprocessbeanfactory
method of interface org.springframework.beans.factory.config.beanfactorypostprocessor
this:
package com.mycompany.myapp.doc.spring.util; import java.util.properties; import javax.annotation.postconstruct; import org.apache.commons.logging.log; import org.apache.commons.logging.logfactory; import org.springframework.beans.beansexception; import org.springframework.beans.factory.config.beanfactorypostprocessor; import org.springframework.beans.factory.config.configurablelistablebeanfactory; import org.springframework.core.priorityordered; public class propertiesbasedenvironmentdetector implements beanfactorypostprocessor, priorityordered{ private static final log log = logfactory.getlog(propertiesbasedenvironmentdetector.class); private properties properties; private string environmentdefinitionpropertyname ="env"; private string defaultenvironment="dev"; private string environmentsystempropertyname; private int order = priorityordered.highest_precedence; private string prefixsystempropertyname = "application-config.prefix"; private string prefixdefault = "classpath:"; private string prefix; public void setproperties(properties properties) { this.properties = properties; } @postconstruct public void init() { if (environmentsystempropertyname == null) { environmentsystempropertyname = environmentdefinitionpropertyname; } string activeenvironment = properties.getproperty(environmentdefinitionpropertyname, defaultenvironment); prefix = properties.getproperty(prefixsystempropertyname); if (prefix == null) { prefix = prefixdefault; properties.put(prefixsystempropertyname, prefix); } system.setproperty(environmentsystempropertyname , activeenvironment); system.setproperty(prefixsystempropertyname , prefix); log.warn("initializing environment: "+activeenvironment); } public string getenvironmentdefinitionpropertyname() { return environmentdefinitionpropertyname; } public void setenvironmentdefinitionpropertyname( string environmentdefinitionpropertyname) { this.environmentdefinitionpropertyname = environmentdefinitionpropertyname; } public string getdefaultenvironment() { return defaultenvironment; } public void setdefaultenvironment(string defaultenvironment) { this.defaultenvironment = defaultenvironment; } public string getenvironmentsystempropertyname() { return environmentsystempropertyname; } public void setenvironmentsystempropertyname(string environmentsystempropertyname) { this.environmentsystempropertyname = environmentsystempropertyname; } @override public void postprocessbeanfactory(configurablelistablebeanfactory beanfactory) throws beansexception { string activeenvironment = system.getproperty(environmentsystempropertyname); log.warn("postprocessing applicationcontext environment: "+activeenvironment+" "+prefix); } @override public int getorder() { return order; } public void setorder(int order) { this.order = order; } }
when application starts, spring loads contexts , our custom class called. in init()
method first loads environmentdefinitionpropertyname
through injected properties
property , set system property having key environmentdefinitionpropertyname
value set in bean definition.
after that, propertyplaceholderconfigurer
can load properties file location since resolves:
<property name="location" value="${application-config.prefix}application-config/${application-config.env}/application.properties" />
into
<property name="location" value="classpath:application-config/dev/application.properties" />
this approach had these principal advantages:
- you can add further environments adding folder under original configuration folder
- the original application package same on environments
- the properties file has 'normal'
key=value
format, application.properties file.
hope helps , may others well.