Create an easy-to-deploy web application project

Overview
The configuration files of many WEB applications are located in the classpath of the project (such as WEB-INF\classes\conf\conf.properties), and the data source also directly uses data sources such as DBCP and C3P0, which are defined in the Spring configuration file. There are big problems in the deployability of this project structure. First, the deployer needs to change the configuration information in the WAR package. Second, the new version of the WAR package cannot directly cover the WAR package in the production environment, otherwise the original configuration file will be overwritten. That is to say, the WAR package is stateful, it contains both the program code and the configuration information of the production environment, so the configuration and portability will be greatly weakened. This article will give an optimized project structure to solve the problem of project deployment.

"Traditional" WEB project properties file

The following is a traditional WEB project that references a classpath-based properties file via <context:property-placeholder/> in its Spring configuration file, as follows:
<context:property-placeholder location="classpath:conf/conf.properties"/>
<rop:annotation-driven
            core-pool-size="${rop.corePoolSize}"
            max-pool-size="${rop.maxPoolSize}"
            queue-capacity="${rop.queueCapacity}"
            keep-alive-seconds="${rop.keepAliveSeconds}"
            thread-ferry-class="${rop.threadFerryClass}"
            sign-enable="${rop.signEnable}"
            ext-error-base-names="${rop.customErrorBasenames}"
            service-timeout-seconds="${rop.serviceTimeoutSeconds}"
            session-manager="sessionManager"
            app-secret-manager="appSecretManager"
            formatting-conversion-service="conversionService"/>


After this WEB project is packaged into a WAR package, if you want to change the parameters in the conf.properties configuration file in the production environment, you must change it in the WAR package, although most WEB applications will unpack the WAR to a file directory. Even so, it is a headache for developers and implementers.

For developers, the conf.properties of the production environment need to be used to replace the conf.properties of the development environment each time a WAR package is created. Often, the developers do not know the specific conditions of the production environment. At this time, the developers can only request the implementation of Personnel should not use this WAR package to directly overwrite the WAR package in the production environment.

For the implementer, he has to upgrade the WAR package cautiously, first copy the conf.properties of the WAR package in the production environment, and overwrite the conf.properties in the upgraded WAR package, so as not to overwrite the conf.properties in the production environment . Of course, it is also very troublesome to change the configuration parameters usually, and it needs to be changed in each WAR package.

How to plan property files
for easy-to-deploy WEB projects In order to facilitate the deployment of WEB projects, the property files must be decoupled from the WAR package, that is, the WAR package does not contain configuration files. But this problem comes again. Is it difficult that in the development environment, the configuration file needs to be placed in a directory other than a project. Isn't the team development of the project very troublesome, and each member needs to manually copy the configuration file of the project. You can't use SVN to sync.

In order to make the project both portable for development (as soon as SVN is synchronized, mvn jetty:run can start the project) and portable for deployment (upload the WAR package that covers the upgrade, no other actions are required). We only need to define the location of the configuration file through a system parameter to perfectly meet the development portability and deployment portability.

<plugin>
	<groupId>org.mortbay.jetty</groupId>
	<artifactId>maven-jetty-plugin</artifactId>
	<version>6.1.5</version>
	<configuration>
		<webDefaultXml>src/resources/dev/webdefault.xml</webDefaultXml>
		<systemProperties>
                        <!-- ①Note that here, we use a JVM system property to specify the path to the configuration file-->
			<systemProperty>
				<name>CONFFILE_PATH</name>
				<value>classpath:conf/conf.properties</value>
			</systemProperty>
		</systemProperties>
		<webAppSourceDirectory>src/main/webapp</webAppSourceDirectory>
		<scanIntervalSeconds>0</scanIntervalSeconds>
		<contextPath>/</contextPath>
		<connectors>
			<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
				<port>7100</port>
			</connector>
		</connectors>
	</configuration>
</plugin>

   At ①, we use a JVM system parameter to specify the path of the configuration file. Here is the development environment. We put the configuration file in the classpath of the project, which can be synchronized through the SVN of the project and can be shared by all members of the project team.
   Under the adjustment of the Spring configuration file above, the property configuration file is referenced through this system parameter CONFFILE_PATH
<!-- ① Note that the values ​​of system parameters are accessed through SpringEL expressions -->
<context:property-placeholder location="#{systemProperties.CONFFILE_PATH}"/>
<rop:annotation-driven
            core-pool-size="${rop.corePoolSize}"
            max-pool-size="${rop.maxPoolSize}"
            queue-capacity="${rop.queueCapacity}"
            keep-alive-seconds="${rop.keepAliveSeconds}"
            thread-ferry-class="${rop.threadFerryClass}"
            sign-enable="${rop.signEnable}"
            ext-error-base-names="${rop.customErrorBasenames}"
            service-timeout-seconds="${rop.serviceTimeoutSeconds}"
            session-manager="sessionManager"
            app-secret-manager="appSecretManager"
            formatting-conversion-service="conversionService"/>

   In the development environment, use Maven's Jetty plug-in. The CONFFILE_PATH system parameter specified by the Jetty plug-in is conf/conf.properties in the class path.
   In the deployment environment, we define the JVM system parameters in the WEB application server. Taking Websphere as an example, the JVM system parameters are defined as follows:
  

