2010-02-05 21 views
29

Esto parece un problema bastante común, pero no he encontrado ningún tipo de consenso sobre el mejor método, así que planteo la pregunta aquí.¿Cargar propiedades específicas del entorno para usar con PropertyPlaceholderConfigurer?

Estoy trabajando en una aplicación de línea de comandos utilizando Spring Batch y Spring. Estoy usando un archivo de propiedades junto con PropertyPlaceholderConfigurer, pero no estoy seguro de cuál es la mejor forma de manejar los archivos de propiedades para múltiples entornos (desarrollo, prueba, etc.). Mi Google sólo está apareciendo formas programáticas de cargar las propiedades (es decir, en el código de Java en sí), que no funciona para lo que estoy haciendo.

Un enfoque que he considerado es simplemente colocar el archivo de propiedades de cada entorno en el servidor y agregar el directorio del archivo al classpath mediante un argumento de línea de comandos, pero he tenido problemas para cargar el archivo con ese método.

El otro método que estoy considerando es que basta con incluir todos los archivos de propiedades en el frasco y utilizar un argumento propiedad del sistema o línea de comandos para rellenar el nombre del archivo de propiedades en tiempo de ejecución, así:

<bean id="propertyConfigurer" 
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="locations"> 
     <list> 
      <value>classpath:job.properties.${env}</value> 
     </list> 
    </property> 
</bean> 

Me inclino por esta última solución, pero también estoy buscando si hay un método mejor que estoy pasando por alto.

También debo mencionar que tengo que hacer la sustitución en tiempo de ejecución en lugar de en la compilación. El proceso que estoy obligado a usar requiere una compilación única que se promocionará a través de los entornos para la producción, por lo que no puedo usar la sustitución de Maven o Ant.

Respuesta

2

Acepto, no debe ser una configuración de tiempo de compilación, ya que desea implementar exactamente la misma carga útil en los diversos contextos.

La propiedad Locations de PropertyPlaceHolderConfigurer puede tomar varios tipos de recursos. ¿También puede ser un recurso de sistema de archivos o una url? Por lo tanto, podría establecer la ubicación del archivo de configuración en un archivo en el servidor local y, luego, cada vez que se ejecute, se ejecutará en el modo especificado por el archivo de configuración en ese servidor. Si tiene servidores particulares para modos particulares de ejecución, esto funcionaría bien.

Leyendo entre líneas aunque parezca que desea ejecutar la misma aplicación en diferentes modos en el mismo servidor. Lo que sugeriría en este caso es pasar la ubicación del archivo de configuración a través de un parámetro de línea de comando. Sería un poco complicado pasar este valor al PropertyPlaceHolderConfigurer, pero no sería imposible.

3

La forma en que normalmente he hecho esto en el pasado es realizar una sustitución del entorno (dev/test/prod) de alguna manera en el paquete/tiempo de implementación.

Eso puede copiar el archivo de configuración correcto en la ubicación correcta en el servidor o simplemente agrupar el archivo de configuración correcto en el paquete de implementación. Si usa Ant/Maven esto debería ser bastante sencillo de lograr. ¿Qué herramienta de compilación estás usando? Ant/Maven, eso debería darte la capacidad de sustituir un valor.

Otra alternativa, que usa PropertyPlaceholderConfigurer es la de la propiedad SYSTEM_PROPERTIES_MODE_OVERRIDE. Usted puede usar esto para establecer la ubicación del archivo que desea cargar a través de un sistema de propiedad de propiedades, consulte:

http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html#SYSTEM_PROPERTIES_MODE_OVERRIDE

Espero que ayude.

+0

Gracias por la sugerencia de tiempo de compilación, pero no funciona para mi caso. He actualizado mi pregunta para reflejar eso; Quise incluir esa información originalmente y olvidarla. Verificare tu otra sugerencia. – Ickster

+0

Estoy usando Maven como herramienta de compilación. ¿Cómo combinará la configuración correcta al empaquetar en maven? – premcs

1

Para la sustitución del tiempo de compilación utilizo las propiedades de compilación Maven para la sustitución de variables. Puede determinar qué propiedades cargar en su archivo Maven settings.xml y el archivo podría ser específico del entorno. Para las propiedades de producción que usan PPC, consulte esto blog

+0

Gracias. Mira el comentario que le dejé a Jon; se aplica a su sugerencia también. – Ickster

+0

Creo que es posible que no hayas leído el blog que menciono. Es el método que usarías en tiempo de ejecución. Con eso estableces una variable de entorno que recoge cualquier archivo de propiedades que quieras, incluso aquellos fuera de tu JAR o WAR. – harschware

+0

El final de mi comentario a Jon dijo que verificaría su otra sugerencia también; mi comentario hacia ti implicaba que también verificaría tu otra sugerencia. Realmente no muy claro de mi parte. Voy a leer el enlace que proporcionaste en breve. ¡Gracias! – Ickster

