Resolving system environment variable using Spring Expression Language - spring

I want to resolve system environment variable using `Spring Expression Language' in spring servlet configuration file. My first approach was:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.#{systemEnvironment.THREAD_ENV}.properties" />
This is throwing below exception:
Caused by: org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 18): Field or property 'THREAD_ENV' cannot be found on object of type 'java.util.Collections$UnmodifiableMap'
Then I have tried:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.#{systemEnvironment['THREAD_ENV']}.properties" />
and
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.#{systemEnvironment[THREAD_ENV]}.properties" />
which both fail and resolve to empty string. I am using Tomcat6 and I export this variable just before restarting Tomcat server:
export THREAD_ENV=live;
/etc/init.d/tomcat6 restart;
Would like to mention that all three methods work on some of my Tomcat6 instances but not on all - what could be the reason of such a strange behaviour? Any idea what I am doing wrong?

The SpEL variable is systemProperties, not systemEnvironment.
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.#{systemProperties['THREAD_ENV']}.properties" />

Related

Grails database configuration

I'm trying to make some changes to the database configuration for a Grails application. Grails version is 2.5.3.
The goal is to remove hard coded dependencies to MySql to be able to use the application with other database providers.
I am also trying to run locally with environment set to prod and with a local MySql database. (To be able to test my changes without deploying, since the "dev" environment uses the H2 database which is set up quite different.) So I'm starting with mvn grails:run-app -Dgrails.env=prod.
I'm not very experienced with Grails and a lot of things seems to happen "magically" which makes troubleshooting a little difficult.
Some configuration files that seems to be involved are:
context.xml. As I understand it this is a tomcat configuration file on the server (outside the application). There is also a myapp/resources/tomcat-conf/context.xml which I believe is used when I'm running locally. context.xml contains a database configuration like this:
<Context>
...
<Resource name="jdbc/TP" auth="Container" type="javax.sql.DataSource"
maxActive="10" maxIdle="5" maxWait="10000" username="xxx" password="xxx"
driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://xxx:3306/xxx?autoReconnect=true" validationQuery="select 1"
/>
...
</Context>
Then there is a myapp/src/main/resources/myapp-PROD.xml
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" />
<property name="generateDdl" value="false" />
<property name="database" value="MYSQL" />
</bean>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/TP"/>
<property name="resourceRef" value="true" />
</bean>
I would like to get rid of the hardcoded "MYSQL" here and preferable be able to get that from context.xml (or a properties file on the server).
myapp-PROD.xml also imports myapp-config.xml which contains:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
Then there is some configuration in myapp/grails-app/conf/DataSource.groovy:
(Including some outcommented configuration)
environments {
...
production {
dataSource {
// driverClassName = "com.mysql.jdbc.Driver"
// username = "xxx"
// password = "xxx"
// url = "jdbc:mysql://xxx:3306/xxx"
jndiName = "java:comp/env/jdbc/TP"
}
}
}
And there is also myapp/grails-app/conf/BootStrap.groovy which does some H2 configuration for "dev" and "test" but no database configuration for "prod".
So I have tried to specify my local MySql database in context.xml. It does not work well. In myapp/target/tomcat/logs/myapp.log I can see this:
*2022-05-23 13:40:01,669 [localhost-startStop-1] DEBUG spring.OptimizedAutowireCapableBeanFactory - Invoking afterPropertiesSet() on bean with name 'dialectDetector'
2022-05-23 13:40:05,703 [localhost-startStop-1] WARN spring.GrailsWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManagerPostProcessor': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager': Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory': Cannot resolve reference to bean 'hibernateProperties' while setting bean property 'hibernateProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'hibernateProperties': Cannot resolve reference to bean 'dialectDetector' while setting bean property 'properties' with key [hibernate.dialect]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dialectDetector': Invocation of init method failed; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Error while extracting DatabaseMetaData; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (**Could not create connection to database server. Attempted reconnect 3 times. Giving up.**)*
I don't see any connection attempts in my database logs, so obviously it is not trying to connect to the database i specified in context.xml.
I also tried to specify the database in DataSource.groovy, but that didn't work either.
So:
What do I need to do to run locally with my local MySql database?
Is there any additional helpful logs I can enable?
How do I get rid of "MYSQL" in myapp-PROD.xml and get dialect from context.xml instead?
What do I need to do to run locally with my local MySql database?
You need to supply a value for the dataSource driver that points to the Mysql driver and you need to supply a JDBC url that the MySql driver recognizes and points to your local MySql.
How do I get rid of "MYSQL" in myapp-PROD.xml and get dialect from
context.xml instead?
The dialect is only 1 piece of the puzzle and probably really isn't the key to the problem. If it really is a requirement to get the dialect from context.xml I don't know how to do it, but a much more common way to deal with the situation is to either use JNDI so you don't have to make any mention of dialects and driver names and user names and passwords in your app. Another option is to put the database config in an external config file that is loaded by the app at startup time.

JNDI lookup failure

i would be grateful if someone can help me rectify the issue in my code. Not sure where I'm going wrong.
Currently my persistence.xml contains
<property name="hibernate.transaction.manager_lookup_class" value="#####.hibernate.JbossTSTransactionManagerLookup"/>
<property name="hibernate.current_session_context_class" value="jta"/>
along with
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="####"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:comp/env/TransactionManager"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
Context initialization failed : org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in class path resource [###XMLConfig.xml]: Invocation of init method failed; nested exception is org.springframework.transaction.TransactionSystemException: JTA TransactionManager is not available at JNDI location [java:comp/env/TransactionManager]; nested exception is org.springframework.jndi.TypeMismatchNamingException: Object of type [class com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple] available at JNDI location [java:comp/env/TransactionManager] is not assignable to [javax.transaction.TransactionManager]
Caused by: org.springframework.transaction.TransactionSystemException: JTA TransactionManager is not available at JNDI location [java:comp/env/TransactionManager]; nested exception is org.springframework.jndi.TypeMismatchNamingException: Object of type [class com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple] available at JNDI location [java:comp/env/TransactionManager] is not assignable to [javax.transaction.TransactionManager]
at org.springframework.transaction.jta.JtaTransactionManager.lookupTransactionManager(JtaTransactionManager.java:598)
Caused by: org.springframework.jndi.TypeMismatchNamingException: Object of type [class com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionManagerImple] available at JNDI location [java:comp/env/TransactionManager] is not assignable to [javax.transaction.TransactionManager] at org.springframework.jndi.JndiTemplate.lookup()
at org.springframework.transaction.jta.JtaTransactionManager.lookupTransactionManager()
Looks like there is some issue with the classpath of your project. Check if there are two versions of the same dependency in your project. In my case I had multiple versions of the jboss-transaction jar in the classpath, keeping just one version of it fixed the issue for me. Check if there is any transitive dependency which is causing this issue which you can exclude in your maven pom.

ActiveMQ nonBlockingRedelivery broker/Spring configuration

I can't get the nonBlockingRedelivery property to actually work, and I can't figure out why. I must be missing something obvious.
If I try to set it up through the broker URL, for example;
tcp://localhost:61616?jms.nonBlockingRedelivery=true
I will get the following error.
Cause: Could not create Transport. Reason:
java.lang.IllegalArgumentException: Invalid connect parameters:
{jms.nonBlockingRedelivery=true}
If I try to set it up through Spring configuration, I will get the following error.
<bean id="connectionFactoryRedelivery" class="org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy">
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${more.MQ.brokerURL}" />
<property name="nonBlockingRedelivery" value="true" />
<property name="redeliveryPolicy" ref="redeliveryPolicy" />
</bean>
</property>
<property name="synchedLocalTransactionAllowed" value="true" />
</bean>
org.springframework.beans.NotWritablePropertyException: Invalid
property 'nonBlockingRedelivery' of bean class
[org.apache.activemq.ActiveMQConnectionFactory]: Bean property
'nonBlockingRedelivery' is not writable or has an invalid setter
method. Does the parameter type of the setter match the return type of
the getter?
My retarded error, I was using an old version of the JAR where this property actually did not exist. The above Spring configuration works 100%, and if you're having problems, they lie somewhere else.
I believe that I'm still seeing this issue with spring-boot-starter-activemq:1.5.10.RELEASE - in that this on spring.activemq.broker-url does not work:
?jms.nonBlockingRedelivery=true
However setting this property fixes it:
spring.activemq.non-blocking-redelivery: true

Properties how to use in spring xml

<context:property-placeholder location="WEB-INF/classes/config.config"/>
config.config:
path=com
<import resource="../../conf/${path}/test.xml"/>
error:
org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from file [test.service.xml]; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'path'
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:412)Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'path'
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:173)
${path} can`t use in import,how to do ??
Can you try to do with this way?
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:yourpropertiesfile"></property>
</bean>
It is possible to put your properties in other location? Maybe this is the problem.

