I've got a WebApp with Tomcat 10, Java11 using Jersey3.
I defined a ConnectionPool in my context.xml for handling the connection to my OracleDB and now I'm trying to access the DataSource within my controller through a #Resource annotation. This should invoke a JNDI-lookup. Unfortunately, I always get a NPE as it seems not to find the resource while running... What am I doing wrong? Or what would the correct mappedName / lookup be?
#Path("/data")
public class DataController {
#Context
ServletContext context;
#Resource(lookup = "java:/jdbc/myDB") //also tried java:/comp/env/jdbc/myDB and mappedName="jdbc/myDB"
protected DataSource ds; //always null
<Context name="myapp">
<Resource type="javax.sql.DataSource"
name="jdbc/myDB"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#//localhost:1521/orcl"
username="xy"
password="xy"/>
According to the tutorials, a ref-link is optional when I define the resource directly in the context.xml.
Thanks for any input!
This link is about jboss, but would seem relevant to you too. It says that according to the specification, Resource annotation to do JNDI lookups would only work with EJBs, so it wouldn't work for your case.
A workaround is to do the programmatic way to see if your datasource is working:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource bean = (DataSource ) envCtx.lookup("jdbc/myDB");
If you can find your datasource you can then try to optimize to avoid "manual" lookup above.
Related
I need to set up a connection pool for an application that uses an embedded Tomcat 8 application server. Normally, I would configure a new resource in the context.xml file. But of course, such a file does not exist when using the embedded version. The definition of the resource would look like this:
<Context>
<Resource name="jdbc/dbname" auth="Container" type="javax.sql.DataSource" username="username" password="password" driverClassName="org.postgresql.Driver" description="Database" url="jdbc:postgresql://localhost:5432/dbname" maxActive="20" maxIdle="3" />
</Context>
Therefore, there must be another solution for adding resources to a context. Is it possible to add the data source resource directly to the Standardcontext in code? If yes, how? Or how else can this be done when using the embedded version?
You can write your own factory and integrate it into Tomcat, and then configure the use of this factory in the element for the web application.
1. Write A Resource Factory Class
You must write a class that implements the JNDI service provider javax.naming.spi.ObjectFactory interface. Every time your web application calls lookup() on a context entry that is bound to this factory (assuming that the factory is configured with singleton="false"), the getObjectInstance() method is called.
To create a resource factory that knows how to produce MyBean instances, you might create a class like this:
package com.mycompany;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
public class MyBeanFactory implements ObjectFactory {
public Object getObjectInstance(Object obj,
Name name2, Context nameCtx, Hashtable environment)
throws NamingException {
// Acquire an instance of our specified bean class
MyBean bean = new MyBean();
// Customize the bean properties from our attributes
Reference ref = (Reference) obj;
Enumeration addrs = ref.getAll();
while (addrs.hasMoreElements()) {
RefAddr addr = (RefAddr) addrs.nextElement();
String name = addr.getType();
String value = (String) addr.getContent();
if (name.equals("foo")) {
bean.setFoo(value);
} else if (name.equals("bar")) {
try {
bean.setBar(Integer.parseInt(value));
} catch (NumberFormatException e) {
throw new NamingException("Invalid 'bar' value " + value);
}
}
}
// Return the customized instance
return (bean);
}
}
In this example, we are unconditionally creating a new instance of the com.mycompany.MyBean class, and populating its properties based on the parameters included in the element that configures this factory (see below). You should note that any parameter named factory should be skipped - that parameter is used to specify the name of the factory class itself (in this case, com.mycompany.MyBeanFactory) rather than a property of the bean being configured.
2. Declare Your Resource Requirements
Next, modify your web application deployment descriptor (/WEB-INF/web.xml) to declare the JNDI name under which you will request new instances of this bean. The simplest approach is to use a element, like this:
<resource-env-ref>
<description>
Object factory for MyBean instances.
</description>
<resource-env-ref-name>
bean/MyBeanFactory
</resource-env-ref-name>
<resource-env-ref-type>
com.mycompany.MyBean
</resource-env-ref-type>
</resource-env-ref>
WARNING - Be sure you respect the element ordering that is required by
the DTD for web application deployment descriptors! See the Servlet
Specification for details.
3. Code Your Application's Use Of This Resource
A typical use of this resource environment reference might look like this:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory");
writer.println("foo = " + bean.getFoo() + ", bar = " +
bean.getBar());
4. Configure Tomcat's Resource Factory
To configure Tomcat's resource factory, add an elements like this to the element for this web application.
<Context ...>
...
<Resource name="bean/MyBeanFactory" auth="Container"
type="com.mycompany.MyBean"
factory="com.mycompany.MyBeanFactory"
singleton="false"
bar="23"/>
...
</Context>
Resource Link:
Adding Custom Resource Factories
How to Configure JNDI DataSource in Tomcat 8 with Java Configuration:
For adding external resource in tomcat 8, you can follow this link: Adding external resources to class-path in Tomcat 8
The question was about embedded tomcat.
How can i set simple string value in configuration of tomcat and then read in java application?
context.xml
<ResourceLink name="global/test" global="testing" type="java.lang.String" />
server.xml
<Enviroment name="testing" value="myUser" type="java.lang.String"/>
web.xml in application
<resource-env-ref>
<resource-env-ref-name>global/test</resource-env-ref-name>
<resource-env-ref-type>java.lang.String</resource-env-ref-type>
</resource-env-ref>
in my java application
public String getValue(){
return new JndiDataSourceLookup().getDataSource("global/test").toString();
}
When i Run tomcat, i see these errors...
org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException: Failed to look up JNDI DataSource with name 'global/test'; nested exception is javax.naming.NameNotFoundException: Name [global/test] is not bound in this Context. Unable to find [global].
javax.naming.NameNotFoundException: Name [global/test] is not bound in this Context. Unable to find [global].
In your web.xml use,
<env-entry>
<description>Sample env entry</description>
<env-entry-name>isConnected</env-entry-name>
<env-entry-type>java.lang.Boolean</env-entry-type><!--order matters -->
<env-entry-value>true</env-entry-value>
</env-entry>
In code,
try {
Context initCxt = new InitialContext();
Boolean isConn = (Boolean)initCxt.lookup("java:comp/env/isConnected");
System.out.println(isConn.toString());
// one could use relative names into the sub-context
Context envContext = (Context) initCxt.lookup("java:comp/env");
Boolean isConn2 = (Boolean)envContext.lookup("isConnected");
System.out.println(isConn2.toString());
} catch (NamingException e) {
e.printStackTrace();
}
Have a look here Naming service tutorial to get a good understanding of InitialContext and JNDI.
I don't know what's inside JndiDataSourceLookup().getDataSource("global/test") but by the name of it, it should return a DataSoruce not a string.
If your lookup is local, simply do
Context ctx = new InitialContext();
String s = (String) ctx.lookup("global/test");
or if you are in a javaee container,
#Resource(name="global/test")
String testString;
and finally in your ejb-jar.xml
<env-entry>
<description>The name was explicitly set in the annotation so the classname prefix isn't required</description>
<env-entry-name>global/test</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>StringValue</env-entry-value>
</env-entry>
Refer this link: http://tomee.apache.org/examples-trunk/injection-of-env-entry/README.html
Your configuration of context.xml, server.xml, and web.xml aren't gonna work.
Is there a way in java to access the values of the resource tag given in the context.xml from the program?
<Context ...>
...
<Resource name="jdbc/EmployeeDB" auth="Container"
type="javax.sql.DataSource" username="dbusername" password="dbpassword"
driverClassName="org.hsql.jdbcDriver" url="jdbc:HypersonicSQL:database"
maxActive="8" maxIdle="4"/>
...
</Context>
I know that we can get the the datasource the following way:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource)
envCtx.lookup("jdbc/EmployeeDB");
Connection conn = ds.getConnection();
... use this connection to access the database ...
conn.close();
But is there a way to print the userName and password in the program that i give in the resource tag?
You can try to cast it to actual implementation of the resource (print out the name with .getClass().getName()) and use the methods provided there. For example tomcat's BasicDataSource has getPassword getUser methods.
I have class called ConnectionManager
public class ConnectionManager{
public static getDBConnection()
{
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mydb");
return ds.getConnection();
}
}
When it tried to call this class from jsp page, servlet, jax-rs restful service, it just works fine.
However, I go the following exception when i try to call ConnectionManager.getDBConnection() from JAX-WS web service:
Name [comp/env/jdbc/mydb] is not bound in this Context. Unable to find [comp].
Please note that i defined the Resource inside TomEE_HOME/conf/context.xml:
<Resource name="jdbc/mydb" auth="Container" type="javax.sql.DataSource"
username="user" password="pass"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
url="jdbc:sqlserver://10.x.x.x:xxxx;databaseName=MyDBName"/>
Please advice.
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