Read Environment Variables in persistence.xml file - java

I want to read environment variables inside persistence.xml file.
Idea is that i don't want my database details to be read from properties file as there is a change of getting properties file override.Instead i want to read details from environment variables.
Is there any way to achieve this criteria.
Iam using Spring 3 my standalone application will be deployed in unix machine.

You can update properties in a persistence unit by supplying a Map (see this).
Conveniently, environment variables can be retrieved as a Map (see this).
Put the two together and you can dynamically update properties in your persistence unit with environment variables.
EDIT: simple example...
persistence.xml...
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
<provider>
oracle.toplink.essentials.PersistenceProvider
</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="toplink.logging.level" value="INFO"/>
<property name="toplink.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
<property name="toplink.jdbc.url" value="jdbc:oracle:thin:#myhost:l521:MYSID"/>
<property name="toplink.jdbc.password" value="tiger"/>
<property name="toplink.jdbc.user" value="scott"/>
</properties>
</persistence-unit>
code that updates persistence.xml "default" unit with environment variables...
Map<String, String> env = System.getenv();
Map<String, Object> configOverrides = new HashMap<String, Object>();
for (String envName : env.keySet()) {
if (envName.contains("DB_USER")) {
configOverrides.put("toplink.jdbc.user", env.get(envName)));
}
// You can put more code in here to populate configOverrides...
}
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("default", configOverrides);

I don't think this will cover EMs created via injection. Worse, I think EMs created through EMF can only be EXTENDED (eg equivalent to the annotation #PersistenceContext(type = PersistenceContextType.TRANSACTION) opposed to EXTENDED) so that if one requires a transaction EM, one must use injection.
I'm wondering if its possible to physically rewrite the persistence.xml file at runtime. Problem one being, ability to rewrite the file (permissions, being able to get to it in META-INF etc), and second, rewriting it before its opened for the first time by JPA (which I thinking happens the first time an injected EM field is actually referenced by application code)

You could use this working example.
It gets all properties defined in the persistence.xml from the PersistenceUnitInfo instance which is obtained from the EntityManagerFactory (by using eclipseLink specific implementations). These properties get replaced with the values defined in environment variables.

Related

Change hibernate JPA properties at runtime

