¿Debe configurar las propiedades de conexión de la base de datos en server.xml o context.xml?

79

Estoy tratando de configurar las propiedades de conexión de la base de datos usando JNDI para una aplicación web Spring.

Estoy considerando dos enfoques de la siguiente manera:

Enfoque 1:

En su configuración de Spring, puede tener algo como:

<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/facs"/>

Luego, en su archivo webapp /META-INF/context.xml también debería tener algo similar:

<?xml version='1.0' encoding='utf-8'?>

<!-- antiResourceLocking="true" -->
<Context path="/podd-apn"
         reloadable="true"
         cachingAllowed="false"
         antiResourceLocking="true"
         >

  <Resource name="jdbc/facs"              
            type="javax.sql.DataSource" username="${database.username}" password="${database.password}"
            driverClassName="org.postgresql.Driver" 
            url="${database.url}"
            maxActive="8" maxIdle="4"
            global="jdbc/facs" 
            />


</Context>

Y en su web.xml debería algo como:

<!-- JNDI -->
  <resource-ref>
    <description>FACs Datasource</description>
    <res-ref-name>jdbc/facs</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref> 


Enfoque 2:

Configuración en el contexto de Spring como este:

<jee:jndi-lookup id="dbDataSource"
   jndi-name="jdbc/DatabaseName"
   expected-type="javax.sql.DataSource" />

Puede declarar el recurso JNDI en el server.xml de Tomcat usando algo como esto:

<GlobalNamingResources>
  <Resource name="jdbc/DatabaseName" auth="Container" type="javax.sql.DataSource"
              username="dbUsername" password="dbPasswd"
              url="jdbc:postgresql://localhost/dbname"
              driverClassName="org.postgresql.Driver"
              initialSize="5" maxWait="5000"
              maxActive="120" maxIdle="5"
              validationQuery="select 1"
              poolPreparedStatements="true"/>
</GlobalNamingResources/>

Y haga referencia al recurso JNDI del sitio web context.xml de Tomcat de esta manera:

<ResourceLink name="jdbc/DatabaseName"
   global="jdbc/DatabaseName"
   type="javax.sql.DataSource"/>


Mi pregunta es ¿cuál es el mejor lugar para guardar las propiedades de la base de datos? ¿Deben colocarse en server.xml o context.xml ?

Además, si tengo 2 bases de datos, ¿debo usar dos configuraciones?

Además, ¿es una buena práctica colocarlos directamente en server.xml o context.xml? ¿O necesito configurar a través de la consola GUI de Tomcat Manager?

¡Gracias!

usuario1016403
fuente

Respuestas:

27

Prefiero un tercer enfoque que toma lo mejor del enfoque 1 y el enfoque 2 descritos por el usuario 1016403 .

Enfoque 3

  1. Guarde las propiedades de la base de datos en el server.xml
  2. hacer referencia a las server.xmlpropiedades de la base de datos desde la aplicación webMETA-INF/context.xml

Beneficios del enfoque 3

Si bien el primer punto es útil por razones de seguridad, el segundo punto es útil para hacer referencia al valor de las propiedades del servidor desde la aplicación web, incluso si los valores de las propiedades del servidor cambiarán.

Además, el desacoplamiento de las definiciones de recursos en el servidor de su uso por la aplicación web hace que dicha configuración sea escalable entre organizaciones con diversa complejidad donde diferentes equipos trabajan en diferentes niveles / capas: el equipo de administradores del servidor puede trabajar sin entrar en conflicto con el equipo de desarrolladores si el administrador comparte lo mismo Nombre JNDI con el desarrollador de cada recurso.

Implementación del enfoque 3

Defina el nombre JNDI jdbc/ApplicationContext_DatabaseName.

Declare las jdbc/ApplicationContext_DatabaseNamediversas propiedades y valores de Tomcat server.xmlusando algo como esto:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContext_DatabaseName" auth="Container" type="javax.sql.DataSource"
              username="dbUsername" password="dbPasswd"
              url="jdbc:postgresql://localhost/dbname"
              driverClassName="org.postgresql.Driver"
              initialSize="5" maxWait="5000"
              maxActive="120" maxIdle="5"
              validationQuery="select 1"
              poolPreparedStatements="true"/>