quote
   Operation steps:
   Server -> Application Server (select the corresponding application server) -> Server Infrastructure (Java and Process Management/Process Definition) -> Other Properties (Java Virtual Machine) -> Other Properties (Custom Properties)

   Here, we put the configuration file in the file:/D:/framework/conf/framework.properties file.
(Note: For Tomcat, Jetty can define JVM parameters in the configuration file of the application server. Please find it online.) In
  
   this way, the old version of the project WAR package can be directly overwritten during deployment, because the configuration file has been placed in a separate WAR package. In the file directory of the external operating system of the package, there is no need to consider the impact of the WAR upgrade on the configuration file.

Data source configuration

    WEB applications basically have data sources. Many traditional WEB projects use DBCP, C3P0 and other data sources to define in Spring configuration files, which will bring problems to deployment:
  • The data source cannot be shared. If there are multiple WAR projects in a server, and these WAR projects all access the same database, since the data sources are independently configured in their respective projects, there will be as many data sources as there are projects. The sharing of data sources cannot be achieved.
  • The optimization of the data source is inconvenient. If the data sources such as DBCP and C3P0 are configured in the Spring configuration file, the optimization parameters of the data source are extracted into the configuration file. Since there are many parameters for data source optimization, not every parameter is often extracted from the configuration file, which limits the optimization of the data source.

   Therefore, Spring should refer to the data source of JNDI, instead of defining the data source by itself in the Spring configuration file through DBCP and C3P0.
   Of course, a very real problem is how to use the JNDI data source in the development environment. In fact, this is no longer a problem. Jetty can define the JNDI data source very early. It is not only Websphere and Weblogic that can define the JNDI data source.
   Here is the configuration to define a JNDI datasource using Maven's maven-jetty-plugin:
<plugin>
	<groupId>org.mortbay.jetty</groupId>
	<artifactId>maven-jetty-plugin</artifactId>
	<version>6.1.5</version>
	<configuration>
		<webDefaultXml>src/resources/dev/webdefault.xml</webDefaultXml>
                <!--①Add an additional jetty configuration file-->
                <jettyEnvXml>src/resources/jetty.xml</jettyEnvXml>
		<systemProperties>
			<systemProperty>
				<name>CONFFILE_PATH</name>
				<value>classpath:conf/conf.properties</value>
			</systemProperty>
		</systemProperties>
		<webAppSourceDirectory>src/main/webapp</webAppSourceDirectory>
		<scanIntervalSeconds>0</scanIntervalSeconds>
		<contextPath>/</contextPath>
		<connectors>
			<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
				<port>7100</port>
			</connector>
		</connectors>
	</configuration>
</plugin>

   
  An additional jetty configuration file jetty.xml is added at ①, and its configuration content is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
        "http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">

<New id="myds" class="org.mortbay.jetty.plus.naming.Resource">
    <Arg>jdbc/myds</Arg>
    <Arg>
        <New class="org.apache.commons.dbcp.BasicDataSource">
            <Set name="driverClassName">oracle.jdbc.driver.OracleDriver</Set>
            <Set name="url">jdbc:oracle:thin:@127.0.0.1:1521:orcl</Set>
            <Set name="username">test</Set>
            <Set name="password">test</Set>
        </New>
    </Arg>
</New>
</Configure>

  A JNDI data source named jdbc/myds is defined here, so that the project's Spring configuration file can directly reference this JNDI data source:
<?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"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
       http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd">
   ...
    <jee:jndi-lookup id="frameworkDS" jndi-name="java:comp/env/jdbc/myds"/>
</beans>

  Since the data source name of JNDI is not fixed, for example, in the deployment environment, it may be called "jdbc/yourds", so it is necessary to attribute the name of jndi. Combined with the configuration file we mentioned above, we can add a property to the configuration file, and for the time being we name the property ds.jndi:

  conf/conf.properties
  ds.jndi=java:comp/env/jdbc/yourds

  The JNDI data source is referenced by parameter name in the Spring configuration 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:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
       http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd">
    ...
    <context:property-placeholder location="#{systemProperties.CONFFILE_PATH}"/>
    <jee:jndi-lookup id="frameworkDS" jndi-name="${ds.jndi}"/>
</beans>


   Prefect! At this point, developers and deployers are very happy. After the JNDI data source is defined in the deployment environment, each WAR package can use it.
   Did you find that Maven and Spring are really omnipotent?

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326294789&siteId=291194637