9

Esencialmente tiene un JAR terminado que desea colocar en otro entorno, y sin ninguna modificación ha recogido las propiedades adecuadas en tiempo de ejecución. Si eso es correcto, entonces los siguientes enfoques son válidos:

1) Confíe en la presencia de un archivo de propiedades en el directorio de inicio del usuario.

Configurar el PropertyPlaceholderConfigurer para hacer referencia a un archivo de propiedades externo al JAR como esto:

<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="ignoreUnresolvablePlaceholders" value="false"/> 
    <property name="order" value="1"/> 
    <property name="locations"> 
     <list> 
     <!-- User home holds secured information --> 
     <value>file:${user.home}/MyApp/application.properties</value> 
     </list> 
    </property> 
    </bean> 

el sistema operativo asegurar el contenido del archivo application.properties para que sólo las personas adecuadas pueden tener acceso a ella . Como este archivo no existe cuando primero ejecuta la aplicación, cree un script simple que interrogará al usuario sobre los valores críticos (por ejemplo, nombre de usuario, contraseña, dialecto Hibernate, etc.) al inicio. Proporcione una amplia ayuda y valores predeterminados razonables para la interfaz de línea de comando.

2) Si su aplicación está en un entorno controlado para que se pueda ver una base de datos, entonces el problema puede reducirse a crear credenciales básicas utilizando la técnica 1) anterior para conectarse a la base de datos durante el inicio del contexto y luego sustitución utilizando valores leídos a través de JDBC. Necesitará un enfoque de dos fases para el inicio de la aplicación: la fase 1 invoca un contexto padre con el archivo application.properties rellenando una JdbcTemplate y el DataSource asociado; la fase 2 invoca el contexto principal que hace referencia al padre para que JdbcTemplate se pueda usar como se configuró en el JdbcPropertyPlaceholderConfigurer.

Un ejemplo de este tipo de código sería el siguiente:

public class JdbcPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer { 

    private Logger log = Logger.getLogger(JdbcPropertyPlaceholderConfigurer.class); 
    private JdbcTemplate jdbcTemplate; 
    private String nameColumn; 
    private String valueColumn; 
    private String propertiesTable; 

    /** 
    * Provide a different prefix 
    */ 
    public JdbcPropertyPlaceholderConfigurer() { 
    super(); 
    setPlaceholderPrefix("#{"); 
    } 

    @Override 
    protected void loadProperties(final Properties props) throws IOException { 
    if (null == props) { 
     throw new IOException("No properties passed by Spring framework - cannot proceed"); 
    } 
    String sql = String.format("select %s, %s from %s", nameColumn, valueColumn, propertiesTable); 
    log.info("Reading configuration properties from database"); 
    try { 
     jdbcTemplate.query(sql, new RowCallbackHandler() { 

     public void processRow(ResultSet rs) throws SQLException { 
      String name = rs.getString(nameColumn); 
      String value = rs.getString(valueColumn); 
      if (null == name || null == value) { 
      throw new SQLException("Configuration database contains empty data. Name='" + name + "' Value='" + value + "'"); 
      } 
      props.setProperty(name, value); 
     } 

     }); 
    } catch (Exception e) { 
     log.fatal("There is an error in either 'application.properties' or the configuration database."); 
     throw new IOException(e); 
    } 
    if (props.size() == 0) { 
     log.fatal("The configuration database could not be reached or does not contain any properties in '" + propertiesTable + "'"); 
    } 
    } 

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 
    this.jdbcTemplate = jdbcTemplate; 
    } 

    public void setNameColumn(String nameColumn) { 
    this.nameColumn = nameColumn; 
    } 

    public void setValueColumn(String valueColumn) { 
    this.valueColumn = valueColumn; 
    } 

    public void setPropertiesTable(String propertiesTable) { 
    this.propertiesTable = propertiesTable; 
    } 

} 

lo anterior, entonces se configuraría en la primavera siguiente (tenga en cuenta la propiedad de orden se produce después de los usuales $ marcadores de posición con el prefijo):

<!-- Enable configuration through the JDBC configuration with fall-through to framework.properties --> 
    <bean id="jdbcProperties" class="org.example.JdbcPropertyPlaceholderConfigurer"> 
    <property name="ignoreUnresolvablePlaceholders" value="false"/> 
    <property name="order" value="2"/> 
    <property name="nameColumn" value="name"/> 
    <property name="valueColumn" value="value"/> 
    <property name="propertiesTable" value="my_properties_table"/> 
    <property name="jdbcTemplate" ref="configurationJdbcTemplate"/> <!-- Supplied in a parent context --> 
    </bean> 

Esto permitiría el seguimiento que se produzca en la configuración del resorte

