This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 6 years ago.
I made a form with Spring MVC. I want use it to fill data in my databases.
I use a DAO. With a sysout I show data in the consol but it's impossible to execute the query.
package controlleurs;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
#Repository("RepertoryDAO")
public class RepertoryDAO {
private JdbcTemplate myJdbc;
public RepertoryDAO(){
}
#Autowired
public void setMonJdbc(DataSource ds) {
//this.JdbcTemplate = new JdbcTemplate(ds);
this.myJdbc = new JdbcTemplate(ds);
}
//Display data
public List<Repertory> getRepetories(){
return myJdbc.query("select*from main",new RowMapper<Repertory>(){
public Repertory mapRow(ResultSet rs, int rowNum1)
throws SQLException {
Repertory rp=new Repertory();
rp.setId(rs.getInt("id"));
rp.setName(rs.getString("name"));
return rp;
}
});
}
//Add a repertory in database
public void addRepertory(String name, String url, int bl, int noFree, String topic){
String backlinkRequired=Integer.toString(bl);
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("name", name);
System.out.println(name);
params.addValue("url", url);
System.out.println(url);
params.addValue("lienRetour", backlinkRequired);
System.out.println(backlinkRequired);
params.addValue("payant", noFree);
System.out.println(noFree);
params.addValue("topic", topic);
System.out.println(topic);
System.out.println("jdbc"+ getMonJdbc().toString());
this.myJdbc.update("insert into main (name,url,lienRetour,payant,topic) values (:name,:url,:lienRetour,:payant,:topic)",params);
}
public JdbcTemplate getMonJdbc() {
return this.myJdbc;
}
This line give me anything in the console.
System.out.println("jdbc"+ getMonJdbc().toString());
The console display this error:
déc. 28, 2016 2:58:32 PM org.apache.catalina.core.StandardWrapperValve
invoke
GRAVE: Servlet.service() for servlet [MyServletSpring] in context with path [/monProjetSpring] threw exception [Request processing
failed; nested exception is java.lang.NullPointerException] with root
cause
java.lang.NullPointerException
at controlleurs.RepertoryDAO.addRepertory(RepertoryDAO.java:65)
at controlleurs.NewDirectory.directoryRegistered(NewDirectory.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
Thanks for your help.
It seems to be a basic Spring issue.
First, look if you've got a dataSource registered, for me the autowired annotation just try to inject the dataSource parameter. If it's right, you could try to define a jdbcTemplate bean in your xml config file with the datasource as properties.
Then you can put your #Autowired just above your jdbcTemplate definition in your DAO class.
This may not be the way you want but it could work easily.
Related
Camunda normally uses UUIDs (e. g. 98631715-0b07-11ec-ab3b-68545a6e5055) as process instance IDs. In my project a process instance ID like 124 is being generated which looks suspicious to me.
This behavior can be reproduced as described below.
Step 1
Check out this repository and start the process engines
core-processes,
core-workflow and
domain-hello-world
so that all of them use the same shared database.
Step 2
Login to the Camunda UI at http://localhost:8080 and navigate to the tasklist.
Start the Starter process in tasklist.
Step 3
Go to the cockpit and navigate to Running process instances (http://localhost:8080/camunda/app/cockpit/default/#/processes).
Click on DomainProcess.
In column ID you will see a numeric (135 in the screenshot above) process instance ID, not a UUID.
Probable cause of the error
In core-processs engine I have the following Config class:
import org.camunda.bpm.engine.impl.history.HistoryLevel;
import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
import org.camunda.bpm.engine.impl.history.handler.CompositeHistoryEventHandler;
import org.camunda.bpm.engine.impl.history.handler.HistoryEventHandler;
import org.camunda.bpm.engine.spring.SpringProcessEngineConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import static org.apache.commons.lang3.ArrayUtils.addAll;
#Configuration
public class Config {
private static final Logger LOGGER = LoggerFactory.getLogger(Config.class);
#Autowired
#Qualifier("camundaBpmDataSource")
private DataSource dataSource;
#Autowired
#Qualifier("camundaTxManager")
private PlatformTransactionManager txManager;
#Autowired
private ResourcePatternResolver resourceLoader;
#Bean
public SpringProcessEngineConfiguration processEngineConfiguration() {
final SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setDataSource(dataSource);
config.setTransactionManager(txManager);
config.setDatabaseSchemaUpdate("true");
config.setHistory(HistoryLevel.HISTORY_LEVEL_FULL.getName());
config.setJobExecutorActivate(true);
config.setMetricsEnabled(false);
final Logger logger = LoggerFactory.getLogger("History Event Handler");
final HistoryEventHandler testHistoryEventHandler = new HistoryEventHandler() {
#Override
public void handleEvent(final HistoryEvent evt) {
LOGGER.debug("handleEvent | " + evt.getProcessInstanceId() + " | "
+ evt.toString());
}
#Override
public void handleEvents(final List<HistoryEvent> events) {
for (final HistoryEvent curEvent : events) {
handleEvent(curEvent);
}
}
};
config.setHistoryEventHandler(new CompositeHistoryEventHandler(Collections.singletonList(testHistoryEventHandler)));
try {
final Resource[] bpmnResources = resourceLoader.getResources("classpath:*.bpmn");
final Resource[] dmnResources = resourceLoader.getResources("classpath:*.dmn");
config.setDeploymentResources(addAll(bpmnResources, dmnResources));
} catch (final IOException exception) {
exception.printStackTrace();
LOGGER.error("An error occurred while trying to deploy BPMN and DMN files", exception);
}
return config;
}
}
If I remove this configuration (or comment the #Configuration line), the error disappears.
Questions
Why does Camunda generate a numeric process instance ID in this case (and not a UUID as in other cases)?
After adding the line
config.setIdGenerator(new StrongUuidGenerator());
in the configuration class
#Bean
public SpringProcessEngineConfiguration processEngineConfiguration() {
final SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setIdGenerator(new StrongUuidGenerator());
config.setDataSource(dataSource);
the process instance IDs became UUIDs again.
For details see Camunda documentation on ID generators.
I tried to testing my Dao class but it return this error for the class DbConnection:
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or in an application resource file: java.naming.factory.initial
at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:691)
at java.naming/javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305)
at java.naming/javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:342)
at java.naming/javax.naming.InitialContext.lookup(InitialContext.java:409)
at model.DbConnection.(DbConnection.java:16)
at model.DbConnection.getInstance(DbConnection.java:30)
at model.ProfileManager.ReturnPatientByKey(ProfileManager.java:27)
at model.ProfileManagerTest.testReturnPatientByKey(ProfileManagerTest.java:32)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
....
my DbConnection class:
package model;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class DbConnection {
private DbConnection() {
Context ctx;
try {
ctx = new InitialContext();
ds = (DataSource)ctx.lookup("java:comp/env/jdbc/CheckUpDb");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
return ds.getConnection();
}
public static DbConnection getInstance () {
if(db==null) {
return new DbConnection();
}
else {
return db;
}
}
private static DataSource ds;
private static DbConnection db;
}
Database connection works in the web application, only the testing return this error.
I don't think the problem si my class ProfileManager because it is only a testing example:
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import model.Bean.PatientBean;
class ProfileManagerTest{
private ProfileManager pm;
String fiscal_code;
String user_password;
PatientBean patient;
#BeforeEach
void setUp() throws Exception {
this.pm = new ProfileManager();
fiscal_code = "aaa";
user_password = "bbb";
patient = mock(PatientBean.class);
}
#AfterEach
void tearDown() throws Exception {
}
#Test
void testReturnPatientByKey() {
patient = (PatientBean) pm.ReturnPatientByKey(fiscal_code, user_password);
assertTrue(true);
}
}
Database connection works in the web application, only the testing return this error.
That's most likely because you have the datasource declared in the server configuration and the server is providing your web app with one, but you don't have the same done in your test.
Do a search in your server files an you will probably discover something like this, or similar, depending on what server you use:
<Resource name="jdbc/CheckUpDb"
driverClassName="..."
type="..."
url="..."
username="..."
password="..."
/>
This is a way to configure a datasource using JNDI. When your web application runs, the server will provide you with this resource by name. This is what ctx.lookup("java:comp/env/jdbc/CheckUpDb"); does. It asks the server to give it that resource.
But when you run your unit tests, you run outside the server environment. That means that any resource you defined in the server (using context.xml for example) doesn't exist when you run your tests. In your tests you have to provide a datasource and make it available to your JNDI context so that this line of code then works:
ds = (DataSource)ctx.lookup("java:comp/env/jdbc/CheckUpDb");
The following post should give you the necessary details to set up your JNDI data source for your test: Setting up JNDI Datasource in jUnit
You will see that the examples there make use of a library called Simple-Jndi that they use to provide a JNDI context and configure it to include the datasource that the tests then try to retrieve by name. You can use any JNDI provider, but you must set up the datasource for your test yourself (inside #BeforeEach or #BeforeAll) because when running unit tests, you don't have the tomcat server doing this for you.
I am trying to integrate HA-JDBC with Spring Boot JPA. Can any one help to integrate Ha-Jdbc with spring boot JPA,
import java.util.Arrays;
import javax.naming.NamingException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import net.sf.hajdbc.SimpleDatabaseClusterConfigurationFactory;
import net.sf.hajdbc.cache.simple.SimpleDatabaseMetaDataCacheFactory;
import net.sf.hajdbc.dialect.oracle.OracleDialectFactory;
import net.sf.hajdbc.distributed.jgroups.JGroupsCommandDispatcherFactory;
import net.sf.hajdbc.sql.DataSource;
import net.sf.hajdbc.sql.DataSourceDatabase;
import net.sf.hajdbc.sql.DataSourceDatabaseClusterConfiguration;
import net.sf.hajdbc.state.simple.SimpleStateManagerFactory;
#Configuration
public class HAJdbcConfig
{
#Bean
public DataSourceDatabase db1()
{
DataSourceDatabase db1 = new DataSourceDatabase();
db1.setId("db1");
db1.setLocation("database url");
db1.setUser("username");
db1.setPassword("password");
return db1;
}
#Bean
public DataSourceDatabase db2()
{
DataSourceDatabase db1 = new DataSourceDatabase();
db1.setId("db1");
db1.setLocation("database url");
db1.setUser("username");
db1.setPassword("password");
return db1;
}
#Bean
public DataSourceDatabaseClusterConfiguration config() throws NamingException
{
DataSourceDatabaseClusterConfiguration config = new DataSourceDatabaseClusterConfiguration();
config.setDatabases(Arrays.asList(db1(), db2()));
config.setDialectFactory(new OracleDialectFactory());
config.setDatabaseMetaDataCacheFactory(new SimpleDatabaseMetaDataCacheFactory());
SimpleStateManagerFactory state = new SimpleStateManagerFactory();
config.setStateManagerFactory(state);
config.setDispatcherFactory(new JGroupsCommandDispatcherFactory());
return config;
}
#Bean
public javax.sql.DataSource primaryDataSource() throws NamingException
{
DataSource ds = new DataSource();
ds.setCluster("mycluster");
//ds.setConfig("ha-jdbc-mycluster.xml");
ds.setConfigurationFactory(
new SimpleDatabaseClusterConfigurationFactory<javax.sql.DataSource, DataSourceDatabase>(config()));
return ds;
}
}
Error retrieving database meta-data; nested exception is org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta-data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Failed to perform naming lookup of jdbc:oracle:thin:#//localhost:1521/orcl
I don't have much experience with HA-JDBC, but maybe I can put you in track. You never declare the Driver and the lookup is trying to create an Oracle URL connection (dbc:oracle:thin:#//localhost:1521/orcl).
I hope it gives you an idea of where to look.
I think there is something wrong with your the jdbc url.
The format is jdbc:oracle:<drivertype>:#<database> in your case
jdbc:oracle:thin:#localhost:1521/orcl
without // before the host.
Can you use the service name for the database? The simple connection URL is jdbc:oracle:thin:#myhost:1521/myorcldbservicename. Also, you can check out the sample SpringBootApp for an example.
I already found out how to create an Oracle database with AQ (Streams?) packages. I also created some queue's in Oracle (by hand). (Using PL/SQL and SQL).
However, I'm having a hard time setting up a proper connection from Spring.
The following works (using oracle.AQ java package):
private final String aqUrl = "jdbc:oracle:thin:#localhost:1521:orcl";
private final String aqUser = "queue_mut";
private final String aqPassword = "******";
private final String aqSchema = "queue_mut";
private final String aqTable = "aq_table1";
private final String aqQueue = "aq_queue1";
#Test
public void testManualAQ() throws ClassNotFoundException, SQLException, AQException {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection connection = DriverManager.getConnection(aqUrl, aqUser, aqPassword);
connection.setAutoCommit(false);
Class.forName("oracle.AQ.AQOracleDriver");
AQSession aqSession = AQDriverManager.createAQSession(connection);
AQQueueTable q_table = aqSession.createQueueTable(aqSchema, aqTable, new AQQueueTableProperty("RAW"));
aqSession.createQueue(q_table, aqQueue, new AQQueueProperty());
}
(based on https://docs.oracle.com/cd/B10501_01/appdev.920/a96587/apexampl.htm)
This shows me that I can connect to Oracle and reach AQ functionality.
Now, I'm trying to create Java Configured beans in order to use the JmsTemplate.
#Resource
private JmsTemplate jmsTemplate;
#Test
public void testJmsTemplate() {
String xmlval = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<product id=\"10\">\n" +
" <description>Foo</description>\n" +
" <price>2.05</price>\n" +
"</product>";
jmsTemplate.convertAndSend(aqSchema + ".jms_ws_incoming_queue", xmlval);
}
(yes, the queue already exists ;-))
With the following configuration class:
import oracle.jms.AQjmsFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jms.core.JmsTemplate;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.sql.DataSource;
#Configuration
public class OracleAQConfiguration {
#Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
#Bean
public ConnectionFactory connectionFactory(DataSource dataSource) throws JMSException {
return AQjmsFactory.getQueueConnectionFactory(dataSource);
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setSessionTransacted(true);
jmsTemplate.setConnectionFactory(connectionFactory);
return jmsTemplate;
}
}
And with properties yml:
spring:
datasource:
url: jdbc:oracle:thin:#localhost:1521:orcl
username: queue_mut
password: ******
driverClassName: oracle.jdbc.driver.OracleDriver
But with this I get errors which I cannot grasp:
2017-04-19 12:11:17,151 INFO my.project.QueueTest: Started QueueTest in 5.305 seconds (JVM running for 6.588)
org.springframework.jms.UncategorizedJmsException: Uncategorized exception occurred during JMS processing; nested exception is oracle.jms.AQjmsException: Error creating the db_connection; nested exception is java.lang.ClassCastException: com.sun.proxy.$Proxy102 cannot be cast to oracle.jdbc.internal.OracleConnection
at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:316)
at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:169)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:487)
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:570)
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:658)
at my.project.QueueTest.testJmsTemplate(QueueTest.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: oracle.jms.AQjmsException: Error creating the db_connection
at oracle.jms.AQjmsDBConnMgr.getConnection(AQjmsDBConnMgr.java:625)
at oracle.jms.AQjmsDBConnMgr.<init>(AQjmsDBConnMgr.java:399)
at oracle.jms.AQjmsConnection.<init>(AQjmsConnection.java:249)
at oracle.jms.AQjmsConnectionFactory.createConnection(AQjmsConnectionFactory.java:513)
at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:180)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:474)
... 36 more
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy102 cannot be cast to oracle.jdbc.internal.OracleConnection
at oracle.jms.AQjmsGeneralDBConnection.getProviderKey(AQjmsGeneralDBConnection.java:98)
at oracle.jms.AQjmsGeneralDBConnection.<init>(AQjmsGeneralDBConnection.java:67)
at oracle.jms.AQjmsDBConnMgr.getConnection(AQjmsDBConnMgr.java:566)
... 41 more
I believe that the Cast exception occurs because it is a ProxyConnection[PooledConnection[oracle.jdbc.driver.T4CConnection#40016ce1]]. But I don't know how to fix this.
Change the jdbc library, in my case this fixed it(if don't, try with some other versions):
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc7</artifactId>
<version>12.1.0.2.0</version>
</dependency>
We experienced the same exception when tried to access Oracle AQ from Spring boot. Research indicated that this exception is thrown as the Database connection pool libraries doesn't allow the access to the underlying connection required by the oracle AQ Library.(Both dbcp and tomcat connection pool libraries threw exceptions , not same but similar)
This exception was gone when we removed the Database connection pool libraries from dependencies which lead to an undesirable state of having no Database connection pooling for the entire application .
We noticed that the if we use the following method the exception is not thrown
AQjmsFactory.getQueueConnectionFactory(url, info);
Perhaps connection pooling is missing with solution also, but that is limited to the component which reads from AQ . Other components in the app will have the benefits of the connection pooling
Here is the java config for the Bean definition:
#Bean
public QueueConnectionFactory connectionFactory() throws Exception {
OracleServiceInfo serviceInfo = (OracleServiceInfo) this.cloud().getServiceInfo(NAME_PRIMARY_DS);
Properties info = new Properties();
String url = serviceInfo.getJdbcUrl();
info.put("driver-name", "oracle.jdbc.OracleDriver");
info.put("user", serviceInfo.getUserName());
info.put("password", serviceInfo.getPassword());
return oracle.jms.AQjmsFactory.getQueueConnectionFactory(url, info);
}
#Bean
public JmsTemplate jmsTemplate() throws Exception {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(connectionFactory());
return jmsTemplate;
}
I am not sure yet if this a good solution. But this is definitely one way to get rid of the exception discussed in the question.
Hi it also took me quite some time to make the connection work, but finally it did and this is how:
First make sure the payload of your Oracle AQ Queue table is not set to RAW but preferably to Text: SYS.AQ$_JMS_TEXT_MESSAGE
Next use a OracleAQConfiguration similar as hereunder:
import oracle.jdbc.pool.OracleDataSource;
import oracle.jms.AQjmsFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.jms.JMSException;
import javax.jms.QueueConnectionFactory;
import javax.sql.DataSource;
import java.sql.SQLException;
#Configuration
public class OracleAQConfiguration {
// Values are retrieved from custom added props in Spring application.properties
#Value("${myapplication.datasource.user}")
private String user;
#Value("${myapplication.datasource.password}")
private String password;
#Value("${myapplication.datasource.connectionstring}")
private String connectionstring;
#Bean
/**
* Spring bean with the configuration details of where the Oracle database is containing the QUEUES
*/
public DataSource dataSource() throws SQLException {
OracleDataSource ds = new OracleDataSource();
ds.setUser(user);
ds.setPassword(password);
ds.setURL(connectionstring);
ds.setImplicitCachingEnabled(true);
ds.setFastConnectionFailoverEnabled(true);
return ds;
}
#Bean
/**
* The KEY component effectively connecting to the Oracle AQ system using the datasource input
*/
public QueueConnectionFactory connectionFactory(DataSource dataSource) throws JMSException {
return AQjmsFactory.getQueueConnectionFactory(dataSource);
}
}
Next use a JMSConfiguration similar as hereunder.
Here I read and write to the same queue which is unlikely in a real application integration scenario. But OK for testing
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import javax.jms.ConnectionFactory;
import javax.sql.DataSource;
#Configuration
public class JMSConfiguration {
private static final String QUEUENAME_WRITE = "MYQUEUE";
private static final String QUEUENAME_READ = "MYQUEUE";
#Autowired
private JMSReceiver jmsReceiver;
#Bean
/**
* Spring bean to WRITE/SEND/ENQUEUE messages on a queue with a certain name
*/
public JmsTemplate jmsTemplate(ConnectionFactory conFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDefaultDestinationName(QUEUENAME_WRITE);
jmsTemplate.setSessionTransacted(true);
jmsTemplate.setConnectionFactory(conFactory);
return jmsTemplate;
}
/**
* Spring bean to READ/RECEIVE/DEQUEUE messages of a queue with a certain name
* All of this happens under a code managed transaction
* to commit the change on Oracle (remove of the message from the queue table)
* Reference the application custom code handling the message here
*/
#Bean
public DefaultMessageListenerContainer messageListenerContainer(ConnectionFactory conFactory, DataSource dataSource) {
DefaultMessageListenerContainer dmlc = new DefaultMessageListenerContainer();
dmlc.setDestinationName(QUEUENAME_READ);
dmlc.setSessionTransacted(true);
dmlc.setConnectionFactory(conFactory);
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
dmlc.setTransactionManager(manager);
// Add here our self-written JMS Receiver
dmlc.setMessageListener(jmsReceiver);
return dmlc;
}
}
Finally for handling incoming JMS messages use something like:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.listener.SessionAwareMessageListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
#Component
public class JMSReceiver implements SessionAwareMessageListener {
private static final Logger logger = LoggerFactory.getLogger(JMSReceiver.class);
#Override
public void onMessage(Message message, Session session) throws JMSException {
// We know/assume the Queue Payload type was set to 'TextMessage'
TextMessage txtMessage = (TextMessage) message;
logger.info("JMS Text Message received: " + txtMessage.getText());
// ... further implementation
}
}
the problem is that the AQ code expected an OracleConnection but when pooled the connection wrapped and hence it fails
I have the following class for obtaining a JDBC connection:
package util;
import java.sql.Connection;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class OracleConnection implements AutoCloseable{
private final String oracle_DS_CTX = "java:jboss/oracleDS";
// #Resource(name="java:jboss/oracleDS")
// private DataSource ds; //doesn't work
private Connection _conn;
public OracleConnection() throws SQLException, NamingException{
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(oracle_DS_CTX);
_conn = ds.getConnection();
}
#Override
public void close() throws Exception {
if(_conn != null){
_conn.close();
}
}
public Connection getConnection() throws SQLException {
return _conn;
}
}
I have a problem using the #Resource annotation. Datasource obtained via InitialContext works without any problems but I am not sure what string should I put into resource name (commented out in my code).
I have tried:
#Resource(name="java:jboss/oracleDS")
#Resource(name="oracleDS")
AS is JBOSS AS7
What name did you define in your standalone.xml?
That is the name you need to define in your #Resource
But there's a little trick, you need to set it in the lookup property instead of name.
Here's an example, let's assume my DS jndi is java:jboss/ExampleDS.
#Resource(lookup = "java:jboss/ExampleDS")
private DataSource dataSource;