Spring boot Auto connection to database [duplicate] - database

I have a nice little Spring Boot JPA web application. It is deployed on Amazon Beanstalk and uses an Amazon RDS for persisting data. It is however not used that often and therefore fails after a while with this kind of exception:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 79,870,633 milliseconds ago.
The last packet sent successfully to the server was 79,870,634 milliseconds ago. is longer than the server configured value of 'wait_timeout'.
You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
I am not sure how to configure this setting and can not find information on it on http://spring.io (a very good site though). What are some ideas or pointers to information?

I assume that boot is configuring the DataSource for you. In this case, and since you are using MySQL, you can add the following to your application.properties up to 1.3
spring.datasource.testOnBorrow=true
spring.datasource.validationQuery=SELECT 1
As djxak noted in the comment, 1.4+ defines specific namespaces for the four connections pools Spring Boot supports: tomcat, hikari, dbcp, dbcp2 (dbcp is deprecated as of 1.5). You need to check which connection pool you are using and check if that feature is supported. The example above was for tomcat so you'd have to write it as follows in 1.4+:
spring.datasource.tomcat.testOnBorrow=true
spring.datasource.tomcat.validationQuery=SELECT 1
Note that the use of autoReconnect is not recommended:
The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don't handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly.

The above suggestions did not work for me.
What really worked was the inclusion of the following lines in the application.properties
spring.datasource.testWhileIdle = true
spring.datasource.timeBetweenEvictionRunsMillis = 3600000
spring.datasource.validationQuery = SELECT 1
You can find the explanation out here

Setting spring.datasource.tomcat.testOnBorrow=true in application.properties didn't work.
Programmatically setting like below worked without any issues.
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
#Bean
public DataSource dataSource() {
PoolProperties poolProperties = new PoolProperties();
poolProperties.setUrl(this.properties.getDatabase().getUrl());
poolProperties.setUsername(this.properties.getDatabase().getUsername());
poolProperties.setPassword(this.properties.getDatabase().getPassword());
//here it is
poolProperties.setTestOnBorrow(true);
poolProperties.setValidationQuery("SELECT 1");
return new DataSource(poolProperties);
}

I just moved to Spring Boot 1.4 and found these properties were renamed:
spring.datasource.dbcp.test-while-idle=true
spring.datasource.dbcp.time-between-eviction-runs-millis=3600000
spring.datasource.dbcp.validation-query=SELECT 1

whoami's answer is the correct one. Using the properties as suggested I was unable to get this to work (using Spring Boot 1.5.3.RELEASE)
I'm adding my answer since it's a complete configuration class so it might help someone using Spring Boot:
#Configuration
#Log4j
public class SwatDataBaseConfig {
#Value("${swat.decrypt.location}")
private String fileLocation;
#Value("${swat.datasource.url}")
private String dbURL;
#Value("${swat.datasource.driver-class-name}")
private String driverName;
#Value("${swat.datasource.username}")
private String userName;
#Value("${swat.datasource.password}")
private String hashedPassword;
#Bean
public DataSource primaryDataSource() {
PoolProperties poolProperties = new PoolProperties();
poolProperties.setUrl(dbURL);
poolProperties.setUsername(userName);
poolProperties.setPassword(password);
poolProperties.setDriverClassName(driverName);
poolProperties.setTestOnBorrow(true);
poolProperties.setValidationQuery("SELECT 1");
poolProperties.setValidationInterval(0);
DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
return ds;
}
}

I have similar problem. Spring 4 and Tomcat 8. I solve the problem with Spring configuration
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<property name="initialSize" value="10" />
<property name="maxActive" value="25" />
<property name="maxIdle" value="20" />
<property name="minIdle" value="10" />
...
<property name="testOnBorrow" value="true" />
<property name="validationQuery" value="SELECT 1" />
</bean>
I have tested. It works well! This two line does everything in order to reconnect to database:
<property name="testOnBorrow" value="true" />
<property name="validationQuery" value="SELECT 1" />

In case anyone is using custom DataSource
#Bean(name = "managementDataSource")
#ConfigurationProperties(prefix = "management.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
Properties should look like the following. Notice the #ConfigurationProperties with prefix. The prefix is everything before the actual property name
management.datasource.test-on-borrow=true
management.datasource.validation-query=SELECT 1
A reference for Spring Version 1.4.4.RELEASE