Spring context:property-placeholder can't resovle nested variables

I have two projects -- project-web, and project-service, both of them use Spring core 3.1.3 and have configurations to load properties from corresponding property files:
project-web -- Spring Integration based project, in its spring config file:
<context:property-placeholder location="WEB-INF/spring-integration/spring-integration.properties" ignore-resource-not-found="true" />
<import resource="classpath*:META-INF/spring/applicationContext.xml" />
where the import is to include the spring configuration file from project-service, and in the project-service project, I have following configured:
<context:property-placeholder location="classpath:META-INF/application.properties, classpath:META-INF/db.properties" ignore-resource-not-found="true"/>
<import resource="classpath:META-INF/spring/applicationContext-data.xml"/>
where the import to include Spring configuration for the DAOs, inside the applicationContext-data.xml I have:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${db.${db.type}.driver}" />
<property name="url" value="${db.${db.type}.url}"/>
<property name="username" value="${db.${db.type}.username}" />
<property name="password" value="${db.${db.type}.password}" />
</bean>
When I run the unit tests for project-service, everything is fine, all the variables are resolved correctly without any problem. But when I run the project-web (project-service will be included as a .jar file in the WEB-INF/lib folder of project-web), it throws error during start up saying can't resolve ${db.type}:
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dataSource' defined in class path resource [META-INF/spring/applicationContext-data.xml]: Could not resolve placeholder 'db.type' in string value "db.${db.type}.driver"
at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:209) ~[spring-beans-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.processProperties(PropertySourcesPlaceholderConfigurer.java:174) ~[spring-context-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:151) ~[spring-context-3.1.3.RELEASE.jar:3.1.3.RELEASE]
......................
Note: I can't declare everything in the project-web, because project-service will be also used by other projects. Anyone know why in project-service works when it runs alone but not when included by the project-web? It can't resolve the nested variable ${db.type}
The problem is that your first PropertyPlaceHolderConfigurer is trying to resolve the placeholder that needs to be resolved by the second one.
You can either use a different prefix for each one (e.g. !{ instead of ${ for one of them), or set
ignore-unresolvable="true"
on the first one - then it will leave the resolution to the other one.

Resources