I usually use a persistence.xml to configure hibernate, via properties like
<properties>
<property name="javax.persistence.lock.timeout" value="90000"/>
<property name="javax.persistence.query.timeout" value="90000" />
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServer2012Dialect" />
<!-- ... -->
However, I need to change one property at runtime (more specifically I need to adjust the value of javax.persistence.query.timeout at runtime). Therefore I tried configuring the session manually in situations where I need non-default properties, like that:
Configuration config = new Configuration();
config.addResource("persistence.xml");
config.setProperty("javax.persistence.query.timeout", "100000");
Session session = config.buildSessionFactory().getCurrentSession();
However, this yields the following exception:
org.hibernate.boot.MappingNotFoundException: Mapping (RESOURCE) not found : persistence.xml : origin(persistence.xml)
Which makes sense, as the persistence.xml isn't a normal hibernate resource file. So how do I set the configuration on the basis of the persistenc.xml (I don't want to configure all the properties twice)? Or more generally, how do I reconfigure hibernate at runtime?
Note that this is similar to, but not duplicating (as it's more specific), this post.
It can be overridden/set per query:
query.setHint("javax.persistence.query.timeout", 5000); // 5 seconds
If your query object is of type org.hibernate.Query you can do:
query.setTimeout(5);
https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/Query.html#setTimeout(int)
Changing the properties in the EntityManagerFactory at runtime (To affect all queries) will not change the configuration in effect.
You can create a new EntityManagerFactory altogether if you want to, described here:
Changing Persistence Unit dynamically - JPA

MyBatis - defining a global parameter

First the problem: I'm using XML-defined queries and the SQL contains database name as part of a table name. For example: SELECT * from mydb.bar. Unfortunately, databases are created/named all over the place and mudb part is really dynamic and can change at any moment. So I wanted to replace it with a property so it would look like SELECT * FROM ${dbname}.bar and then I defined the following section in mybatis-config.xml:
<properties>
<property name="dbname" value="mydb"/>
</properties>
But when I run the query ${dbname} evaluates to null. Same happens if I define this property in the properties file. I would hate to pass this as part of the each call parameters since this is truly a global property. Can this be done? And if yes - how?
Yes, you can! This is kind of a weird undocumented feature maybe. When building your Configuration object, do something like this. (org.apache.ibatis.session.Configuration)
configuration.getVariables().put("global_param", "123");
Then in your XML map, you can reference.
select * from ${global_param}
I had the same issue using Spring+MyBatis, and solved it by setting 'configurationProperties' using my Spring XML definition for sqlSessionFactory. My example below shows how to set a custom global property named 'encryptionKey', with a value which you can either hard-code in the XML file, or load from an external file using the context:property-placeholder tag (as below).
<context:property-placeholder location="/WEB-INF/spring/config-datasource.properties" />
<beans:bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="typeAliasesPackage" value="com.example.model" />
<beans:property name="configurationProperties">
<beans:props>
<beans:prop key="encryptionKey">${jdbc.encryptionKey}</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
I was using an XML configuration but not Spring and set a property inside the Configuration object but discovered that had to be done before the mapper files are loaded (see here). I abandoned the Configuration object approach and went with this approach, which worked for me:
Reader reader = Resources.getResourceAsReader("..../mybatis-config.xml");
Properties properties = new Properties();
properties.setProperty("dbname", "mydb");
SqlSessionFactory.factory = new SqlSessionFactoryBuilder().build(reader, "development", properties);
Then, as Andy Pryor posted, use select * from ${dbname} in the XML mapper.

How to configure embedded OpenEJB container for tests properly?

This is my SLSB:
#Stateless
public class MyService {
PersistenceContext(unitName = "abc")
EntityManager em;
public boolean exists(int id) {
return this.em.find(Employee.class, id) != null;
}
}
This is my persistence.xml (I'm using Glassfish v3):
<persistence>
<persistence-unit name="abc">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/MyDS</jta-data-source>
<properties>
<property name="hibernate.archive.autodetection" value="class" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQLInnoDBDialect" />
</properties>
</persistence-unit>
</persistence>
Now I'm trying to create a test, using OpenEJB embedded container. This is my test class:
class MyServiceText {
#Test
public void testChecksExistence() throws Exception {
Properties properties = new Properties();
properties.setProperty(
javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory"
);
InitialContext ic = new InitialContext(properties);
// actual testing skipped
}
}
I would like to use HSQL for testing. How can I instruct OpenEJB that my persistence unit "abc" has to point to HSQL during testing? Shall I create a new version of persistence.xml? Shall I use openejb.xml? I'm lost in their examples and documentation.. :(
It's a Maven-3 project.
I would suggest placing a file named jndi.properties in src/test/resources for your OpenEJB configuration. This will then be available in the test classpath, you can then use the no-argument contructor of InitialContext to lookup datasources and ejbs. An example configuration looks like this, I'm using mysql for my datasource:
java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory
myDS=new://Resource?type=DataSource
myDS.JdbcDriver=com.mysql.jdbc.Driver
myDS.JdbcUrl=jdbc:mysql://127.0.0.1:3306/test
myDS.JtaManaged=true
myDS.DefaultAutoCommit=false
myDS.UserName=root
myDS.Password=root
OpenEJB should then automatically replace the reference in persistence.xml with this datasource, if this is the only datasource then this should work even if the names are different.
Edit: Persistence unit settings
According to the documentation you referenced it should also be possible to configure persistence unit properties through jndi.properties:
abc.hibernate.hbm2ddl.auto=update
abc.hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
I haven't tested this myself since I'm using mysql for both tests and normal executions, only with different database names. Please let me know if this works, I've been thinking about replacing mysql in my testcases too.

Why I have to declare each and every class in my hibernate.cfg.xml when using annotations?

Why it isn't enough to set the #Entity annotation?
Am I missing the point here e.g. performance?
The annotation is not enough because hibernate does not know where your annotated classes live without some sort of explicit declaration. It could, in theory, scan every single class in the classpath and look for the annotation but this would be very very expensive for larger projects.
You can use spring which has a helper that can allow you to specify the package(s) that your hibernate objects are in and it will just scan these packages for #Entity. If you have all your objects in a small number of fixed packages this works well.
E.g.
<bean id="referenceSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<array>
<value>com.xxx.hibernate.objects</value>
</array>
</property>
</bean>
The above is the Spring declaration. If you aren't familiar with the above syntax you can just construct it programmatically.
AnnotationSessionFactoryBean sfb = new AnnotationSessionFactoryBean();
sfb.setDataSource( ds );
sfb.setHibernateProperties( hibProps);
sfb.setPackagesToScan( ... );
sfb.initialise();
SessionFactory sf = sfb.getObject();
It supports a bunch of config options so you can use raw properties or pass in a pre-config'd datasource.
You don't if you set hibernate.archive.autodetection property to true. There is a performance issue here as Hibernate will search the jar files for the JPA annotation. Note that, it will also initializes those classes.
Yes :)
The hibernate.cfg.xml file is not used to specify your entities, it's used to configure things like hibernate's connection parameters and global settings. The hibernate.cfg.xml file also contains instructions on how to locate the entities. You can list the XML mapping files using <mapping resource=XYZ>, but if you're using JPA annotations like #Entity, then this is unnecessary, Hibernate will auto-detect them.
You can mix annotations and mapping XML if you choose, but that's by no means necessary in most situations.
By default all properly annotated
classes and all hbm.xml files found
inside the archive are added to the
persistence unit configuration. You
can add some external entity through
the class element though.
Hibernate EntityManager Reference Docs

Load environment-specific properties for use with PropertyPlaceholderConfigurer?

This seems like a pretty common problem, but I haven't found any sort of consensus on the best method, so I'm posing the question here.
I'm working on a command-line Java application using Spring Batch and Spring. I'm using a properties file along with a PropertyPlaceholderConfigurer, but I'm a little unsure of the best way of handling the properties files for multiple environments (dev, test, etc.). My Googling is only turning up programmatic ways of loading the properties (i.e., in the Java code itself), which doesn't work for what I'm doing.
One approach I've considered is simply placing each environment's properties file on the server and adding the file's directory to the classpath via a command-line argument, but I've been having trouble loading the file using that method.
The other method I'm considering is to just include all the properties files in the jar and use a system property or command line argument to fill in the name of the properties file at runtime, like this:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:job.properties.${env}</value>
</list>
</property>
</bean>
I lean towards the latter solution, but I'm also looking to see if there's a better method I'm overlooking.
I should also mention that I have to make the substitution at runtime rather than in the build. The process I'm constrained to use requires a single build which will be promoted through the environments to production, so I'm unable to use substitution ala Maven or Ant.
Essentially you have a finished JAR which you want to drop into another environment, and without any modification have it pick up the appropriate properties at runtime. If that is correct, then the following approaches are valid:
1) Rely on the presence of a properties file in the user home directory.
Configure the PropertyPlaceholderConfigurer to reference a properties file external to the JAR like this:
<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>
The operating system will secure the contents of the application.properties file so that only the right people can have access to it. Since this file does not exist when you first run up the application, create a simple script that will interrogate the user for the critical values (e.g. username, password, Hibernate dialect etc) at start up. Provide extensive help and sensible default values for the command line interface.
2) If your application is in a controlled environment so that a database can be seen then the problem can be reduced to one of creating the basic credentials using technique 1) above to connect to the database during context startup and then performing substitution using values read via JDBC. You will need a 2-phase approach to application start up: phase 1 invokes a parent context with the application.properties file populating a JdbcTemplate and associated DataSource; phase 2 invokes the main context which references the parent so that the JdbcTemplate can be used as configured in the JdbcPropertyPlaceholderConfigurer.
An example of this kind of code would be this:
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;
}
}
The above would then be configured in Spring like this (note the order property comes after the usual $ prefixed placeholders):
<!-- 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>
This would allow the follow to occur in the Spring configuration
<!-- 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) Of course, if you're in a web application container then you just use JNDI. But you're not so you can't.
Hope this helps!
You could use <context:property-placeholder location="classpath:${target_env}configuration.properties" />
in your Spring XML and configure ${target_env} using a command-line argument (-Dtarget_env=test.).
Starting in Spring 3.1 you could use <context:property-placeholder location="classpath:${target_env:prod.}configuration.properties" /> and specify a default value, thereby eliminating the need to set the value on the command-line.
In case Maven IS an option, the Spring variable could be set during plugin execution, e.g. during test or integration test execution.
<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>
I assume different Maven profiles would also work.
Spring Property Placeholder Configurer – A few not so obvious options
<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}}: Here "mode" defines the project mode (environment) - dev / prod
Properties file looks like:
#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
I agree - it should not be a build time configuration as you want to deploy the exact same payload to the various contexts.
The Locations property of PropertyPlaceHolderConfigurer can take various types of resources. Can also be a filesystem resouce or a url? Thus you could set the location of the config file to a file on the local server and then whenever it runs it would run in the mode specified by the config file on that server. If you have particular servers for particular modes of running this would work fine.
Reading between the lines though it seems you want to run the same application in different modes on the same server. What I would suggest in this case is to pass the location of the config file via a command line parameter. It would be a little tricky to pass this value into the PropertyPlaceHolderConfigurer but would not be impossible.
The way I've normally done this in the past is to perform a substitution of the environment (dev/test/prod) in some sort of way at package/deployment time.
That can either copy the correct config file to the right location on the server or just bundle the correct config file in the deployment package. If you use Ant/Maven this should be fairly straightforward to achieve. Which build tool are you using? Ant/Maven, that should provide you with the ability to substitute a value.
Another alternative, which use PropertyPlaceholderConfigurer is that of the SYSTEM_PROPERTIES_MODE_OVERRIDE property. You can use this to set the location of the properties file you wish to load through a system property, see:
http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html#SYSTEM_PROPERTIES_MODE_OVERRIDE
Hope that helps.
For build time substitution I use Maven build properties for variable substitution. You can determine what properties to load in your Maven settings.xml file and the file could be specific to the environment. For production properties using PPC see this blog
Hi after reading Spring in Action found a solution provided by Spring.
Profile Or Conditional : you can create multiple profile eg. test, dev, prod etc.
Spring honors two separate properties when determining which profiles are active:
spring.profiles.active and spring.profiles.default . If spring.profiles.active
is set, then its value determines which profiles are active. But if spring
.profiles.active isn’t set, then Spring looks to spring.profiles.default . If neither
spring.profiles.active nor spring.profiles.default is set, then there are no
active profiles, and only those beans that aren’t defined as being in a profile are created.
There are several ways to set these properties:
1 As initialization parameters on DispatcherServlet
2 As context parameters of a web application
3 As JNDI entries
4 As environment variables
5 As JVM system properties
6 Using the #ActiveProfiles annotation on an integration test class
I use the classpath option and adjust the classpath per environment in Jetty. In the jetty-maven-plugin you can set a directory for testclasses and have your testresources located there.
For non-local environments (test / production) I use an environment flag and send the appropriate files to the $JETTY_HOME/resources folder (which is built into Jetty's classpath)

Categories

Resources