As some people already pointed out, spring-boot 1.4+, has specific namespaces for the four connections pools. By default, hikaricp is used in spring-boot 2+. So you will have to specify the SQL here. The default is SELECT 1. Here's what you would need for DB2 for example:
spring.datasource.hikari.connection-test-query=SELECT current date FROM sysibm.sysdummy1
Caveat: If your driver supports JDBC4 we strongly recommend not setting this property. This is for "legacy" drivers that do not support the JDBC4 Connection.isValid() API. This is the query that will be executed just before a connection is given to you from the pool to validate that the connection to the database is still alive. Again, try running the pool without this property, HikariCP will log an error if your driver is not JDBC4 compliant to let you know. Default: none

For those who want to do it from YAML with multiple data sources, there is a great blog post about it: https://springframework.guru/how-to-configure-multiple-data-sources-in-a-spring-boot-application/
It basically says you both need to configure data source properties and datasource like this:
#Bean
#Primary
#ConfigurationProperties("app.datasource.member")
public DataSourceProperties memberDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.member.hikari")
public DataSource memberDataSource() {
return memberDataSourceProperties().initializeDataSourceBuilder()
.type(HikariDataSource.class).build();
}
Do not forget to remove #Primary from other datasources.

Related

Apache Camel "transacted" does not work well with sql component`s "outputType=StreamList"

In my transacted camel route I need to:
Call oracle package to set value for variable in this package;
Execute sql statement which is using variable from package;
Note that package variable is only visible in connection from which it was set - so I need to use "transacted" here.
Here is a sample code which demonstrates the problem:
from("direct-vm:process")
.transacted()
.to("sql:call my_pack.set_v1('10')")
.to("sql:select my_pack.get_v1 from dual?outputType=StreamList")
.split(body()).streaming()
.log("${body}")
.end();
Result for above code will be: GET_V1=null
If I comment ".transacted()" I will get: GET_V1=10
If I remove "StreamList" option from sql and un-comment ".transacted()": GET_V1=10
Question: is it not possible for "transacted" to work with sql component`s "StreamList" option?
Additional info:
If I start above route in multiple threads, like this:
Map<String, String> map = new HashMap<>();
map.put("10", "10");
map.put("20", "20");
map.put("30", "30");
map.put("40", "40");
map.put("50", "50");
map.forEach((key, values) -> {
from("timer://runOnce?repeatCount=1")
.setHeader("key", constant(key))
.setHeader("value", constant(values))
.inOnly("seda:processParallel");
});
from("seda:processParallel?concurrentConsumers=5")
.to("direct:process");
from("direct:process")
//.transacted()
.to("sql:call my_pack.pset_v1(:#value)?dataSource=generalDataSource")
.to("sql:select :#key key, my_pack.get_v1 value from dual?outputType=StreamList")
.split(body()).streaming()
.to("log:row")
.end();
I will get inconsistent results:
KEY=**20**, VALUE=**50**
KEY=**50**, VALUE=**40**
KEY=**40**, VALUE=**20**
KEY=**10**, VALUE=**30**
KEY=**30**, VALUE=**10**
Transaction manager configures as shown below:
#Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
if it is only for same database , you dont need transacted() which is XA transaction
usually it convers different resources for example one JMS , another Database
Can you show us how you have defined your transactionManager ? In particular, did you bind this txManager to your datasource ?
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="generalDataSource"/>
</bean>

Problems accessing datasource in Karaf 4 from Apache Camel

