How to access JNDI DataSource with Play Framework - java

According to this documentation:
https://www.playframework.com/documentation/2.5.x/JavaDatabase#exposing-the-datasource-through-jndi
I simply need another entry in my application.conf to expose a DataSource in JNDI:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.default.jndiName=DefaultDS
I've added "tyrex" % "tyrex" % "1.0.1" to my libraryDepenencies in build.sbt.
From reading several other posts on this, it sound like I should be able to simply use
DataSource ds = (DataSource) play.api.libs.JNDI.initialContext().lookup("DefaultDS");
To fetch the DataSource from JNDI. However, when I try this it throws the following Exception:
javax.naming.NameNotFoundException: DefaultDS not found
at tyrex.naming.MemoryContext.internalLookup(Unknown Source)
at tyrex.naming.MemoryContext.lookup(Unknown Source)
at javax.naming.InitialContext.lookup(InitialContext.java:417)
The main reason I'm trying to do this is so that Quartz can re-use the DataSource/ConnectionPool created by Play instead of defining another in quartz.properties. According to the docs:
http://www.quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigDataSources.html
I should need the following two lines in quartz.properties:
org.quartz.jobStore.dataSource = h2
org.quartz.dataSource.h2.jndiURL = DefaultDS
However, Quartz throws a bunch of exceptions:
java.sql.SQLException: Could not retrieve datasource via JNDI url 'DefaultDS' javax.naming.NameNotFoundException: DefaultDS not found [See nested exception: java.sql.SQLException: Could not retrieve datasource via JNDI url 'DefaultDS' javax.naming.NameNotFoundException: DefaultDS not found]
I'm not sure where to go next. Any help would be appreciated.
Thanks.

Found it.
I simply needed to add jdbc to my libraryDependencies in build.sbt. This created and exposed the DataSource in JNDI.

Related

SpringBoot - How to avoid "Error creating bean with name 'entityManagerFactory'... " when some condition has true value

I have a SpringBoot application (version 1.5.22.RELEASE), and it's using a MariaDB database as datasource to get some data to populate a cache at startup. The data comes from a JPA Repositoy using a collection of a Entity. All the datasource parameters are in the application.properties file.
At this moment, I have to switch the cache datasource to a REST service instead the database. If I have a new property with the value in true, I must use the REST service because the DB will be off; otherwise (with the new property in false), I must use the database, because the REST service will be unavailable.
When I try to test my application with the property in true and the database unavailable, the application throws the following error, and the application doesn't start:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1630) ~[spring-beans-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ~[spring-beans-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481) ~[spring-beans-4.3.25.RELEASE.jar:4.3.25.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312) ~[spring-beans-4.3.25.RELEASE.jar:4.3.25.RELEASE]
Is there a way to avoid this error when the database isnĀ“t available and continue with the application startup?
Thanks in advance. I appreciate your help and feedback.
Yes, you can disable the database autoconfiguration by using code below:
#SpringBootApplication
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(PayPalApplication.class, args);
}
}
And then in the startup, you can connect to the database manually. Detect the availability of the database, fetch cache data or get a connection error, then switch to the backup service.

Error when loading datasource

So I recently have started working in a new proyect. These guys have the Datasource connection declared this way:
Context init = new InitialContext();
Context context = (Context) init.lookup("java:comp/env");
ds = (DataSource)context.lookup("jdbc/WhateverDS");
I don't know why have they declared it this way. I've had always seen it like this:
DataSource ds = new InitialContext().lookup("java:comp/env/jdbc/WhateverDS");
First of all do you guys know why would they do it in two steps?
My second question is that sometimes we get the following error, and if it may have something to do with the code above:
Caused by: javax.naming.NameNotFoundException: The name comp/env is not bound in this context
at org.apache.naming.NamingContext.lookup(NamingContext.java:818)
at org.apache.naming.NamingContext.lookup(NamingContext.java:166)
at org.apache.naming.SelectorContext.lookup(SelectorContext.java:157)
at javax.naming.InitialContext.lookup(InitialContext.java:411)
... 19 more
We are using MySQL over Tomcat using jdbc.
Edit:
They have nothing about the DB declared in web.xml.
The context.xml (among other parameters) looks like this:
Resource driverClassName="com.mysql.jdbc.Driver"
name="jdbc/WhateverDS"
type="javax.sql.DataSource"
url="jdbc:mysql://localhost:3306/someDB"
To answer your first question: there is no effective difference between the two-step and the one-step lookup for the DataSource.
Your second question appears to be a duplicate of javax.naming.NameNotFoundException: Name [comp/env] is not bound in this Context

Flyway and Spring Boot integration