<!-- Read from application.properties --> 
<property name="username">${username}</property> 
... 
<!-- Read in from JDBC as part of second pass after all $'s have been fulfilled --> 
<property name="central-thing">#{name.key.in.db}</property> 

3) Por supuesto, si estás en un contenedor de aplicaciones web, entonces simplemente usas JNDI. Pero no eres así que no puedes.

Espero que esto ayude!

+0

Creo que su sugerencia de solicitar el conjunto inicial de propiedades es interesante. Desafortunadamente, no es algo que pueda usar, ya que la aplicación se lanzará a través de una herramienta de programación en todos los entornos y la cadena de comandos que uso no podrá cambiar de una ejecución a la siguiente. – Ickster

+0

Tenga en cuenta que solo la instalación inicial requiere el archivo application.properties para crearse.Si pudieras arreglar con un administrador de sistema para colocar el archivo, configurado como lo necesitaban, basado en una plantilla, entonces eso podría funcionar. Por supuesto, eso requiere que la aplicación se despliegue en un entorno conocido y controlado. –

0

Utilizo la opción de ruta de clases y ajuste la ruta de clase por entorno en Jetty. En el plugin jetty-maven, puede establecer un directorio para clases de prueba y tener allí sus recursos de prueba.

Para entornos que no son locales (test/producción) utilizo una bandera ambiente y enviar los archivos correspondientes a la carpeta $ JETTY_HOME/recursos (que se construye en la ruta de clase del embarcadero)

3

This blog post tiene algunas buenas ideas para cambiar fuera de los archivos de propiedades. Terminé usando el PropertyPlaceholderConfigurer doble para usar una propiedad del sistema para especificar el archivo de configuración.

8

Usted podría utilizar <context:property-placeholder location="classpath:${target_env}configuration.properties" /> en su XML primavera y configurar ${target_env} utilizando un argumento de línea de comandos (-Dtarget_env=test.).

Comenzando en Spring 3.1 puede usar <context:property-placeholder location="classpath:${target_env:prod.}configuration.properties" /> y especificar un valor predeterminado, eliminando así la necesidad de establecer el valor en la línea de comandos.

En caso de que Maven sea una opción, la variable Spring se podría establecer durante la ejecución del complemento, p. durante la ejecución de prueba o prueba de integración.

<plugin> 
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-surefire-plugin</artifactId> 
    <version>2.12</version> 
    <configuration> 
     <systemPropertyVariables> 
      <target_env>test.</target_env> 
     </systemPropertyVariables> 
    </configuration> 
</plugin> 

Asumo que los diferentes perfiles de Maven también funcionarían.

6

Primavera propiedad de marcador de posición Configurer - A pocos no opciones tan obvias

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="location" value="classpath:db.properties"></property> 
</bean> 
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 
    <property name="url" value="${db.url.${mode}}" /> 
    <property name="username" value="${db.username.${mode}}" /> 
    <property name="password" value="${db.password.${mode}}" /> 
</bean> 

${db.username.${mode}}: Aquí "modo" define el modo de proyecto (medio ambiente) - dev/prod archivo Propiedades parece:

#Database properties 
#mode dev/prod 
mode=dev 

#dev db properties 
db.url.dev=jdbc:mysql://localhost:3306/dbname 
db.username.dev=root 
db.password.dev=root 

#prod db properties 
db.url.prod=jdbc:mysql://localhost:3306/dbname 
db.username.prod=root 
db.password.prod=root 
+0

Esta es realmente una buena opción para mí. – RishiPandey

+0

podemos especificar el sistema env. variable en el archivo de propiedades en el valor de modo. De esta manera no tenemos que hacer ningún cambio es el código desplegable. podemos implementar en cualquier lugar simplemente cambiando la variable de entorno. – RishiPandey

+0

RishiPandey, ¿cómo harías eso? ¿decirle al archivo de propiedades para establecer el valor del modo de la variable env del sistema? –

0

Hola después de leer Spring in Action encontré una solución provista por Spring. Perfil o Condicional: puede crear múltiples perfiles, por ej. test, dev, prod etc.

Spring distingue dos propiedades distintas al determinar qué perfiles están activos: spring.profiles.active y spring.profiles.default. Si se establece spring.profiles.active , su valor determina qué perfiles están activos. Pero si el resorte .profiles.active no está configurado, entonces Spring mira a spring.profiles.default. Si no se establece spring.profiles.active ni spring.profiles.default, no hay perfiles activos , y solo se crean los beans que no están definidos como pertenecientes a un perfil.

Hay varias maneras de configurar estas propiedades: 1 Como parámetros de inicialización en DispatcherServlet 2 como parámetros de contexto de una aplicación web 3 Como JNDI & entradas 4 como variables de entorno 5 Como las propiedades del sistema de JVM 6 Uso de la @ Anotación ActiveProfiles en una clase de prueba de integración

Cuestiones relacionadas