Loading different properties files into persistence.xml in a maven spring project - java

I have a scenario where I have to load different dialect,provider into my persistence.xml file based upon the environment I am deploying the application.
For Eg
In one environment I am using Oracle11g and in another I am using MySql8.
I want my persistnece.xml to look something like this.
<persistence-unit name="firstPU" transaction-type="RESOURCE_LOCAL">
<provider>${somekey.provider}</provider>
<properties>
<property name="hibernate.dialect" value="${somekey.dialect}" />
</properties>
</persistence-unit>
Then have two separate property files(first.property, second.property) and select either one of them using build profile in my pom.xml. For Eg-
<profile>
.
.
.
<build>
<resources>
<resource>
<directory>src/main/resources/config/${build.profile.id}</directory>
<excludes>
<exclude>**/first.properties</exclude>
</excludes>
</resource>
</resources>
</build>
.
.
.
</profile>
So based upon the profile selected it will exclude one of the .property files and read from the other.
The issue with all this is the values are returning as null from the property files. (not anymore)
Am I missing something here or is there a better way to do this sort of stuff ?
Update--
this is working fine for reading dialect values. However, I can't read Provider !!
Is it possible to read Provider value as well from a property file ?

Ditch the persistence.xml and configure the LocalEntityManagerFactory with Spring using placeholders. This way you can simply add a properties file and change the content, without needing to recreate the artifact.
Create an application.properties file (or whatever name you like) and add the following
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
Then, assuming you are using java based configuration, add an #PropertySource to (optionally) load this file from an external location.
#Configuration
#PropertySource("file:/conf/application.properties")
public class MyConfiguration {
#Autowired
private Environment env;
#Bean
public LocalContainerEntityManagerFactory entityManagerFactory(DataSource ds) {
LocalContainerEntityManagerFactory emf = new LocalContainerEntityManagerFactory();
emf.setDataSource(ds);
emf.setJpaVendorAdapater(jpaVendorAdapter());
// Other settings
return emf;
}
#Bean
public HibernateJpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabasePlatform(env.getRequiredProperty("spring.jpa.database-platform"));
return adapter;
}
}
Now you can change the dialect (and other properties if needed) to anything you like.
NOTE: the name of the property was chosen to be the same as the one from Spring Boot, so that you can reuse this configuration when switching to Spring Boot, which supports all of this out-of-the-box.

Instead of defining properties in persistence.xml, you can define all these database details in the properties file and in build script you can include respective property file in your war/ear.
final Properties persistenceProperties = new Properties();
InputStream is = null;
try {
is = getClass().getClassLoader().getResourceAsStream("persistence.properties");
persistenceProperties.load(is);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ignored) {
}
}
}
entityManagerFactory = Persistence.createEntityManagerFactory("firstPU", persistenceProperties);
Refer this link for more details

Related

How to define a different connection pool by deployed application in glassfish

