#Value Annotation and a Database PropertyPlaceHolderConfigurer - java

I've got two PropertyPlaceHolderConfigurer in my Spring XML. The first one obtains application properties from a file. The second one obtains user properties from database and looks like this:
<myConfiguration>
<bean id="databaseProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="properties">
<bean class="org.apache.commons.configuration.ConfigurationConverter"
factory-method="getProperties">
<constructor-arg>
<ref bean="propertiesSource" />
</constructor-arg>
</bean>
</property>
</bean>
<bean id="propertiesSource" class="org.apache.commons.configuration.DatabaseConfiguration">
<constructor-arg type="javax.sql.DataSource" ref="tomcatDataSource" />
<constructor-arg value="application_properties" />
<constructor-arg value="PROPERTY_KEY" />
<constructor-arg value="PROPERTY_VALUE" />
</bean>
<bean id="propertiesService" class="com.xxx.PropertiesServiceImpl">
<property name="propertiesSource" ref="propertiesSource"></property>
</bean>
<myConfiguration>
It works properly and I can access to these properties injecting 'propertiesService' like:
#Autowired
private PropertiesService propertiesService;
Which is:
public class PropertiesServiceImpl implements PropertiesService {
#Autowired
private DatabaseConfiguration propertiesSource;
private Properties properties;
#Override
public String getProperty(String key) {
if (properties == null) {
properties = ConfigurationConverter.getProperties(propertiesSource);
}
return properties.getProperty(key);
}
#Override
public Properties getProperties() {
if (properties == null) {
properties = ConfigurationConverter.getProperties(propertiesSource);
}
return properties;
}
The problem is that I like to use #Value annotation but it does not work.
I've tried:
private #Value("#{propertiesService.helloWorld}") String helloWorld;
And, of course, the property exists and is reachable through 'propertiesService' but it results in the next error:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 18): Property or field 'helloWorld' cannot be found on object of type 'com.infraportal.model.properties.PropertiesServiceImpl' - maybe not public?
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:224)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:94)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:46)
at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:374)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:88)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:120)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:242)
at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:161)
Which makes me think that Spring is looking for a 'getHelloWorld()' method instead using 'getProperty(String key)'
Any suggestion?

You need to explicitly specify which method is to be invoked, on bean referred in EL, and supply the argument value as below
#Value("#{propertiesService.getProperty('helloWorld')}")
private String helloWorld;

#Value: It is used for expression-driven dependency injection.
If you want to use with below example.
sample code
#Configuration
#PropertySource("classpath:jdbc.properties")
public class AppConfig {
#Value("${jdbc.driverClassName}")
private String driverClassName;
#Value("${jdbc.url}")
private String jdbcURL;
#Value("${jdbc.username}")
private String username;
#Value("${jdbc.password}")
private String password;
.....
..
}

Related

How to connect Two Database MySQL and MongoDB in the same project? Is it possible?