</GlobalNamingResources/>

Vincular las jdbc/ApplicationContext_DatabaseNamepropiedades de la aplicación web META-INF/context.xmlmediante un contexto JNDI privado de la aplicación java:comp/env/especificado en el nameatributo:

<Context path="/ApplicationContext" ... >
  <!--
    "global" attribute links to GlobalNamingResources in the ${catalina.base}/conf/server.xml (server administrator team)
    "name" attribute is relative to the application-private JNDI context java:comp/env/ and is looked up from the java web application (application developer team)
  -->
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName" name="jdbc/DatabaseName" type="javax.sql.DataSource"/>
</Context>

Finalmente, para utilizar el recurso JNDI, especifique el nombre JNDI jdbc/DatabaseNameen el descriptor de implementación de la aplicación web:

<resource-ref>
    <description>DatabaseName's Datasource</description>
    <res-ref-name>jdbc/DatabaseName</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref> 

y en el contexto de primavera:

<jee:jndi-lookup id="DatabaseNameDataSource"
   jndi-name="jdbc/DatabaseName"
   expected-type="javax.sql.DataSource" />

Enfoque 3 inconvenientes

Si se cambia el nombre JNDI, tanto el server.xmlcomo el META-INF/context.xmltendrán que ser editados y será necesaria una implementación; sin embargo, este escenario es raro.

Enfoque 3 variaciones

Muchas fuentes de datos utilizadas por una aplicación web

Simplemente agregue configuraciones a Tomcat's server.xml:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContext_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContext_DatabaseName2" ... />
  ...
</GlobalNamingResources/>

Agregue la aplicación web de enlace META-INF/context.xmlmediante un contexto JNDI privado de la aplicación java:comp/env/especificado en el nameatributo:

<Context path="/ApplicationContext" ... >
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName1" name="jdbc/DatabaseName1" ... />
  <ResourceLink global="jdbc/ApplicationContext_DatabaseName2" name="jdbc/DatabaseName2" ... />
  ...
</Context>

Finalmente, agregue el uso de recursos JNDI en el descriptor de implementación de la aplicación web:

<resource-ref>
    <description>DatabaseName1's Datasource</description>
    <res-ref-name>jdbc/DatabaseName1</res-ref-name> ... 
</resource-ref> 
<resource-ref>
    <description>DatabaseName2's Datasource</description>
    <res-ref-name>jdbc/DatabaseName2</res-ref-name> ... 
</resource-ref>
...

y en el contexto de primavera:

<jee:jndi-lookup id="DatabaseName1DataSource"
   jndi-name="jdbc/DatabaseName1" ... />
<jee:jndi-lookup id="DatabaseName2DataSource"
   jndi-name="jdbc/DatabaseName2" ... />
...


Muchas fuentes de datos utilizadas por muchas aplicaciones web en el mismo servidor

Simplemente agregue la configuración a Tomcat's server.xml:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContextX_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContextX_DatabaseName2" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName1" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName2" ... />
  ...
</GlobalNamingResources/>

la otra configuración debe ser deducible del caso de variación anterior.


Muchas fuentes de datos a la misma base de datos utilizada por muchas aplicaciones web en el mismo servidor

En tal caso, las server.xmlconfiguraciones de un Tomcat como:

<GlobalNamingResources>
  <Resource name="jdbc/ApplicationContextX_DatabaseName" ... />
  <Resource name="jdbc/ApplicationContextY_DatabaseName" ... />

termina en dos aplicaciones web diferentes META-INF/context.xml como:

<Context path="/ApplicationContextX" ... >
  <ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>

y, como:

<Context path="/ApplicationContextY" ... >
  <ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/DatabaseName" ... />
</Context>