I am using glassfish 4 and java EE 7. I would need to define a connection pool that is different for each application I will deploy in glassfish.
I have one application (.war file) per client, and each client has its own user/password/schema in my mysql db, so the data is not shared between the clients.
I know how to define a connection pool in glassfish, but then all my applications can only use the same settings (I am using bonecp btw).
I would like to be able to change the user/password/schema for each application deployed. Is it possible to define entirely the connection pool in persistence.xml and not in glassfish, so I could have a different one within my different .war files?
With 10 .war files deployed (10 clients), I would like to have 10 different connection pools (different user/password/schema defined).
If you create the data source programmatically, then you can inject it to JPA without a need to declare it in your persistence.xml. Here's an example
Define the persistence-xml:
<persistence-unit name="foo-PU" transaction-type="RESOURCE_LOCAL">
<!-- the provider: Hibernate, EclipseLink or another -->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- No need to define any connection parameter nor the data source -->
<!-- class definitions here -->
</persistence-unit>
Defining a .properties file to store the configuration for the data source:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/foo_db
db.user=user
db.password=s3cr3t
db.bonecp.idleConnectionTestPeriod=60
db.bonecp.idleMaxAge=240
db.bonecp.maxConnections=10
# more properties...
Defining a class that will generate the data source
public class DataSourceGenerator {
public static DataSource getDataSource(String properties) {
Properties conf = new Properties();
try {
conf.load(
DataSourceFactory.class
.getClassLoader().getResourceAsStream(
properties));
} catch (IOException e) {
//handle the error
//naive handling shown here
e.printStacktrace();
}
BoneCPDataSource dataSource = new BoneCPDataSource();
//set the properties from the .properties file
dataSource.setDriverClass(conf.getProperty("db.driver"));
dataSource.setJdbcUrl(conf.getProperty("db.url"));
dataSource.setUsername(conf.getProperty("db.user"));
dataSource.setPassword(conf.getProperty("db.password"));
dataSource.setIdleConnectionTestPeriodInMinutes(
Long.parseLong(
conf.getProperty("db.bonecp.idleConnectionTestPeriod")));
dataSource.setIdleMaxAgeInSeconds(
Long.parseLong(
conf.getProperty("db.bonecp.idleMaxAge")));
dataSource.setMaxConnectionsPerPartition(
Integer.parseInt(
conf.getProperty("db.bonecp.maxConnections")));
//more properties to load...
return dataSource;
}
}
Create your EntityManagerFactory programmatically as well:
public class EntityManagerFactoryGenerator {
public static EntityManagerFactory createEMF() {
Map<String, Object> properties = new HashMap<>();
String dataSourceKey = "";
//uncomment here depending on your needs...
//using Hibernate
//dataSourceKey = org.hibernate.cfg.AvailableSettings.DATASOURCE;
//using EclipseLink
//dataSourceKey = org.eclipse.persistence
// .config.PersistenceUnitProperties.NON_JTA_DATASOURCE;
properties.put(
dataSourceKey,
DataSourceGenerator.getDataSource("mysql-con.properties"));
return Persistence.createEntityManagerFactory("foo-PU", properties);
}
}
Step 1. Go to Glassfish admin console to configure the JDBC connection details.
Inside Resources - JDBC create one pool for each set of connection details, then create a JDBC data source to each created pool.
Step 2. Go to your persistence file of each application and point to the right data source.
Don't specify the provider if you don't need anything special as Glassfish comes with EclipseLink already, which works fine.

Spring Config server add property at runtime