I've created a datasource in Karaf 4 (ServiceMix 7) that works from the karaf console - I can list tables, execute queries and so on.
My issue is when I try to use it from my Camel route.
Excerpt from my blueprint:
...
<reference filter="(osgi.jndi.service.name=jdbc/erp)" id="erpDataSource" interface="javax.sql.DataSource"/>
...
<to id="erpSelectQuery" uri="jdbc:erpDataSource"/>
...
It finds my dataSource but the blueprint can't start due to:
"java.lang.IllegalArgumentException: connectionFactory must be specified"
My datasource was created using:
jdbc:ds-create -dbName erp -dt DataSource -dn mysql -u erp -dc com.mysql.jdbc.Driver -p pre jdbc/erp
I'm at loss here
I have never done it via the jdbc command syntax I followed the guides from the Ops4J Wiki On Datasource creation which I like for one reason alone, this method creates a simple text file that can be administered by not just a Java developer, i.e. it is easier to modify and troubleshoot.
For the sake of not subjecting my answer to link rot I will just outline the procedure here.
Create a datasource configuration file(simple text file) in /servicemixhome/etc with the following naming convention org.ops4j.datasource-give_your_datasource_a_name.cfg .
In the config file configure the appropriate settings an example of mine looks like this:
osgi.jdbc.driver.class = com.mysql.jdbc.Driver
databaseName=dhData
user=foo
url=jdbc:mysql://192.199.199.199:3306/dhData
password=somepassword
dataSourceName=myDSName
Make sure you installed the ops4j required features:
feature:install pax-jdbc-mysql pax-jdbc-config
Now list the datasources using the following syntax:
karaf#root()> service:list javax.sql.DataSource
This will echo something like the list below back.
[javax.sql.DataSource]
----------------------
osgi.jdbc.driver.class = com.mysql.jdbc.Driver
databaseName=dhData
user=foo
url=jdbc:mysql://192.199.199.199:3306/dhData
password=somepassword
dataSourceName=myDSName
Provided by :
OPS4J Pax JDBC Config (216)
At this point you can reference the datasource usign an osgi filter in the blueprint.xml with the following syntax:
<reference filter="(&(objectClass=javax.sql.DataSource)(dataSourceName=myDSName ))" id="myData" interface="javax.sql.DataSource"/>
Then to reference this as property of a bean for example you could do the following:
<bean class="foo.bar" id="ImsCbrEventsBean">
<property name="dataSource" ref="myData"/>
</bean>
Keep in mind this creates a singular connection to a database and you should really create a connection pool.
This can be done by installing the pax-jdbc-pool-dbcp2 feature or any of the other connection pools but use only one at a time, then modifying the datasource config file to carry appropriate information like the example below:
osgi.jdbc.driver.name = mysql
databaseName=dhData
user=foo
url=jdbc:mysql://192.199.199.199:3306/dhData
password=somepassword
dataSourceName=myDSName
jdbc.pool.maxTotal=32
jdbc.pool.blockWhenExhausted=true
jdbc.pool.lifo=false
jdbc.pool.maxIdle=24
jdbc.pool.maxWaitMillis=5000
jdbc.pool.minEvictableIdleTimeMillis=1800000
jdbc.pool.minIdle=16
jdbc.pool.numTestsPerEvictionRun=3
jdbc.pool.softMinEvictableIdleTimeMillis=-1
jdbc.pool.testOnBorrow=true
jdbc.pool.testOnCreate=true
jdbc.pool.testOnReturn=true
jdbc.pool.testWhileIdle=true
jdbc.pool.timeBetweenEvictionRunsMillis=3600000

App Engine Java and EclipseLink: Deadlock on shared-cache access