por lo que alguien podría estar preocupado por el hecho de que lo mismo name="jdbc/DatabaseName"sea ​​buscado y luego utilizado por dos aplicaciones diferentes implementadas en el mismo servidor: esto no es un problema porque jdbc/DatabaseNamees un contexto JNDI privado de la aplicación java:comp/env/, ApplicationContextX porjava:comp/env/ lo que al usar no se puede (por diseño) busque el recurso vinculado global="jdbc/ApplicationContextY_DatabaseName".

Por supuesto, si se sintió más relajado sin esta preocupación, podría usar una estrategia de nomenclatura diferente como:

<Context path="/ApplicationContextX" ... >
  <ResourceLink global="jdbc/ApplicationContextX_DatabaseName" name="jdbc/applicationXprivateDatabaseName" ... />
</Context>

y, como:

<Context path="/ApplicationContextY" ... >
  <ResourceLink global="jdbc/ApplicationContextY_DatabaseName" name="jdbc/applicationYprivateDatabaseName" ... />
</Context>
taringamberini
fuente
Pregunta con respecto a su escenario de "Muchas fuentes de datos para la misma base de datos utilizada por muchas aplicaciones web en el mismo servidor" ... <Resource name="jdbc/ApplicationContextX_DatabaseName" ... /> <Resource name="jdbc/ApplicationContextY_DatabaseName" ... /> Si los recursos fueran grupos de conexiones, ¿esto le daría dos grupos separados, uno por aplicación web? Considerando que, si me vincule desde ambas aplicaciones web a un recurso, solo habría un grupo de conexiones, ¿correcto? ¿Alguna razón para preferir uno al otro? (grupos de conexiones de base de datos separados uno por aplicación web frente a un grupo de conexiones compartido por todas las aplicaciones web)? Gracias.
Rebeccah
1
@Rebeccah - P1: Si los recursos fueran grupos de conexiones, ¿esto le daría dos grupos separados, uno por aplicación web? A1: Sí lo haría.
taringamberini
1
@Rebeccah - P2: Considerando que, si me vincule desde ambas aplicaciones web a un recurso, solo habría un grupo de conexiones, ¿correcto? A2: Correcto.
taringamberini
2
@Rebeccah - P3: ¿Alguna razón para preferir una a la otra? (grupos de conexiones de base de datos separados uno por aplicación web frente a un grupo de conexiones compartido por todas las aplicaciones web)? R3: Prefiero "grupos de conexiones de base de datos separados uno por aplicación web" porque no me gustaría que el uso intensivo de webAppX termine en lentitud de webAppY debido al agotamiento del grupo de conexiones de webAppX; Además, el sistema de registro y monitoreo de la base de datos no podía distinguir entre las solicitudes webAppX y webAppY, por lo que comprender y aislar los problemas será difícil o, peor aún, imposible.
taringamberini
23

YOUR_APP.xml archivo