I want to add some property at runtime in spring config server and it should be available to all client applications with #Value annotation.
I wont have this property predefine because I am going calculate that value in spring config server and add to environment.
Can you please help me understand what is best way to achieve this.
Spring cloud configuration contains a feature named 'RefreshScope' which allows to refresh properties and beans of a running application.
If you read about spring cloud config, it looks like it can only load properties from a git repository, but that is not true.
You can use RefreshScope to reload properties from a local file without any need to connect to an external git repository or HTTP requests.
Create a file bootstrap.properties with this content:
# false: spring cloud config will not try to connect to a git repository
spring.cloud.config.enabled=false
# let the location point to the file with the reloadable properties
reloadable-properties.location=file:/config/defaults/reloadable.properties
Create a file reloadable.properties at the location you defined above.
You can leave it empty, or add some properties. In this file you can later, at runtime, change or add properties.
Add a dependency to
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
All beans, that are using properties, that may be changed during runtime, should be annotated with #RefreshScope like this:
#Bean
#RefreshScope
Controller controller() {
return new Controller();
}
Create a class
public class ReloadablePropertySourceLocator implements PropertySourceLocator
{
private final String location;
public ReloadablePropertySourceLocator(
#Value("${reloadable-properties.location}") String location) {
this.location = location;
}
/**
* must create a new instance of the property source on every call
*/
#Override
public PropertySource<?> locate(Environment environment) {
try {
return new ResourcePropertySource(location);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Configure Spring to bootstrap the configuration using that class.
Create (or extend) the META-INF/spring.factories file in your resource folder:
org.springframework.cloud.bootstrap.BootstrapConfiguration=your.package.ReloadablePropertySourceLocator
This bean will read the properties from the reloadable.properties. Spring Cloud Config will reload it from disk, when you refresh the application.
Add runtime, edit reloadable.properties as you like, then refresh the spring context.
You can do that by sending a POST request to the /refresh endpoint, or in Java by using ContextRefresher:
#Autowired
ContextRefresher contextRefresher;
...
contextRefresher.refresh();
This should also work, if you choose to use it in parallel to properties from a remote git repository.

Disable #Alternative classes

In my Java EE 7 program, I want to use #Alternative to inject different implementation depending on the context, production or test for example. What I did is to declare my class annotated with #Alternative in my beans.xml file. It works great and my alternative class is injected wherever I want instead of the default one. But I don't know if there is a way to skip this behavior and inject the default class other than removing the declaration in the beans.xml file. Which is not possible easily when the application is packaged. It would be great if I can choose if I want to use the default classes or the alternative ones in a configuration file, for example in my standalone.xml file of my WildFly server. Is this possible?
Elaborating on my comment, you can do the following.
Define a single qualifier
#Inherited
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, FIELD, METHOD, PARAMETER})
public #interface BeanSelector {
#NonBinding
private String environment;
}
Define an annotation literal
public class BeanSelectorImpl extends AnnotationLiteral<BeanSelector> implements BeanSelector {
private final String environment;
public BeanSelectorImpl(final String environment) {
this.environment = environment;
}
public String environment() {
return this.environment;
}
}
Create a producer that reads from the environment
#ApplicationScoped
public class BeanSelectorProducer {
#Any
#Inject
private Instance<MyBeanService> myBeanServices;
#Produces
#BeanSelector(environment = "")
public MyBeanService produceService() {
final String env = System.getProperty("env.property");
final BeanSelector beanSelector = new BeanSelectorImpl(env);
//You may wish to handle exceptions.
return myBeanServices.select(beanSelector).get();
}
}
The negative side of this implementation is that all your beans will be in service.
The other option of defining different beans.xml for every environment is probably a better option.
I'm afraid it's not possible to be achieved with the plain #Alternative annotation. See below a couple of approaches you could try:
Using different beans.xml files
You could consider having a different beans.xml files for each environment and then pack the right one according to, for example, a Maven profile.
Writing your own alternative stereotype
You can define your own alternative stereotype and manage the injection with a CDI extension.
This approach is mentioned in this post from NightSpawN. I tested it on WildFly 10 and it worked as expected. Find the steps below:
Define an enumeration with your environment types:
public enum EnvironmentType {
DEVELOPMENT, TESTING, STAGING, PRODUCTION;
}
Create your own #Alternative stereotype to hold meta-information about the environment:
#Stereotype
#Alternative
#Target(TYPE)
#Retention(RUNTIME)
public #interface EnvironmentAlternative {
EnvironmentType[] value();
}
And declare the alternative stereotype in the beans.xml:
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<stereotype>com.example.EnvironmentAlternative</stereotype>
</alternatives>
</beans>
For example purposes, let's define a sample service:
public interface GreetingService {
String sayGreeting();
}
Define a default implementation:
#Default
public class DefaultGreetingService implements GreetingService {
#Override
public String sayGreeting() {
return "Hey!";
}
}
Also define some alternative implementations using the #EnvironmentAlternative stereotype:
#EnvironmentAlternative(DEVELOPMENT)
public class DevelopmentGreetingService implements GreetingService {
#Override
public String sayGreeting() {
return "Hey from a development environment!";
}
}
#EnvironmentAlternative(PRODUCTION)
public class ProductionGreetingService implements GreetingService {
#Override
public String sayGreeting() {
return "Hey from a production environment!";
}
}
The #EnvironmentAlternative annotation also supports an array with multiple environment types:
#EnvironmentAlternative({ TESTING, STAGING })
Here's where the magic happens!
Create a CDI Extension to observe CDI lifecycle events. The processAnotated() method gets called for each annotated type the container processes, and if it is annotated with #EnvironmentAlternative and the current environemnt is not in the specified environments, event's veto() method is called, preventing the type from being processed any further:
public class EnvironmentAlternativesExtension implements Extension {
private EnvironmentType currentEnvironment = PRODUCTION;
public <T> void processAnotated(#Observes ProcessAnnotatedType<T> event) {
EnvironmentAlternative alternative =
event.getAnnotatedType().getJavaClass()
.getAnnotation(EnvironmentAlternative.class);
if (alternative != null && !containsCurrentEnvironment(alternative.value())) {
event.veto();
}
}
private boolean containsCurrentEnvironment(EnvironmentType[] environments) {
for (EnvironmentType environment : environments) {
if (environment == currentEnvironment) {
return true;
}
}
return false;
}
}
The default implementation will be used when no suitable alternatives are found.
Next, register the CDI extension as a service provider by creating a file named javax.enterprise.inject.spi.Extension under the META-INF/services folder. The content of the file will be just the canonical name of the extension class:
com.example.EnvironmentAlternativesExtension
Finally inject and use the above defined service:
#Inject
private GreetingService service;
String greeting = service.sayGreeting();
In a real application you won't hardcode the value of the currentEnvironment field. You could, for example, use a system property to determine the environemnt where the application is running.
To set a system property in the standalone.xml, use the <system-properties> tag under the <server> tag:
<server xmlns="urn:jboss:domain:4.2">
...
<system-properties>
<property name="environment" value="PRODUCTION"/>
</system-properties>
...
</server>
Then use the following piece of code to get the value of the environemnt variable and set the value of the currentEnvironment field:
String environment = System.getProperty("environment");
currentEnvironment = EnvironmentType.valueOf(environment);
In my opinion the simpliest solution was to create a maven profile like it's mentionned in some comments.
In my pom.xml file, I added a resources filtering section and a profile:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
<profiles>
<profile>
<id>default</id>
<properties>
<MyBean></MyBean>
</properties>
</profile>
<profile>
<id>alternative</id>
<properties>
<MyBean><![CDATA[<class>com.MyBean</class>]]></MyBean>
</properties>
</profile>
</profiles>
The beans.xml file is now like that:
<beans ...>
<alternatives>
${MyBean}
</alternatives>
</beans>
Finally I just need to execute the maven command with the useful profil: mvn package -P alternative.