Currently I'm using Hibernate(MySQL) with Spring, the configuration is running fine for me, but once I configured another configuration mongo-config.xml file and trying to run a test case with mongodb it's showing Error creating bean with name .... from first configuration.
Below is my mongo-config.xml
<context:annotation-config />
<context:component-scan base-package="com.test.mongo" />
<context:property-placeholder location="classpath:mongo-dao.properties" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
<bean id="mongoDbFactory" class="org.springframework.data.mongodb.core.MongoFactoryBean">
<property name="driverClassName" value="${spring.datasource.driverClassName}" />
<property name="host" value="${spring.data.mongodb.host}" />
<property name="port" value="${spring.data.mongodb.port}" />
<property name="databaseName" value="${spring.data.mongodb.database}" />
and my first configuration for hibernate is looks like something
<context:component-scan base-package="com.hb.dao" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db.jdbc.driverClassName}" />
<property name="url" value="${db.jdbc.url}" />
<property name="username" value="${db.jdbc.username}" />
<property name="password" value="${db.jdbc.password}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>com.hb..dao.domain.entity</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql:false}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql:false}</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
And the stack trace is
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:101)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:331)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:213)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:290)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'accessProfileDaoImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.hibernate.SessionFactory com.soe.dao.AbstractDao.sessionFactory; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.hibernate.SessionFactory] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations:
Here is my test Class-
public class MongoQuestionsTest extends BaseDaoMongoTest{
private static final Logger logger = LogManager.getLogger(MongoQuestionsTest.class);
#Autowired
private MongoTestDao mongoTestDaoImpl;
#Test
public void saveQuestions(){
MongoQuestions mongoQuestions = new MongoQuestions();
mongoQuestions.setUsername("Hi");
mongoQuestions.setPassword("Hello");
mongoTestDaoImpl.save(mongoQuestions);
logger.debug("Mongo User Set with id " + mongoQuestions.getId());
}
and **BaseDaoMongoTest**---
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:/mongo-config-test.xml"})
public class BaseDaoMongoTest {
}
And in MongoTestDaoImpl class I just Auto-wired MongoTemplate and calling save() method that's it.
I'm not sure whether this is the best solution. But, it worked for me.
If you have two relational databases using Jpa module, then I would suggest you to create entity and transaction manager beans to read each datasource config. Refer the below link for the above use case.
springboot always read data from primary datasource
As you wish to have a combination of SQL and NoSQL, I would create entity and transcation manager beans for MySQL database as it works well with Jpa. And leave as-is configuration for Mongo(configs read directly from application.properties).
MySQLConfiguration datasource config class :
#Configuration
#PropertySource("classpath:persistence-multiple-db.properties")
#EnableJpaRepositories(basePackages = "com.springdata.dao.mysql", entityManagerFactoryRef = "mysqlEntityManager", transactionManagerRef = "mysqlTransactionManager")
public class MySQLConfiguration {
#Autowired
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean mysqlEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(myMySQLDataSource());
em.setPackagesToScan(new String[] { "com.springdata.models" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
#Primary
public DataSource myMySQLDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.mysql.jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("spring.mysql.jdbc.url"));
dataSource.setUsername(env.getProperty("spring.mysql.user"));
dataSource.setPassword(env.getProperty("spring.mysql.pass"));
return dataSource;
}
#Bean
#Primary
public PlatformTransactionManager mysqlTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(mysqlEntityManager().getObject());
return transactionManager;
}
Above datasource config params are read from classpath:persistence-multiple-db.properties file in the classpath.
# mysql jdbc connections
spring.mysql.jdbc.driverClassName=com.mysql.jdbc.Driver
spring.mysql.jdbc.url=jdbc:mysql://localhost:3306/test?autoReconnect=true&useSSL=false
spring.mysql.user=root
spring.mysql.pass=password1
# hibernate.X
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
The above configuration, should be suffice to deal MySQL datasource. To have mongo configuration in your project, add the below lines to application.properties.
# mongo configuration
spring.data.mongodb.uri=mongodb://localhost
spring.data.mongodb.database=test
Springboot will automatically create the necessary mongo datasource beans and keeps them readily available for spring container to use.
Now, create repository interfaces for both MySQL and Mongo datasources.
MyMongoRepository interface:
#Transactional
public interface MyMongoRepository extends MongoRepository<Users, String>{
}
MySQLRepository interface:
#Transactional
public interface MySQLRepository extends JpaRepository<Users, String>{
}
Users pojo class :
#Entity
#Table(name = "users")
#Document(collection="users")
#Data
public class Users {
#Id
#javax.persistence.Id
private String id;
private String name;
private Integer age;
}
Have added below annotations to enable mongorepositoreis and for component scan to springboot main class.
#SpringBootApplication
#ComponentScan(basePackages = { "com.springdata" })
#EnableMongoRepositories(basePackages={"com.springdata.dao.mongo"})
public class SpringbootmysqlmongoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootmysqlmongoApplication.class, args);
}
}
Finally, some code to test.
MyRepositoryImpl class:
#Service
public class MyRepositoryImpl {
#Autowired
private MyMongoRepository myMongoRepository;
#Autowired
private MySQLRepository mySQLRepository;
#PostConstruct
public void extractUsers(){
myMongoRepository.findAll().forEach((user) -> System.out.println("user name from mongo is : "+user.getName()));
mySQLRepository.findAll().forEach((user) -> System.out.println("User name from mysql is : "+user.getName()));
}
}
Have created users table in mysql test database and users collection in mongo test database.
Lastly, have uploaded my code to git repository.
https://github.com/harshavmb/springbootmysqlmongo

Use properties of other property placeholder in own property placeholder configurer