I'm have a Java Maven Google App Engine project configured like follows:
I'm using EclipseLink as JPA persistence-manager for Cloud SQL. My object contains some simple fields (string, date, ...) and a ManyToMany relationship, which is configured as Lazy-Load
#Entity
#Table(name = "mytable")
public class MyObject1 {
private String nome;
private String descrizione;
#ManyToMany
#JoinTable
(
name = "myobject1_has_myobject2",
joinColumns = { #JoinColumn(name = "object1_id", referencedColumnName = "id") },
inverseJoinColumns = { #JoinColumn(name = "object2_id", referencedColumnName = "id") }
)
private List<MyObject2> relationshipObjects;
}
The project flow works like this:
- A query is made that retrieve x results of type MyObject1 (let's say 10 results)
- The query results list is iterated and each result is given to a different Thread for processing
- Each thread iterate the ManyToMany relationship (the relationshipObjects object), which is Lazy and this in confirmed because the code calls IndirectList.iterator, and do some processing for each MyObject2 item of the list
- When all the threads have finished, the query result of MyObject1 is iterated once again to create a request response
This kind of implementation is giving some trouble regarding the multi-thread implementation and a some sort of deadlock.
Here is the stacktrace
Caused by: Exception [EclipseLink-2001] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.ConcurrencyException
Exception Description: Wait was interrupted.
Message: [null]
at org.eclipse.persistence.exceptions.ConcurrencyException.waitWasInterrupted(ConcurrencyException.java:108)
at org.eclipse.persistence.internal.helper.ConcurrencyManager.acquireDeferredLock(ConcurrencyManager.java:187)
at org.eclipse.persistence.internal.identitymaps.CacheKey.acquireDeferredLock(CacheKey.java:210)
at org.eclipse.persistence.internal.identitymaps.AbstractIdentityMap.acquireDeferredLock(AbstractIdentityMap.java:84)
at org.eclipse.persistence.internal.identitymaps.IdentityMapManager.acquireDeferredLock(IdentityMapManager.java:146)
at org.eclipse.persistence.internal.sessions.IdentityMapAccessor.acquireDeferredLock(IdentityMapAccessor.java:81)
at org.eclipse.persistence.internal.sessions.AbstractSession.retrieveCacheKey(AbstractSession.java:5200)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:965)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildWorkingCopyCloneNormally(ObjectBuilder.java:899)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObjectInUnitOfWork(ObjectBuilder.java:852)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:735)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:689)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:805)
at org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:962)
at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:573)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1175)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1134)
at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:460)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1222)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258)
at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:473)
I cannot reproduce all the time the problem, besides that I can give you all the information I can gather.
Looking inside the EclipseLink documentation I found a section related to this matter
Cache - If using a shared cache, EclipseLink requires locking the cache on reads and writes to ensure consistency.
You will see cache access, such as IdentityMapManager acquireLock or acquireDeferredLock, or WriteLockManager as the last call on the stack.
In my persistence-unit I did not configure the shared-cache behaviour, so it is running on default which is enabled.
Here is my persistence-unit properties
<properties>
<!-- configure the various connection pool properties -->
<!-- http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_connection_pool.htm -->
<property name="eclipselink.connection-pool.default.initial" value="1" />
<property name="eclipselink.connection-pool.default.min" value="64" />
<property name="eclipselink.connection-pool.default.max" value="64" />
<property name="eclipselink.connection-pool.default.shared" value="true" />
<!-- whether connections in EclipseLink read connection pool should be shared (not exclusive). Connection sharing means the same JDBC connection will be used concurrently for multiple reading threads. -->
<property name="eclipselink.jdbc.connection_pool.read.shared" value="true" />
<!-- specify if JDBC statements should be cached -->
<!-- http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_jdbc_cachestatements.htm -->
<property name="eclipselink.jdbc.cache-statements" value="true" />
<!-- the number of statements held when using internal statement caching -->
<!-- http://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_jdbc_cachestatements_size.htm#CACBICGG -->
<property name="eclipselink.jdbc.cache-statements.size" value="100" />
</properties>
I can see in my stacktrace that there is indeed the IdentityMapManager.acquireDeferredLock(IdentityMapManager.java:146) row that is referred.
The thing is, this error is thrown by the App Engine request (see last line of stacktrace) when I call the getResultList method.
This call is made by the main request thread, other threads (one for query result) has not been launched yet.
So I started to looking for the shared-cache documentation and I found this part:
The shared cache exists for the duration of the persistence unit (EntityManagerFactory, or server) and is shared by all EntityManagers and users of the persistence unit
My EntityManagerFactory instance is instance-shared (I have a static variabile which is initialized at first query).
So at first access (for each App Engine instance) the variabile is initialized and then shared for all the http requests that will be server by the same instance.
I did this sort of "caching" because the deployment descriptor at the first access of EntityManagerFactory is very slow, and even if I can pre-warmup this object, opening a new EMF at every request costs about 1-2 seconds.
So, I open/close a new EntityManager at each flow (and each thread, because EntityManager is not thread-safe) but the EMF object is shared.
Also, there is another line which says
This is normally related to having relationships that do not use LAZY, ensure all relationship use LAZY.
My ManyToMany relationship is already Lazy, as pointed out before, so even this point cannot be the cause
Basing on that here, I tried to gather all together:
- EclipseLink requires locking the cache on reads and writes to ensure consistency, so the access to this cache is atomic and multiple threads are queued.
- The sharedcache is based on the EMF object
- The EMF object is shared between requests of the same instance
As suggested by EclipseLink documentation I tried to disable the shared-cache and all the flow appaers to works, but it is now very slow.
Anyway, this is another point that confirm the problem here is related to the shared-cache of JPA.
This solution is not suitable because, even w/o considering the speed problem, all those request and threads that concurr of get data from the DBMS (while iterating the Lazy list) consumes all the available connections and the DBMS starts on giving connection errors.
Another suggestion from the documentation
DeferredLockManager.SHOULD_USE_DEFERRED_LOCKS = false;
but the error is still the same, nothing changed (the error is on IdentityMapManager.acquireLock, so the deferredLock is not used anyway)
From the App Engine logs I can see that all these requests are killed after 60s timeout, so the Wait was interrupted message can be related to all the threads that were waiting to access the shared-cache, but at the end the App Engine deadline killed the request.
Because of that I tried to deploy on basic-scaling (which does not have the 60s deadline) to see if the request is only slower than the dealine or it is truly stuck on a deadlock
Inside the logs there is no error... but the longest requests does not even show. At this point I can think that the erroneous requests are stuck indefinitely and the request logs will not be shown at all.
Another test I made is reducing all the persistence-unit configuration, removing all the shared configuration, like follows
<properties>
<property name="eclipselink.connection-pool.default.initial" value="1" />
<property name="eclipselink.connection-pool.default.min" value="64" />
<property name="eclipselink.connection-pool.default.max" value="64" />
</properties>
But the error is still the same. So it is not related to a connection-pool sharing but the multi-thread itself
As ultimate test I tried to remove the multi-thread flow (each query result is processed one-by-one by the main thread) and leaving the shared-cache enabled.
This is working.
At this point I'm wondering... because the shared-cache is synchronized, so there is a "funnel" that block anyway the multi-thread process, should I use the monothread implementation anyway?