I trying to integrate Flyway for migrations in a Spring Boot project with Hibernate and Spring JPA. I'm getting the following Exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flyway' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.api.FlywayException: Found non-empty schema "PUBLIC" without metadata table! Use init() or set initOnMigrate to true to initialize the metadata table.
My pom.xml is looking like this:
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>3.2</version>
</dependency>
I'm using Hibernate and a config java file for postgres (dev stage) and h2 (local). The signatures are looking like this:
#Bean(initMethod = "migrate")
public Flyway flyway() {
Flyway fly = new Flyway();
fly.clean();
fly.init();
//flyway.setInitOnMigrate(true);
fly.setSchemas("SBA_DIALOG");
//flyway.setLocations("filesystem:src/main/resources/db/migration");
fly.setDataSource(this.dataSource());
fly.migrate();
return fly;
}
#Bean(name = "sbaEntityManagerFactory") #DependsOn("flyway")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
...
I can't find anything about my problem described in this question.
Can anybody help?
Spring-Boot is capable on it's own to do this.
Just add flyway as dependency to your project and spring-boot will pick it up.
Flyway migration will start when the service starts up.
If you already have some tables in the database add:
spring.flyway.baselineOnMigrate = true
in your property file to keep flyway calm when it discovers that some tables already exist. ;-)
Flyway should pick up your datasource. If you need for example another user or something like that for flyway, you can set these properties:
spring.flyway.url: jdbc:postgresql://${db.host}/${db.name}
spring.flyway.user: MYUSER
spring.flyway.password: MYPWD
(Of course add your values! You can use SPEL to reference other properties)
Update
One word of caution: If you use a clustered database you may encounter problems that multiple instances that are started at the same time try to perform the updates at the same time. This is a problem when the table locks don't work, which happened to me using a clustered mariaDB.
For any one who want to solve it in java code you can use :
fly.setBaselineOnMigrate(true);
Edit(22-09-2020)
another solution also is :
spring:
flyway:
baselineOnMigrate: true
validateOnMigrate: false

comp/env/pool not found in context "java:"?

I have a web application trying to access a JNDI declared in WebSphere Application Server.
The JNDI is declared under Object pool managers. However, I'm receiving an error when I access the pool. The error says that comp/env/pool is not found in context "java:".
My code is written as follows:
InitialContext initialContext = new InitialContext();
ObjectPoolManager opm = (ObjectPoolManager)initialContext.lookup("java:comp/env/pool");
Accessing the pool via the code below works:
ObjectPoolManager opm = (ObjectPoolManager)initialContext.lookup("pool");
I'm confused because according to what I've found on the internet, java:comp/env/ is a default prefix for JNDI. So why does it cause an error in my case?
Thank you!
you can only use java:comp/env if you have declared a reference to the Object Pool in your web.xml under the resource-ref section.
See What is resource-ref in web.xml used for? for further explanation.

JNDI path Tomcat vs. Jboss

I have DataSource which is configured on Tomcat 6 in context.xml as MyDataSource.
And I'm fetching it the following way:
DataSource dataSource;
try {
dataSource = (DataSource) new InitialContext().lookup("java:comp/env/MyDataSource");
} catch (NamingException e) {
throw new DaoConfigurationException(
"DataSource '" + url + "' is missing in JNDI.", e);
}
Everything works fine. Now I'm exporting this code to Jboss AP 6. and I configured my dataSource and its connection pool as local-tx dataSource under the same name.
When I'm executing the code above, I'm getting NamingException exception. after some investigation I've found that correct way to call my DataSource under Jboss is
dataSource = (DataSource) new InitialContext().lookup("java:/MyDataSource");
Can anybody explain me why should I omit "comp/env" in my JNDI path under Jboss?
The portable approach for defining data sources is to use a resource reference. Resource references enable you to define the JNDI name for your data source, relative to your application naming context (java:comp/env), and then map that logical reference to the physical resource defined in the application server, whose JNDI name is proprietary to the application server vendor. This approach enables your code and assembly to be portable to any compliant application server.
Step 1: Declare and Lookup Resource Reference
Option 1
This can be done by declaring a resource-ref in your web deployment descriptor (WEB-INF/web.xml):
<resource-ref>
<description>My Data Source.</description>
<res-ref-name>jdbc/MyDataSource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Within your code, you can then lookup this resource using the JNDI name java:comp/env/jdbc/MyDataSource:
dataSource = (DataSource) new InitialContext().lookup("java:comp/env/jdbc/MyDataSource");
This JNDI name will not change regardless of the server where the application is deployed.
Option 2
Alternatively, starting in Java EE 5 (Servlet 2.5), this can be done even easier within your code using the #Resource annotation. This eliminates the need for configuring the resource-ref in your web deployment descriptor (web.xml) and prevents the need to perform an explicit JNDI lookup:
public class MyServlet extends HttpServlet {
#Resource(name = "jdbc/MyDataSource")
private DataSource dataSource;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// dataSource may be accessed directly here since the container will automatically
// inject an instance of the data source when the servlet is initialized
}
This approach has the same results as the previous option, but cuts down on the boilerplate code and configuration in your assembly.
Step 2: Map Resource Reference to Data Source
Then, you will need to use your application server's proprietary approach for mapping the resource reference to the physical data source that you created on the server, for example, using JBoss's custom deployment descriptors (WEB-INF/jboss-web.xml):
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<resource-ref>
<res-ref-name>jdbc/MyDataSource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<jndi-name>java:/MyDataSource</jndi-name>
</resource-ref>
</jboss-web>
Or, for example, using Tomcat's context.xml:
<Resource name="jdbc/MyDataSource" . . . />
You can add to your data source definition the 'jndi-name' tag:
jndi-name - the JNDI name under which the DataSource should be bound.
You can find data source documentation on JBoss wiki: ConfigDataSources

Categories