I try to implement an own PropertyPlaceholderConfigurer which uses properties of an other PropertyPlaceholderConfigurer in the constructor. I tried doing it like this.
<!-- load properties which are used in second configurer -->
<context:property-placeholder
location="classpath:config.properties" ignore-unresolvable="true" order="1" />
<bean class="com.example.MyPropertyPlaceholderConfigurer">
<!-- use property of other file -->
<constructor-arg value="${password}" />
<property name="location">
<value>file:config.properties</value>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
config.properties
password=1234
But the property ${password} in the constructor of MyPropertyPlaceholderConfigurer is not resolved.
What is my mistake?
I post hereby my solution I came up with to overcome this problem.
I implemented a single PropertyPlaceholderConfigurer which loads all properties and adds special functionality to some properties. This way inside the PropertyPlaceholderConfigurer you are able to use properties which are defined in the loaded property files.
config.properties
password=1234
special.properties
user={SPECIAL}name
spring config:
<bean
class="com.example.MyPropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:config.properties</value>
<value>classpath:special.properties</value>
</list>
</property>
</bean>
PropertyPlaceholderConfigurer:
public class MyPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private final static String PROPERTY_PREFIX = "{SPECIAL}";
protected Properties properties;
#Override
protected void convertProperties(Properties props) {
this.properties = props;
super.convertProperties(props);
}
#Override
public String convertPropertyValue(String originalValue) {
if (originalValue.startsWith(PROPERTY_PREFIX)) {
return convert(originalValue.substring(PROPERTY_PREFIX.length()));
}
return originalValue;
}
protected String convert(String value){
// access properties from config.properties
String pw = this.properties.getProperty("password");
// use properties and do what you need to do
return value + pw;
}
}
Maybe it helps somebody.

Spring annotation based SAP connector

I'm trying to move from a xml based config to java annotations
I need your help getting this to work:
Obviously I can't set the RemoteJco interface to my SapConnector but what can I do to get this xml-config working?
#Bean
public RmiProxyFactoryBean jcoPool(){
RmiProxyFactoryBean jcoPool = new RmiProxyFactoryBean();
jcoPool.setServiceUrl("rmi://localhost/CH");
jcoPool.setServiceInterface(RemoteJco.class);
jcoPool.setRefreshStubOnConnectFailure(true);
return jcoPool;
}
#Bean
public SapConnector SapConnector(){
SapConnector sapConnector = new SapConnector();
sapConnector.setJcoPool(jcoPool());
return sapConnector;
}
this in the XML-Config works just fine:
<!-- JCO-Pool RMI Service -->
<bean id="jcoPool" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://localhost/CH"/>
<property name="serviceInterface" value="com.itensis.jco.common.RemoteJco"/>
<property name="refreshStubOnConnectFailure" value="true" />
</bean>
<bean id="SapConnector" class="com.itensis.core.SapConnector">
<property name="jcoPool">
<ref bean="jcoPool" />
</property>
</bean>
this is my SAP-Connector
#Service
public class SapConnector {
#Autowired private RemoteJco jcoPool;
public RemoteJco getJcoPool() {
return jcoPool;
}
public void setJcoPool(RemoteJco jcoPool) {
this.jcoPool = jcoPool;
}
}
You have to make some changes on the jcoPool bean:
#Bean
public RemoteJco jcoPool(){
RmiProxyFactoryBean jcoPool = new RmiProxyFactoryBean();
jcoPool.setServiceUrl("rmi://localhost/CH");
jcoPool.setServiceInterface(RemoteJco.class);
jcoPool.setRefreshStubOnConnectFailure(true);
jcoPool.afterPropertiesSet();
return (RemoteJco) jcoPool.getObject();
}
Make sure that you return value has the same class as you used as service interface. And you have to call afterPropertiesSet() before calling getObject on the RmiProxyFacotoryBean instance.

Instantiating an object from bean using constructor args and refs