Why does Jasypt try to decrypt Camel Property Placeholders, regardless of the ENC( prefix?

In my Blueprint application deployed in JBoss Fuse 6.1.0-379, I want to secure the password I use for creating a database connection. I read this article and added <enc:property-placeholder> to the blueprint configuration. However my blueprint configuration has many property placeholders, and it seems that the Jasypt Placeholder Resolver is trying to decrypt all the placeholders I define in my Camel Context. When the Blueprint Context starts up, I get the following exception:
11:59:51,233 | ERROR | t-379-dmz/deploy | BlueprintCamelContext | 151 - org.apache.camel.camel-blueprint - 2.12.0.redhat-610379 | Error occurred during starting Camel: CamelContext(camel-5) due Failed to create route route7: Route(route7)[[From[{{uri}}]] -> [Log[logging]]] because of Failed to resolve endpoint: {{uri}} due to: org.jasypt.exceptions.EncryptionOperationNotPossibleException
org.apache.camel.FailedToCreateRouteException: Failed to create route route7: Route(route7)[[From[{{uri}}]] -> [Log[logging]]] because of Failed to resolve endpoint: {{uri}} due to: org.jasypt.exceptions.EncryptionOperationNotPossibleException
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:182)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:778)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:1955)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultCamelContext.doStartCamel(DefaultCamelContext.java:1705)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultCamelContext.doStart(DefaultCamelContext.java:1579)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.support.ServiceSupport.start(ServiceSupport.java:61)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:1547)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.blueprint.BlueprintCamelContext.start(BlueprintCamelContext.java:177)[151:org.apache.camel.camel-blueprint:2.12.0.redhat-610379]
at org.apache.camel.blueprint.BlueprintCamelContext.maybeStart(BlueprintCamelContext.java:209)[151:org.apache.camel.camel-blueprint:2.12.0.redhat-610379]
at org.apache.camel.blueprint.BlueprintCamelContext.serviceChanged(BlueprintCamelContext.java:147)[151:org.apache.camel.camel-blueprint:2.12.0.redhat-610379]
at org.apache.felix.framework.util.EventDispatcher.invokeServiceListenerCallback(EventDispatcher.java:934)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.util.EventDispatcher.fireEventImmediately(EventDispatcher.java:795)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.util.EventDispatcher.fireServiceEvent(EventDispatcher.java:544)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.Felix.fireServiceEvent(Felix.java:4666)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.Felix.registerService(Felix.java:3674)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.BundleContextImpl.registerService(BundleContextImpl.java:347)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.aries.blueprint.container.BlueprintContainerImpl.registerService(BlueprintContainerImpl.java:448)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
at org.apache.aries.blueprint.container.BlueprintContainerImpl.doRun(BlueprintContainerImpl.java:383)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
at org.apache.aries.blueprint.container.BlueprintContainerImpl.run(BlueprintContainerImpl.java:261)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
at org.apache.aries.blueprint.container.BlueprintExtender.createContainer(BlueprintExtender.java:270)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
at org.apache.aries.blueprint.container.BlueprintExtender.modifiedBundle(BlueprintExtender.java:233)[9:org.apache.aries.blueprint.core:1.0.1.redhat-610379]
at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.customizerModified(BundleHookBundleTracker.java:500)[11:org.apache.aries.util:1.0.1.redhat-610379]
at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.customizerModified(BundleHookBundleTracker.java:433)[11:org.apache.aries.util:1.0.1.redhat-610379]
at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$AbstractTracked.track(BundleHookBundleTracker.java:725)[11:org.apache.aries.util:1.0.1.redhat-610379]
at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.bundleChanged(BundleHookBundleTracker.java:463)[11:org.apache.aries.util:1.0.1.redhat-610379]
at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$BundleEventHook.event(BundleHookBundleTracker.java:422)[11:org.apache.aries.util:1.0.1.redhat-610379]
at org.apache.felix.framework.util.SecureAction.invokeBundleEventHook(SecureAction.java:1103)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.util.EventDispatcher.createWhitelistFromHooks(EventDispatcher.java:696)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.util.EventDispatcher.fireBundleEvent(EventDispatcher.java:484)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.Felix.fireBundleEvent(Felix.java:4650)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.Felix$4.run(Felix.java:2123)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.Felix.runInContext(Felix.java:2147)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.Felix.startBundle(Felix.java:2121)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:955)[org.apache.felix.framework-4.0.3.redhat-610379.jar:]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundle(DirectoryWatcher.java:1247)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundles(DirectoryWatcher.java:1219)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.startAllBundles(DirectoryWatcher.java:1208)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.process(DirectoryWatcher.java:503)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.run(DirectoryWatcher.java:291)[7:org.apache.felix.fileinstall:3.3.11.redhat-610379]
Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: {{uri}} due to: org.jasypt.exceptions.EncryptionOperationNotPossibleException
at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:480)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.util.CamelContextHelper.getMandatoryEndpoint(CamelContextHelper.java:71)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.model.RouteDefinition.resolveEndpoint(RouteDefinition.java:192)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:106)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultRouteContext.resolveEndpoint(DefaultRouteContext.java:112)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.model.FromDefinition.resolveEndpoint(FromDefinition.java:72)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultRouteContext.getEndpoint(DefaultRouteContext.java:88)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:890)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:177)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
... 38 more
Caused by: org.apache.camel.RuntimeCamelException: org.jasypt.exceptions.EncryptionOperationNotPossibleException
at org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException(ObjectHelper.java:1363)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.util.ObjectHelper.invokeMethod(ObjectHelper.java:1005)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.blueprint.BlueprintPropertiesParser.parseProperty(BlueprintPropertiesParser.java:137)[151:org.apache.camel.camel-blueprint:2.12.0.redhat-610379]
at org.apache.camel.component.properties.DefaultPropertiesParser.createPlaceholderPart(DefaultPropertiesParser.java:201)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.component.properties.DefaultPropertiesParser.doParseUri(DefaultPropertiesParser.java:105)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.component.properties.DefaultPropertiesParser.parseUri(DefaultPropertiesParser.java:51)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.component.properties.PropertiesComponent.parseUri(PropertiesComponent.java:160)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.component.properties.PropertiesComponent.parseUri(PropertiesComponent.java:119)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultCamelContext.resolvePropertyPlaceholders(DefaultCamelContext.java:1155)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
at org.apache.camel.impl.DefaultCamelContext.getEndpoint(DefaultCamelContext.java:478)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
... 46 more
Caused by: org.jasypt.exceptions.EncryptionOperationNotPossibleException
at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.decrypt(StandardPBEByteEncryptor.java:918)
at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.decrypt(StandardPBEStringEncryptor.java:725)
at org.apache.karaf.jaas.jasypt.handler.EncryptablePropertyPlaceholder.getProperty(EncryptablePropertyPlaceholder.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)[:1.7.0_25]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)[:1.7.0_25]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)[:1.7.0_25]
at java.lang.reflect.Method.invoke(Method.java:606)[:1.7.0_25]
at org.apache.camel.util.ObjectHelper.invokeMethod(ObjectHelper.java:1001)[143:org.apache.camel.camel-core:2.12.0.redhat-610379]
... 54 more
I created a test bundle with a Blueprint Context which contains only one placeholder property defined in the Camel Context, without using the encrypted ENC() placeholder syntax. I just added <enc:property-placeholder> and the bundle failed to start with same exception (org.jasypt.exceptions.EncryptionOperationNotPossibleException).
Is this desired behavior?
My Blueprint configuration:
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0"
xmlns:enc="http://karaf.apache.org/xmlns/jasypt/v1.0.0"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0">
<cm:property-placeholder persistent-id="encrypt.config" update-strategy="reload" >
<cm:default-properties>
<cm:property name="uri" value="timer://foo?fixedRate=true&period=6000"/>
</cm:default-properties>
</cm:property-placeholder>
<enc:property-placeholder>
<enc:encryptor class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config">
<bean class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndDES" />
<property name="password" value="password" />
</bean>
</property>
</enc:encryptor>
</enc:property-placeholder>
<camelContext xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://camel.apache.org/schema/blueprint"
xsi:schemaLocation="http://camel.apache.org/schema/blueprint">
<route>
<from uri="{{uri}}"/>
<log message="logging" loggingLevel="INFO" id="logBeforeService"></log>
</route>
</camelContext>
</blueprint>
EDIT: Response from RedHat Support
So this is a known issue, and theres a couple of Jira issues for it (here and here), and it appears as if the issue has been resolved in newer versions of Camel. I have tested with version 2.12.0.redhat-611412, provided by the patch named jboss-fuse-6.1.0.redhat-379-r1p3, and the exception is no longer being thrown.
Regardless of what I have said previously, im quite happy with this implementation. I would want an exception to be thrown if it couldnt decrypt a value which was actaully encrypted, and that is exactly what happens. I modified the encrypted value to ENC(invalid_and_should_throw_exception), and an exception was thrown exactly like I would expect it to.
Caused by: org.jasypt.exceptions.EncryptionOperationNotPossibleException
EDIT: A more concise Answer
Camel-Blueprint behaves differently to Camel-Core, in regards to the way that it resolves property placeholder values. Camel-Core requires the developer to define a Camel Property Placeholder Resolver, which resolves properties in the Camel Context, for the camel property syntax [1]. Obviously the reasoning behind this is to avoid conflicts between the spring property syntax [2] and the Camel Simple Expression Language syntax [3]. The developer has the choice to optionally bridge the Spring Property Placeholder Resolver with Camel by adding extra configuration.
[1 - Camel Property Syntax]
{{org.my.prop}}
[2 - Spring Property Syntax]
${org.my.prop}
[3 - Simple Expression Language Syntax]
${exchange.body}
In Camel-Blueprint, the bridging between the Blueprint Property Placeholder Resolvers and the Camel Context happens automatically. When a Blueprint Camel Context is created, the Blueprint Bundle Context is injected into it. With the Blueprint Bundle Context, Camel pulls all of the beans out of it and determines if they are assignable to the Apache Aries implementation AbstractPropertyPlaceholder. With each instance of the Property Placeholder Resolvers you have defined, Camel is then capable of calling the resolveProperty method on them, without having to parse the property syntax defined by each of the resolvers.
Because the Jasypt Property Placeholder Resolver expects the placeholder syntax [4], it just ignores everything which dosent match this syntax. Because Camel-Blueprint by-passes that validation which ensures the property syntax, we end up in a scenario where Camel is telling the Jasypt Placeholder Resolver to decrypt every property that we attempt to use in our Camel Context. This of course will throw an exception, because you’re trying to decrypt a property which hasn’t been encrypted.
[4 - Jasypt Blueprint Property Syntax]
ENC(encrypted.value)
Solutions:
Create a class which implements the Jasypt StringEncryptor and holds the StandardPBEStringEncryptor as an attribute. The implemented encrypt and decrypt methods call the encrypt and decrypt methods of the StandardPBEStringEncryptor, but catch any exceptions that are thrown.
This is the solution I gave in my original answer.
This is dangerous, if an encrypted value can’t be decrypted that shouldn’t be ignored. The bundle should not start up, to prevent e.g. your database account from getting locked.
Decrypt values manually before passing them to the Placeholder Resolver.
You could create a configuration service, where you compaile all your configuration from your various sources, decrypt all the encrypted values manually, then expose the properties as an OSGi service to be shared accross bundles.
I’ve gone off this design, it’s basically re-implementing the ConfigurationAdmin service which is provided natively by Karaf (with the addition of decryption which Karaf doesn’t provide), it’s just not as good as the one Karaf provides as it is not capable of detecting when application configuration has changed.
Decrypt values at runtime.
Not a fan of this either, requires your application to be aware of which application properties are expected to be encrypted.
I have raised a support ticket with Redhat through our support contract, I'll keep you updated if anything comes of it.
Original Answer:
I think I figured this one out. According to the camel documentation, in blueprint camel is capable of detecting that a blueprint placeholder resolver is present, and attempts to use that to resolve its properties.
The problem with this is that it does not care what the placeholder prefix and suffix is, it just goes ahead and uses it regarless. The Jasypt placeholder resolver has been desgined so that it is only even invoked if the placeholder prefix is "ENC(" and the suffix is ")", remember Camel dosent care about this. Camel passes its unresolved properties to the Jasypt property resolver, which of course attempts to decrypt them. Because they are not encrypted, an exception is thrown.
To get around this, I have created a custom encryptor which implements the Jasypt StringEncryptor. The custom encryptor contains an instance of the StandardPBEStingEncryptor, and uses that to do the actual encryption/ decryption. The key difference is that Exceptions are caught and ignored, so if an Exception is thrown trying to decrypt a camel property which isnt encrypted, then it is ignored and the application continues as normal.
The Java Class:
package uk.co.test;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CustomEncryptor implements StringEncryptor {
private StandardPBEStringEncryptor encryptor;
private static final Logger LOG = LoggerFactory.getLogger(CustomEncryptor.class);
public CustomEncryptor(String password) {
encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword(password);
}
#Override
public String decrypt(String value) {
String ret = null;
try {
ret = encryptor.decrypt(value);
} catch (Exception e) {
LOG.error("Failed to decrypt value.");
}
return ret;
}
#Override
public String encrypt(String value) {
String ret = null;
try {
ret = encryptor.encrypt(value);
} catch (Exception e) {
LOG.error("Failed to encrypt value.");
}
return ret;
}
public StandardPBEStringEncryptor getEncryptor() {
return encryptor;
}
public void setEncryptor(StandardPBEStringEncryptor encryptor) {
this.encryptor = encryptor;
}
}
The Blueprint configuration:
<enc:property-placeholder>
<enc:encryptor class="uk.co.test.CustomEncryptor">
<argument value="myPass" />
</enc:encryptor>
</enc:property-placeholder>