Loading additional spring profiles from java config

Is there any possibility to load aditional spring profiles from java config?
I know that I can use -Dspring.profile.active argument and also add profiles to spring.profiles.include in application.properties.
What I need is to be able to activate profiles from java config. I've created PropertyPlaceholderConfigurer, where I'm adding some custom property files, which also contains property spring.profiles.include, all properties are load and it works ok, but spring doesn't activate any profiles which are inclded using this property.
#Bean
public static PropertyPlaceholderConfigurer ppc() throws IOException {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocations(new ClassPathResource("properties/" + property + ".properties"));
ppc.setIgnoreUnresolvablePlaceholders(true);
return ppc;
}
The active spring profiles are defined in properties via the following configuration: spring.profiles.active:.
You should list in all the files that you import the profiles that they activate via the above configuration key.
EDIT
First, as per the official documentation the configuration spring.profiles.include is more suitable for unconditionally adding active profiles.
Second, I can assume that PropertyPlaceholderConfigurer is not suitable for what you want to achieve. The official documentation lists the ways you can Externalize Configuration. You can try to use #PropertySource:
#PropertySources({
#PropertySource(value = "classpath:application.properties"),
#PropertySource(value = "classpath:other.properties", ignoreResourceNotFound = true)
})
public class Application {
...
}
}
Additionally, you can try to list the other properties files in property spring.config.location inside application.properties as described here.

how to add if else condtion in hibernate configuration file

I am working in spring hibernate project where I am connecting to multiple database. These database details are loaded in a properties file and is imported into hibernate.xml file. Each key was mapped as below.
dbName= dbHost
Mapping as below :
<bean id="dbId" parent="abstractDataSource">
<property name="url" value="${dbName}" />
</bean>
But now i need to include a condition such that the property key to be mapped to multiple values like below
E.g. dbName= statusFlag,dbHost,dbBackupHostName
I need to modify the hibernate configuration file such that using the keyName, corresponding values should be retrieved and splitted with comma separated.
Then I need to check for the statusFlag. If it is true, then i should set the dbValue to 'dbHost', else if the flag is false, it should be set to 'dbBackupHostName'.
Please help me how to implement this condition check in hibernate configuration file.
Also please let me know if this approach is fine to proceed or am i making it too difficult to implement?
Waiting for some valueable answers..
~~~Suriya
To use complex logic in bean wiring and creation in spring you can use Java Configuration
It should work for Spring 3. Sample pseudo code is below.
#Configuration
public class MyCustomConfig {
#Value
String statusFlag;
#Value
String dbHost;
#Value
String dbBackupHostName;
#Bean
public BasicDataSource datasource () {
BasicDataSource datasource = new BasicDataSource();
//Do some custom processing to create datasource
...
return datasource;//return after creation
}
}
Use PropertyPlaceholderConfigurer to load properties as #PropertySource is not available in 3.0.5.
If you can change the spring version to 3.1 which shouldn't make much difference then you can use #Profile along with #PropertySource.
If the logic is not too complex and you can separate set of properties that have to be active at a time. (Local db, Dev db, Prod db, Custom etc.) Then you can try using #Profile.
#Profile annotated bean is created only if that profile is active. A profile can be activated by setting spring.profiles.active.
So to activate the Dev profile we can set in the properties files
spring.profiles.active=dev
//activating 2 profiles
spring.profiles.active=dev,mvc
Below is the sample pseudo code.
#Configuration
#PropertySource("bootstrap.properties")//set spring.profiles.active in it
public class MyCustomConfig {
#Profile("profile1")
public BasicDataSource datasource1 () {
//config using ${profile1.dbName} etc
...
}
#Profile("profile2")
public BasicDataSource datasource2 () {
//config using ${profile2.dbName} etc
...
}
#Profile("profile3")
public BasicDataSource datasource3 () {
//config using ${profile3.dbName} etc
...
}
}

Resources