DataSource initialization error within liberty profile server - java

I am trying to set up a DataSource in liberty profile server, but I'm getting a NullPointerException (on my ds variable below) when my code tries to use it.
The relevant code, and server.xml entries are below.
Interestingly, if I change the jndiName to java:comp/env/jdbc/Oracle I get an IllegalArgumentException on server startup, but with the config below it doesn't even seem to try to activate the DataSource...
public abstract class DAOBase {
//#Resource(name = "jdbc/Oracle", type = javax.sql.DataSource.class, shareable = true, authenticationType = AuthenticationType.CONTAINER)
#Resource(lookup = "jdbc/Oracle")
private DataSource ds;
protected Connection getConnection() throws SQLException {
Connection conn = ds.getConnection();
return conn;
}
My server.xml config:
<featureManager>
<feature>jsp-2.2</feature>
<feature>jaxrs-1.1</feature>
<feature>localConnector-1.0</feature>
<feature>appSecurity-2.0</feature>
<feature>jpa-2.0</feature>
<feature>jdbc-4.0</feature>
<feature>jndi-1.0</feature>
</featureManager>
<library id="OracleJDBC_ID">
<fileset dir="C:\src\main\lib" includes="ojdbc6.jar"/>
</library>
<jdbcDriver id="OracleDriver" libraryRef="OracleJDBC_ID"/>
<dataSource jdbcDriverRef="OracleDriver" jndiName="jdbc/Oracle">
<properties.oracle URL="jdbc:oracle:thin:#ldap://oid:***/opl***,cn=OracleContext,dc=****,dc=com" password="****" user="*****"/>
</dataSource>
Error in the log:
[ERROR ] CWWKE0701E: [com.ibm.ws.jdbc.dataSource(200)] The modified method has
thrown an exception Bundle:com.ibm.ws.jdbc(id=82) java.lang.IllegalArgumentException:
J2CA8011E: Value java:comp/env/jdbc/Oracle is not supported for jndiName on dataSource
at com.ibm.ws.jdbc.DataSourceService.activate(DataSourceService.java:209)
at [internal classes]
EDIT:
The code is in the base class for our Data Access layer. We're calling this in a RESTful web service via a very plain initialization:
AuditDAO auditDAO = new AuditDAO();

The #Resource will not work in POJO unless you activate CDI. Try the following:
add beans.xml file to the WEB-INF folder, and add CDI feature
<feature>cdi-1.0</feature>
inject auditDAO to your web service using #Inject AuditDAO auditDAO
use the following reference in the dao
#Resource(name="jdbc/Oracle", lookup = "jdbc/Oracle")
private DataSource ds;

Related

Connection closes using JNDI in Tomcat

Running under a Tomcat 9 and JDK 1.8, using Spring 5, I am trying to configure a JNDI connection to get a DataSource.
If I configure Spring, through XML, I get my DataSource and everything seems to work fine. I configured the DataSource in my applicationContext.xml, in this way:
<jee: jndi-lookup id = "dataSource" jndi-name = "jdbc / yages"
resource-ref = "true" />
When I use the AbstractAnnotationConfigDispatcherServletInitializer class to initialize Spring, my DataSource is created but when I try to catch the connection it gives me the following error:
java.sql.SQLException: Data source is closed
at org.apache.tomcat.dbcp.dbcp2.BasicDataSource.createDataSource (BasicDataSource.java:2049)
I try to create the DataSource with this function:
#Bean (name = "dataSource")
public DataSource dataSource (Environment env) throws NamingException
{
DataSource datasource = null;
try {
JndiDataSourceLookup lookup = new JndiDataSourceLookup ();
datasource = lookup.getDataSource ("jdbc/yages");
datasource.getConnection ();
return datasource;
} catch (SQLException ex) {
ex.printStackTrace ();
}
return datasource;
}
It seems that the DataSource is created correctly, but the connection to the database seems to be closed.
However, if I use the DataSource, configure it through XML, it works well for me, which is why I assume that it is not a problem neither of the connection to the database nor of the configuration of Tomcat.
Any idea why the connection is closed?
Thank you.
I found the solution.
It is to create the Bean with this instruction
#Bean(name = "dataSource", destroyMethod = "")
The problem is that Spring to do an undeploy, destroy the Bean and close the connection. To avoid this, I have to change the default behavior of Spring.

Working with DataSource, JNDI API in IntelliJ

I'm trying to understand establishing a database-connection with a DataSource Object and the JNDI API.
I'm working with Intellij UE and have a local Tomcat-8- and Postgres-Server running.
I proceed as mentioned in the Oracle Java Documentation:
Creating Instance of DataSource Class and Setting its Properties
org.postgresql.ds.PGSimpleDataSource dataSource = new org.postgresql.ds.PGSimpleDataSource();
dataSource.setServerName("localhost");
dataSource.setDatabaseName("db01");
dataSource.setUser("jwi");
dataSource.setPassword("password");
Registering DataSource Object with Naming Service That Uses JNDI API
Context ctx = null;
try {
ctx = new InitialContext();
ctx.bind("jdbc/localDB", dataSource);
} catch (NamingException e) {
e.printStackTrace();
}
The Oracle Documentation says:
With the properties set, the system administrator can register the BasicDataSource object with a JNDI (Java Naming and Directory Interface) naming service.
So my first Question is: What means to register a DataSource? Is my code obove already the registration of an DataSource Object to JNDI?
Using Deployed DataSource Object
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("jdbc/localDB");
dbCon = ds.getConnection();
...
In this code cutting IntelliJ always claims, that it can't resolve the method getConnection().
The Oracle Documentation says:
After a basic DataSource implementation is deployed by a system administrator, it is ready for a programmer to use.
So my second Question is: What exactly means deployed in this case? Creating a DataSource Instance and execute the registration with JDNI? Or does deployed mean the Tomcat context.xml and web.xml configuration (Tomcat 8 JNDI How-To)?
I'd really appreciate if anybody has a good step by step instruction for this issue, in fact that the Oracle Documentation isn't really clear about some points imho.
for the second question, deployed means that your datasource is declared in the context.xml in tomcat.
Here is an example of an oracle database (you have to change the driver for postgres) :
<Resource name="jdbc/myoracle" auth="Container"
type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#127.0.0.1:1521:mysid"
username="scott" password="tiger" maxTotal="20" maxIdle="10"
maxWaitMillis="-1"/>
After that, you can code the java part, for that you can watch this link http://www.javapractices.com/topic/TopicAction.do?Id=127
For a complete example, there's a good tutorial here http://alvinalexander.com/blog/post/java/how-configure-tomcat-dbcp-connection-pool-pooling-postgres.
Hope this help

Configure Multiple DataSource in Spring Boot with JNDI

I want to manage multiple DataSource using your Application Servers built-in features and access it using JNDI. I am using Spring boot with Spring JPA data.
I am able to configure the application.properties for single datasource:
spring.datasource.jndi-name=jdbc/customers
And my configuration in context.xml file as below:
<Resource name="jdbc/customer" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/customer"/>
Everything works fine.
But when I am unable to configure for two datasource.
I am sure on the configuration in context.xml file:
<Resource name="jdbc/customer" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/customer"/>
<Resource name="jdbc/employee" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/employee"/>
I am in doubt about the application.properties file configuration.
I tried the below options with no success:
spring.datasource.jndi-name=jdbc/customers,jdbc/employee
Please let me know any details on Spring boot with JNDI for multiple data source. I was looking for this configuration for days now.
Second Trial As per Spring Boot Documentation
spring.datasource.primary.jndi-name=jdbc/customer
spring.datasource.secondary.jndi-name=jdbc/project
Configuration class.
#Bean
#Primary
#ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
The application does not get started. Though the tomcat server is getting started. No errors are printed in the log.
Third Trial: With JndiObjectFactoryBean
I have the below application.properties
spring.datasource.primary.expected-type=javax.sql.DataSource
spring.datasource.primary.jndi-name=jdbc/customer
spring.datasource.primary.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.datasource.primary.jpa.show-sql=false
spring.datasource.primary.jpa.hibernate.ddl-auto=validate
spring.datasource.secondary.jndi-name=jdbc/employee
spring.datasource.secondary.expected-type=javax.sql.DataSource
spring.datasource.secondary.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.datasource.secondary.jpa.show-sql=false
spring.datasource.secondary.jpa.hibernate.ddl-auto=validate
And the below java configuration:
#Bean(destroyMethod="")
#Primary
#ConfigurationProperties(prefix="spring.datasource.primary")
public FactoryBean primaryDataSource() {
return new JndiObjectFactoryBean();
}
#Bean(destroyMethod="")
#ConfigurationProperties(prefix="spring.datasource.secondary")
public FactoryBean secondaryDataSource() {
return new JndiObjectFactoryBean();
}
But still getting error:
Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'primaryDataSource' defined in class path resource [com/web/initializer/MvcConfig.class]: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [jdbc/customer] is not bound in this Context. Unable to find [jdbc].
Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'secondaryDataSource' defined in class path resource [com/web/initializer/MvcConfig.class]: Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [jdbc/employee] is not bound in this Context. Unable to find [jdbc].
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:686)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
at org.springframework.boot.context.web.SpringBootServletInitializer.run(SpringBootServletInitializer.java:117)
at org.springframework.boot.context.web.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:108)
at org.springframework.boot.context.web.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:68)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:175)
Update:
Trial using the below properties file:
spring.datasource.primary.expected-type=javax.sql.DataSource
spring.datasource.primary.jndi-name=java:comp/env/jdbc/customer
spring.datasource.secondary.jndi-name=java:comp/env/jdbc/employee
spring.datasource.secondary.expected-type=javax.sql.DataSource
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=validate
It creates all the tables in customer schema, but fails trying to find the other tables also.(from the second schema)
This is the solution for your third trial a little bit modified.
Consider this solution (Spring Boot 1.3.2):
application.properties file:
spring.datasource.primary.jndi-name=java:/comp/env/jdbc/SecurityDS
spring.datasource.primary.driver-class-name=org.postgresql.Driver
spring.datasource.secondary.jndi-name=java:/comp/env/jdbc/TmsDS
spring.datasource.secondary.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.show-sql=false
Configuration:
#Configuration# EnableConfigurationProperties
public class AppConfig {
#Bean# ConfigurationProperties(prefix = "spring.datasource.primary")
public JndiPropertyHolder primary() {
return new JndiPropertyHolder();
}
#Bean# Primary
public DataSource primaryDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource(primary().getJndiName());
return dataSource;
}
#Bean# ConfigurationProperties(prefix = "spring.datasource.secondary")
public JndiPropertyHolder secondary() {
return new JndiPropertyHolder();
}
#Bean
public DataSource secondaryDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource(secondary().getJndiName());
return dataSource;
}
private static class JndiPropertyHolder {
private String jndiName;
public String getJndiName() {
return jndiName;
}
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
}
}
And then you can follow guide http://docs.spring.io/spring-data/jpa/docs/1.3.0.RELEASE/reference/html/jpa.repositories.html to use your datasources with jpa repositories.
You could use a plain JndiObjectFactoryBean for this. Simply replace the DataSourceBuilder with a JndiObjectFactoryBean should do the trick.
Java configuration
#Bean(destroyMethod="")
#Primary
#ConfigurationProperties(prefix="datasource.primary")
public FactoryBean primaryDataSource() {
return new JndiObjectFactoryBean();
}
#Bean(destroyMethod="")
#ConfigurationProperties(prefix="datasource.secondary")
public FactoryBean secondaryDataSource() {
return new JndiObjectFactoryBean();
}
Properties
datasource.primary.jndi-name=jdbc/customer
datasource.primary.expected-type=javax.sql.DataSource
datasource.secondary.jndi-name=jdbc/project
datasource.secondary.expected-type=javax.sql.DataSource
You can set every property of the JndiObjectFactoryBean using the #ConfigurationProperties annotation. (See the expected-type I added, but you could also set cache or lookup-on-startup etc.).
Note: when doing a JNDI lookup set the destroyMethod to an "" else you might get the situation that when the application is shutdown your JNDI resource is getting closed/shutdown as well. This is not something you want in a shared environment.
It works for me and contains less code
#Configuration
public class Config {
#Value("${spring.datasource.primary.jndi-name}")
private String primaryJndiName;
#Value("${spring.datasource.secondary.jndi-name}")
private String secondaryJndiName;
private JndiDataSourceLookup lookup = new JndiDataSourceLookup();
#Primary
#Bean(destroyMethod = "") // destroy method is disabled for Weblogic update app ability
public DataSource primaryDs() {
return lookup.getDataSource(primaryJndiName);
}
#Bean(destroyMethod = "") // destroy method is disabled for Weblogic update app ability
public DataSource secondaryDs() {
return lookup.getDataSource(secondaryJndiName);
}
}
In my case, when i start my application using Spring Boot App, the database configurations are read on application-dev.properties, when I publish on tomcat, using datasources, was necessary add a validation to check if my profile is prod, in this case, i do a jndi lookup
#Bean(name = "dsName")
#ConfigurationProperties("ds.datasource.configuration")
public DataSource dataSource(#Qualifier("dsProperties") DataSourceProperties db1DataSourceProperties)
{
if(Arrays.asList(environment.getActiveProfiles()).contains("prod"))
{
final JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource("java:comp/env/jdbc/DS1");
}
else
{
return db1DataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}
The concise way I get success and explore more
set up many jndi resources in external tomcat , that you can start/stop in eclipse.Note- double click the tomcat in eclipse and select use workspace metedata , means dont deploy the app to tomcat webapp folder. Add jndi resources in respective eclipse server files( context.xml - ResourceLink, server.xml - Resource , web.xml - resource-ref).
no need to set spring.datasource.* in application.properties. since jndi-contest which is a datasource type( i.e. type="javax.sql.DataSource") is exported to external server.
in SpringBootApplication annotated class , create the datasource beans from all the jndi resources(those setup as per #1) through jndi lookup
#Bean(name = "abcDataSource")
public DataSource getAbcDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource("java:comp/env/jdbc/abcDataSource");
return dataSource;
}
if spring jdbc used in your project then provide the above datasource to create a jdbcTemplate bean
#Bean(name = "jdbcAbcTemplate")
public JdbcTemplate abcJdbcTemplate(#Lazy #Qualifier("abcDataSource")
DataSource refDS)
{
return new JdbcTemplate(refDS);
}
just autowire a property of DataSource type and get systemout its details to explore more.
While the above answers are good I am going to add one more to illustrate a deadly gotcha if mixing jndi and full data connection configuration. In a typical development environment you may fully qualify the database connection in your local dev environment then use jndi as you push to qa, etc.. Your
application .properties looks like so:
spring.datasource.url=jdbc:sqlserver://server.domain.org:1433;databaseName=dbxx
spring.datasource.username=userxxyyzz
spring.datasource.password=passxxyyzz
spring.datasource.platform=mssql
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
and application-qa.properties like so:
spring.datasource.jndi-name=java:jboss/datasources/dbxx
The problem arises when you have to define your own beans to have multiple datasources. If you use the default Spring managed datasource then it automatically detects jndi vs fully qualified connection and returns a datasource with no change needed in the application code. If you define your own datasource it no longer does this. If you have application.properties like so:
spring.custom.datasource.url=jdbc:sqlserver://server.domain.org:1433;databaseName=dbxx
spring.custom.datasource.username=userxxyyzz
spring.custom.datasource.password=passxxyyzz
spring.custom.datasource.platform=mssql
spring.custom.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
and application-qa.properties like so:
spring.custom.datasource.jndi-name=java:jboss/datasources/dbxx
with a datasource bean like so, as suggested in Spring docs https://docs.spring.io/spring-boot/docs/2.1.11.RELEASE/reference/html/howto-data-access.html
#Primary
#Bean(name="customDataSourcePropertiesBean")
#ConfigurationProperties("spring.custom.datasource")
public DataSourceProperties customDataSourceProperties() {
return new DataSourceProperties();
}
#Primary
#Bean(name="customDataSourceBean")
#ConfigurationProperties("spring.custom.datasource")
public HiakriDataSource customDataSource(#Qualifier("customDataSourcePropertiesBean") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
This datasource builder does not attempt to read the jndi config in application-qa.properties and silently fails back to application.properties returning the WRONG database connection. Resolution is fairly simple - test which environment you are in and customize the type of database connection created. Debugging this was a bear, as the symptom was that the app appeared to be ignoring application-qa.properties. I share to spare others the pain. Add spring.profiles.active=qa etc. to your properties files to know which environment you are in then:
#Value("${spring.profiles.active}")
String profile;
#Value("${spring.custom.jndi-name}")
String jndi;
#Primary
#Bean(name="customDataSourcePropertiesBean")
#ConfigurationProperties("spring.custom.datasource")
public DataSourceProperties customDataSourceProperties() {
return new DataSourceProperties();
}
#Primary
#Bean(name="customDataSourceBean")
#ConfigurationProperties("spring.custom.datasource")
public DataSource customDataSource(#Qualifier("customDataSourcePropertiesBean") DataSourceProperties properties) {
if(profile.equals("localhost")) {
return DataSourceBuilder
.create()
.username(properties.getDataUsername())
.password(properties.getPassword())
.url(properties.getUrl())
.driverClassName(properties.getDriverClassName())
.build();
}else {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource(jndi);
}
}

Error casting T4CConnection to OracleConnection

Spring application using JNDI lookup to get datasource as following:
#Bean(name = "dataSource1", destroyMethod = StringUtils.EMPTY)
public DataSource dataSource() {
final JndiDataSourceLookup lookup = new JndiDataSourceLookup();
lookup.setResourceRef(true);
return lookup.getDataSource(this.environment.getProperty(Constants.DB_JNDI_NAME_ES));
}
and getting a connection from the datasource as follows :
#Autowired
#Qualifier("dataSource1")
private DataSource ds;
Connection conn = null;
conn = this.ds.getConnection();
But when i pass that connection to StructDescriptor it throws classCastException as follows:
StructDescriptor desc1 =
StructDescriptor.createDescriptor("MSAF_DBA.AMOUNT_DUE_OBJ",conn);
java.lang.ClassCastException: weblogic.jdbc.wrapper.PoolConnection_oracle_jdbc_driver_T4CConnection cannot be cast to oracle.jdbc.OracleConnection
at oracle.sql.StructDescriptor.createDescriptor(StructDescriptor.java:101)
at oracle.sql.StructDescriptor.createDescriptor(StructDescriptor.java:72)
at com.ceiwc.es.policyholder.dao.PolicyHolderDaoImpl.getAmountDue(PolicyHolderDaoImpl.java:290)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
My understanding is the getConnection() returns a T4CConnection where as OracleConnection is required. Tried couple of ways to get the oracleConnection but cant seem to get it. Any help would be appreciated.
I believe this has to do with the contents of your war/ear file. Do not package in the Oracle driver .jar file.
Specifically, if you have ojdbc6.jar in your war file (or the equivalent) it will cause conflicts. It is fine to use that jar for compilation but you won't want it in your classpath as it is already in the Weblogic classpath by default.
See these links for similar info: here and here

camel cdi, lookup datasource provided by glassfish

I managed to get camel working with its CDI module, but I'm unable to register datasources in the registry. I basically have a sql route that looks like this:
sql:SELECT * FROM INBOX_DFEP WHERE id > :#lastID?dataSource=jdbc/DFEP
my application server is glassfish 4, and I've registered a jdbc resource with that name, as I usually do with JPA. Anyway camel does not seems to be able to lookup that datasource for me.
org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: jdbc/DFEP of type: javax.sql.DataSource
what am I doing wrong? how do I register a datasource in the camel registry using CDI?
You can try this:
public class DataSourceFactory {
#Resource(lookup = "jdbc/DFEP")
private javax.sql.DataSource datasource;
#Produces
#Named("queryDatasource")
public javax.sql.DataSource getDatasource() {
return datasource;
}
}
After that you only have to change your toute to:
sql:SELECT * FROM INBOX_DFEP WHERE id > :#lastID?dataSource=queryDatasource

Categories

Resources