Tomcat database connections leak

Hi We are using tomcat 6 and context.xml is like below
<Context>
<Resource defaultAutoCommit="false" defaultReadOnly="false"
driverClassName="oracle.jdbc.driver.OracleDriver"
fairQueue="false" initialSize="${DBPool.initialPoolSize}"
jdbcInterceptors="ConnectionState;StatementFinalizer"
jmxEnabled="true" logAbandoned="false" maxActive="${DBPool.maxPoolSize}"
maxIdle="30" maxWait="30000"
minEvictableIdleTimeMillis="5000" minIdle="${DBPool.minPoolSize}"
name="jdbc/BankDBPool" password="${DBPool.bankPassword}"
removeAbandoned="true" removeAbandonedTimeout="60"
testOnBorrow="false" testOnReturn="true"
testWhileIdle="false" timeBetweenEvictionRunsMillis="5000"
type="javax.sql.DataSource"
url="${DBPool.jdbcUrl}"
factory="uk.co.xxxx.encryption.dbcp.DecryptingBasicDataSourceFactory"
useEquals="false" username="${DBPool.bankUser}" validationInterval="30000" validationQuery="select 1 from dual" />
</Context>
DBPool.maxPoolSize=400
DBPool.minPoolSize=15
DBPool.initialPoolSize=15
The issue is we have to set maxPoolSize to very high as it is giving connection not available Exception.
DB Monitor tool is showing the connections idle but seems they can not be reused. Traffic to this application is very low. Around 10000 hits in a day.
We are trying to figure out what might be an issue here.
All my service methods are all marked
#Transactional(propagation = Propagation.REQUIRED, readOnly = true or false)
DecryptingBasicDataSourceFactory only does job of returning datasource.
We are using spring and hibernate.
The issue was resolved. Apparently one of the method had #Transactional missing. Another change was reducing the batch-size in hibernate properties from 100 to 20. But mostly it was adding #Transactional which fixed the issue.

Resources