I've a class which takes in 2 Object Injections. 1 of them is to be injected through other bean ref whereas the other injected based on bean call. I want to instantiate an object using spring. How can I do this ?
I tried doing this:
MyBean Class:
class MyBean{
Injection1 ijn1;
MyBean(Injection1 ijn1,Injection2 ijn2){
this.ijn1=ijn1;
this.ijn2=ijn2;
}
}
Beans.xml
<bean name="myBean" class="MyBean" scope="prototype">
<constructor-arg>
<null />
</constructor-arg>
<constructor-arg>
<ref bean="injection2" />
</constructor-arg>
</bean>
<bean name="injection2" class="Injection2">
</bean>
Application Code:
MyBean getMyBean(Injection ijn1) {
return (MyBean)context.getBean("myBean", new Object[] { ijn1 })
}
But this doesn't works.
Any tips ?
You code doesn't work because spring looks for a MyBean's constructor like MyBean(Injection1 ijn1); you have to pass injection2 in this way.
MyBean getMyBean(Injection ijn1) {
return (MyBean)context.getBean("myBean", new Object[] { ijn1, context.getBean("injection2") })
}
If you want to use your code another way is to have partial inject in this way:
class MyBean{
Injection1 ijn1;
Injection2 ijn2;
MyBean(Injection1 ijn1){
this.ijn1=ijn1;
}
public void setIjn2(Injection2 ijn2I ) {
this.ijn2 = ijn2;
}
}
and in xml
<bean name="myBean" class="MyBean" scope="prototype">
<property name="inj2" ref="injection2" />
</bean>
<bean name="injection2" class="Injection2">
</bean>

NPE when injection class in spring

I have problem when injection class.
In my configuration I have one class which setting level of login and then one variable for setting level:
<bean id="config" class="..." init-method="init">
<property name="log4jConfig" ref="log4j" />
<property name="levelLogging" value="9" />
</bean>
and code:
public void setlevelLogging(Integer level) {
if (level == null) {
set0();
} else {
switch (level) {
case 0:
set0();
break;
case 9:
set9();
}
}
this.level = level;
}
private void set0() {
log4jConfig.setLoggerLevel("org.springframework.ws.server.MessageTracing", "OFF");
log4jConfig.setLoggerLevel("org.app", "INFO");
log4jConfig.setLoggerLevel("org.springframework.ws.client.MessageTracing", "OFF");
}
public void setLog4jConfig(Log4jConfigJmx log4jConfig) {
this.log4jConfig = log4jConfig;
}
when I want to run this code I got NPE because log4jConfig is null when is setlevelLogging calling.
How I can solve this exception ?
now I exclude this class from properties and creating new class in configClass:
Log4jConfigJmx log4jConfig = new Log4jConfigJmx()
but I dont think this is good idea
EDIT:
I try example below but I have still some problem:
first I got this exception:
[ERROR] java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
because I am using transactional and AOP so I add default constructor to the class so I have two of them:
public Config() {
}
public Config(Log4jConfigJmx log4jConfig, Integer level) {
this.log4jConfig = log4jConfig;
setlevelLoggin(level);
}
setlevelLogging ...
<bean id="config" class="..." init-method="init">
<constructor-arg index="0" ref="log4j" />
<constructor-arg index="1" value="9" />
</bean>
but now I have still NPE
pls help
You should place the code from the method setLoggingLevel in the init method.
Only leave this.level = level so it is a plain setter.
The init method is called after all the properties have been set.
----EDIT after comment----
After you comment I suggest you use a constructor:
public Class(Integer level, Log4jConfigJmx log4jConfig){
this.log4jConfig = log4jConfig;
setLevelLogging(level);
}
<bean id="config" class="..." init-method="init">
<constructor-arg index="0" value="9"/>
<constructor-arg index="1" ref="log4j"/>
</bean>
You can use constructor-args like this:
<bean id="config" class="..." init-method="init">
<constructor-arg><ref bean="anotherExampleBean"/></constructor-arg>
<constructor-arg type="int"><value>9</value></constructor-arg>
</bean>
Then your constructor can initialise both variables whilst keeping your setter methods in place.
E.g.
public MyClass(Log4jConfigJmx log4jConfig, Integer level) {
this.log4jConfig = log4jConfig;
this.setLevelLogging(level);
}
You can also try making the log4jConfig autowired.
Change your bean config back to:
<bean id="config" class="..." init-method="init">
<property name="levelLogging" value="9" />
</bean>
And then simply annotate a field in your class to be autowired:
#Autowired
private Log4jConfigJmx log4jConfig;
Add this at the top of your spring context to enable annotations:
<context:annotation-config />

Categories