Prefiero el enfoque 2 (ponga todo (no solo algún atributo en la configuración), pero en lugar de colocarlos en el global server.xmlo global context.xml, debe colocarlo en el archivo específico de la aplicación en su Tomcat.context.xml.default YOUR_APP.xml

El YOUR_APP.xmlarchivo se encuentra en $catalinaHome/conf/<engine>/<host>(por ejemplo conf/Catalina/localhost/YOUR_APP.xml).

La configuración en la aplicación específica YOUR_APP.xmlsolo está disponible para la aplicación específica.

Consulte la guía publicada por MuleSoft. Y consulte la documentación oficial, Referencia de configuración de Tomcat , página para The Context Container

Para citar esa documentación:

Los elementos de contexto individuales pueden definirse explícitamente:

•…

• En archivos individuales (con extensión ".xml") en el $CATALINA_BASE/conf/[enginename]/[hostname]/directorio. La ruta de contexto y la versión se derivarán del nombre base del archivo (el nombre del archivo menos la extensión .xml).

•…

Ralph
fuente
2
Gracias por tu respuesta. si coloco todas las propiedades en nuestras aplicaciones META-INF / context.xml? ¿Es el mejor lugar para guardar?
user1016403
5
No creo que colocar el valor de algunas propiedades DENTRO (por ejemplo en META-INF / context.xml) de la aplicación sea un buen enfoque, porque entonces tienes que recompilar e implementar la aplicación si las propiedades cambian. - Entonces esto sería casi lo mismo que no usar ninguna propiedad y colocar los valores directamente en el archivo config.xml de primavera
Ralph
Entonces, ¿cuál es el lugar recomendado para guardarlos?
user1016403
2
Solo agregaría, en lugar de definirlos para todas las aplicaciones que usan context.xml.default, puede usar archivos de configuración de contexto específicos de la aplicación, como yourapp.xml en la misma carpeta, lo que le permite apuntar dos implementaciones de la misma guerra a diferentes bases de datos.
usethe4ce
1
No es una respuesta útil. Simplemente indica una preferencia y no proporciona ninguna justificación.
DavidS
10

Enfoque 4

En lugar de usar JNDI, trabajo con .propertiesarchivos y construyo objetos complejos durante la inicialización del programa en lugar de en el tiempo de configuración.

Ya usas Spring y es fácil de construir DataSourcepor:

<context:property-placeholder location="classpath:app.properties"/>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
    <property name="url" value="jdbc:oracle:thin:@${db.host}:${db.port}:${db.user}"/>
    <property name="username" value="${db.user}"/>
    <property name="password" value="${db.pass}"/>
</bean>

Estoy completamente de acuerdo con Ralph con el uso del descriptor de implementación en, $CATALINA_BASE/conf/[enginename]/[hostname]/$APP.xmlpero en su lugar, JNDI me gusta el archivo de valor clave simple.

Con Spring, inyectar las propiedades anteriores en los campos de frijoles es fácil:

@Value("${db.user}") String defaultSchema;

en lugar de JNDI:

@Inject ApplicationContext context;
Enviroment env = context.getEnvironment();
String defaultSchema = env.getProperty("db.user");

Tenga en cuenta también que EL permite esto (valores predeterminados y sustitución recursiva profunda):

@Value('${db.user:testdb}') private String dbUserName;

<property name='username' value='${db.user.${env}}'/>

Para externalizar el .propertiesarchivo, utilizo Tomcat 7 moderno que tiene org.apache.catalina.loader.VirtualWebappLoader :

<Loader className="org.apache.catalina.loader.VirtualWebappLoader"
        virtualClasspath="/srv/web/app/"/>

Por lo tanto, sus devops se llenan virtualClasspathcon rutas completas externas locales que están separadas por aplicación y se colocan app.propertiesen ese directorio.

Ver también:

Gavenkoa
fuente
0

También puede utilizar el soporte de URL JNDI para diferentes configuraciones de aplicaciones para pruebas, pruebas de integración y producción.

<Context>
...
<Resource auth="Container" factory="com.benasmussen.jndi.url.URLFactory" 
name="url/MyUrl" type="java.net.URL" url="file:///your/path/to/file"/>
...
</Context>

<jee:jndi-lookup id="myUrl" jndi-name="java:comp/env/url/MyUrl" expected-type="java.net.URL" />

Consulte el soporte de URL JNDI de Tomcat del proyecto GitHub para habilitar el soporte de URL JNDI para servidores Tomcat.

Ben Asmussen
fuente
0

paso 1: context.xml

    <Context path="/projectname">
  <Resource auth="Container" 
            driverClassName="com.mysql.jdbc.Driver"
            logAbandoned="true" 
            maxActive="100" ``
            maxIdle="30" 
            maxWait="10000" 
            name="refname" 
            removeAbandoned="true" 
            removeAbandonedTimeout="60" 
            type="javax.sql.DataSource" 
            url="jdbc:mysql://localhost:8080/dbname" 
            username="root"
            password="root"/>
</Context>

Paso 2: web.xml

<resource-ref>
        <description>DB Connection</description>
        <res-ref-name>refname</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>

Paso 3: cree una clase para conectarse

Connection connection = null;        
            Context context = (Context) new InitialContext().lookup("java:comp/env");
            DataSource ds = (DataSource) context.lookup("refname");
            connection = ds.getConnection();

Todo esta listo

tamil arasan
fuente