Optional datasources in java web applications - java

I'm creating a java web application which should run on multiple application servers (jetty, tomcat, websphere, jboss). There is an optional datasource in this application. If it is configured the application will use it, otherwise some functionality in the application will be disabled.
The problem is, if I define the resource-ref in the web.xml, it will be required to be defined in the application context. If I don't define it in the web.xml, then when I try to deploy the application in websphere, there is no way to reference the datasource in the application.
Is there a way to make websphere aware of the optional datasource without always having to define it in the application context?

Don't use an "optional data source". Instead, always define the resource-ref, and add an env-entry:
#Resource(name="useDataSource")
private boolean useDataSource;
...or corresponding XML:
<env-entry>
<env-entry-name>useDataSource</env-entry-name>
<env-entry-type>java.lang.Boolean</env-entry-type>
<env-entry-value>false</env-entry-value>
</env-entry>
The value of the env-entry can be changed at deployment time.

Related

Make <resource-ref/> optional in the sense that deployment does not fail when a JNDI name is not pointing to a valid resource

We have an EJB application with a jboss-ejb3.xml which defines resource-refs for JNDI mapped resources. In our use-case not all resources are available in all environments. Since jboss-ejb3.xml cannot be externalized, I was hoping if there was a way the definitions can be somehow marked optional so the deployment does not fail when a mapped resource is not configured?
For example, we have our jboss-ejb3.xml defined as:
<resource-ref>
<res-ref-name>cache/ErrorDataCache</res-ref-name>
<res-type>java.util.Map</res-type>
<jndi-name>java:jboss/infinispan/cache/ejb/ErrorDataCache</jndi-name>
<!-- what to add to make this optional ?? -->
</resource-ref>
In our DEV JBOSS EAP 7.2 server, we are configuring a back-end cache so when server starts up and deploys the application, it registers the JNDI context (java:comp/env/cache/ErrorDataCache) to the mapped resource (java:jboss/infinispan/cache/ejb/ErrorDataCache).
However, in our production servers of same EAP version, we are forbidden to cache these values. Also we are forbidden to change the EAR archive to maintain a separate jboss-ejb3.xml.
I also tried using system properties substitution/placeholder, but there has to be a default fall-back otherwise it still fails with error "unresolved property value of app.module.env.${mappedjndi.errordatacache}"
<resource-ref>
<res-ref-name>cache/ErrorDataCache</res-ref-name>
<res-type>java.util.Map</res-type>
<jndi-name>${mappedjndi.errordatacache:"what default value??"}</jndi-name>
</resource-ref>
I have read that there is an <ignore-dependency/> tag which I tried, but it fails with error: invalid tag , not defined in the XSD.

What is java:comp/env scope rules?

I understand that java:comp/env is the node in the JNDI tree where you can find properties for the current Java EE component (a webapp, or an EJB), and also I know that each EJB has its own component environment ,also there is java:global and java:app and a java:module depending on that I have some questions
when I use Context envContext = (Context)initContext.lookup("java:comp/env"); to get initContext what Context exactly I get (global,app,module,webApp or EJB Context)?
Is there is certain rules applied for searching different scopes?
Let's say I have web application with many EJBs,does this means that I have many Initial Contexts (one for webApp and one for each EJB) or all of these resources are somehow collected under one context java:comp/env?
Many Thanks.
when I use Context envContext = (Context)initContext.lookup("java:comp/env"); to get initContext what Context exactly I get (global,app,module,webApp or EJB Context)?
Quoting from TomEE documentation http://tomee.apache.org/lookup-of-other-ejbs-example.html
In a webapp, the java:comp/env namespace is shared by all servlets.
This is essentially equivalent to the java:module namespace in Java EE
6. Understand there is a conflict in definition here and that for EJBs, java:comp is scoped at the component (the EJB itself) not the
module as with webapps.
Is there is certain rules applied for searching different scopes?
Quoting from JavaEE 6 Tutorial http://docs.oracle.com/cd/E19798-01/821-1841/girgn/index.html
The java:global JNDI namespace is the portable way of finding remote
enterprise beans using JNDI lookups. The java:module namespace is used
to look up local enterprise beans within the same module. The
java:app namespace is used to look up local enterprise beans packaged
within the same application. That is, the enterprise bean is packaged
within an EAR file containing multiple Java EE modules.
Let's say I have web application with many EJBs,does this means that I have many Initial Contexts (one for webApp and one for each EJB) or all of these resources are somehow collected under one context java:comp/env?
Based on above links, you will have not have many contexts.
Regarding question 3 on the scope of java:comp/env in presence of EJBs.
Traditionally (before JEE 6) java:comp/env was module level for war modules and EJB level for EJBs in jar modules.
In that model, one has to define environment entries (via resource-ref and env-entry in ejb-jar.xml, or for resource-refs since JEE5 one could use #Resource at the class level) for every single EJB.
<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>B1</ejb-name>
<ejb-class>p1.B1</ejb-class> <!-- ejb-class should be skipped if bean is already defined via annotation -->
<env-entry>
<env-entry-name>entry1</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>value1</env-entry-value
</env-entry>
</session>
</enterprise-beans>
</ejb-jar>
Since JEE 6, one can deploy EJBs as part of war module. Whether war module is deployed directly or is part or ear module, it has a single java:comp/env namespace shared between all servlets, EJBs and any other code within that module. One can define environment entries in web.xml:
<web-app>
<env-entry>
<env-entry-name>entry1</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>value1</env-entry-value
</env-entry>
</web-app>
In this war based model, one could still have ejb-jar.xml to configure other aspects of EJBs, but env-entry for a given bean in ejb-jar.xml would end up injecting the environment value for all other beans in the war.
So, I would always use war archives for everything (possibly packaged in ear).
When I use Context envContext = (Context)initContext.lookup("java:comp/env"); to get initContext what Context exactly I get (global,app,module,webApp or EJB Context)?
You get the one you asked for, the one you correctly described as 'the current Java EE component (a webapp, or an EJB)'.
Is there is certain rules applied for searching different scopes?
You search within the scope you specify. I don't know what else your question could mean.
Let's say I have web application with many EJBs, does this means that I have many Initial Contexts (one for webApp and one for each EJB) or all of these resources are somehow collected under one context java:comp/env?
The question doesn't make sense. You have as many InitialContexts as you create. They are objects. What they refer to depends on what you lookup within that InitialContext. If you keep looking up "the current Java EE component (a webapp, or an EJB)", you will keep getting exactly that. All 'these resources' are 'collected' under their own names respectively, within java:comp/env.
As far of my knowledge by default the scope will be Global

Optional JNDI Binding For Websphere Web Application

I would like to have optionally supplied information available to my webapp when deployed to Websphere (we are using 8.5) via the application's JNDI context.
I know that I can put a resource-ref or resource-env-ref in my web.xml but when I do that WAS will require me to supply a binding for it and deployment time.
My application will look in JNDI for certain values and adjust its behavior if found, but will function fine with default behaviour if it does not find values in JNDI. How, in WAS, do I supply a binding (just for a string or a URL) for my webapp without declaring a dependency on it in a resource-ref or resource-env-ref in my web.xml.
I know how to do this in Tomcat, I just put a Environment entry in the context.xml, like this:
<Environment
name="com.myorg.config"
value="http://localhost:8081/suff"
type="java.lang.String"/>
You can use an #Resource String lookupName; to accomplish the same in a portable manner. You will be prompted for a value at deployment time, but you can specify nothing, which will cause no value to be injected.

How to read external properties files in WebSphere?

I developed a sample web application which will read the data from an external properties file. The properties file is in the source folder in my system and is not included inside the WAR file.
The property file is accessed like this:
Properties prop = new Properties();
//File f1 = new File("Property.properties");
prop.load(getClass().getClassLoader().getResourceAsStream("Property.properties"));
How do I access this property file externally inside the WAR file?
What changes have to be made in the code to read it in the WAR file?
I think the most versatile approach is to define a simple environment entry as described in the section EE.5.4 Simple Environment Entries of Java™ Platform, Enterprise Edition (Java EE) Specification, v5.
From the section (page 68):
A simple environment entry is a configuration parameter used to
customize an application component’s business logic. The environment
entry values may be one of the following Java types: String,
Character, Byte, Short, Integer, Long, Boolean, Double, and Float.
You may also use URL connection factory as described in the section EE.5.6.1.4 Standard Resource Manager Connection Factory Types of the specification.
The Application Component Provider must use the java.net.URL resource
manager connection factory type for obtaining URL connections.
Both require a definition of a resource reference in the deployment descriptor WEB-INF/web.xml of your web application so you can inject the value using #Resource or use JNDI API with java:comp/env as the entry point.
The benefit is that you can change the configuration of your web application without having to recompile the code as well as let you change it using an application server's administrative tools your admins are accustomed with.
In web.xml you define the resource reference.
<resource-ref>
<res-ref-name>propertiesURL</res-ref-name>
<res-type>java.net.URL</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-ref>
<res-ref-name>propertiesPath</res-ref-name>
<res-type>java.lang.String</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Then in your code you use the following to access the values:
#Resource
String propertiesPath;
#Resource
URL propertiesURL;
With this you met the requirements of Java EE and you can use propertiesPath or propertiesURL as if they were passed as input parameters to your methods.
Now, it's time to meet expectations of WebSphere Application Server.
What you defined are logical names that need to be mapped to their administered names (an application server knows about and can provide to the application).
In WebSphere Application Server you use WebSphere Binding descriptor WEB-INF/ibm-web-bnd.xml with the following configuration:
<?xml version="1.0" encoding="UTF-8"?>
<web-bnd xmlns="http://websphere.ibm.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-web-bnd_1_1.xsd"
version="1.1">
<virtual-host name="default_host" />
<resource-ref name="propertyURL" binding-name="propertyURL" />
<resource-ref name="propertyURL" binding-name="propertyURL" />
</web-bnd>
When the application gets deployed WAS allows you to map these mappings to its administered resources. Use the ISC console to define values of the environment entries and map them to the application.
It has became easier with WebSphere Liberty Profile. I described the mechanism as offered by WLP in my article Using #Resource to access JNDI in WebSphere AS 8.5 Liberty Profile.
You have three options:
configure the Websphere to include the directory which contains the property file in the classpath. Don't know how to do it, but I'm sure it is possible, since our application does the same thing
include the property file in the war archive. You probably don't want to do that.
instead using the classloader to load the property file use the file api with an absolute path. I'm not completely sure WAS does allow that, but it is a bad idea anyway, because it makes your application very dependent on things that it really shouldn't care about, such as the installation path of your application.
WebSphere has two folders on the classpath, properties can be loaded from there:
Enterprise Applications > myear > Manage Modules > myjar.jar > Class loader viewer
4 - Extension - com.ibm.ws.bootstrap.ExtClassLoader
file:/projekte/IBM/WebSphere/AppServer-8.5/classes/
file:/projekte/IBM/WebSphere/AppServer-8.5/lib/

Using a JNDI datasource created by another application with Tomcat

I have a .properties file in my application which contains dataSource properties.
I set up a JNDI reference to this dataSource using the following code :
// first I create MyDataSource from the properties found in the .properties file
//then :
Context initContext = new InitialContext();
initContext.createSubcontext("jdbc");
initContext.createSubcontext("jdbc/oracle");
initContext.rebind(jdbc/oracle/myDataSource, MyDataSource);
If I use a lookup in this application, the dataSource is found :
Context initContext = new InitialContext();
BasicDataSource dataSource =
(BasicDataSource) initContext.lookup("jdbc/oracle/myDataSource")
//everything works fine and I can use my dataSource to getConnection,
//requests, etc...
Now I would like to use this dataSource in another application. But if I do the same lookup than previously, I don't find myDataSource (whereas there is still the previous application in tomcat and the jndi binding is done on start-up with the help of a listener).
How can I get myDataSource in this second application, given that I can't use a Tomcat's resource in server.xml or a context.xml file (for different reasons I have to use this .properties file)?
Thanks
"local" JDNI directories are read-only in Tomcat. Nevertheless, you can bind "global" JNDI resources in a LifecycleListener, and then "link" them to your context(s)(*):
You need to implement org.apache.catalina.LifecycleListener http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/LifecycleListener.html
Then register it in your server.xml like this (along with the other listeners):
<Listener className="yourlistener.YourLifecycleListener"/>
Your listener should await for 2 events:
public void lifecycleEvent(final LifecycleEvent event) {
if (Lifecycle.START_EVENT.equals(event.getType())) {
// Create your datasource instance...
Context initContext = new InitialContext();
initContext.createSubcontext("jdbc");
initContext.createSubcontext("jdbc/oracle");
initContext.rebind("jdbc/oracle/myDataSource", myDataSource);
} else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
// unbind...
}
}
Then you'll have to propagate resource accesses by "linking" them from "global" JNDI directory to "local" JNDI directory using ResourceLink element in your META-INF/context.xml:
<ResourceLink name="jdbc/oracle/myDataSource" global="jdbc/oracle/myDataSource"
type="javax.sql.DataSource" />
That worked for me so far.
(*) Some notes:
There's an advantage on using lifecycle listeners. Since the order of context creation is not guaranteed. The advantage is that all of your contexts will see this object created.
If you need to create and configure datasource creation more dynamically that on lifecycle listener creation, note that you can bind a custom class implementing the Factory pattern.
To avoid classloading incompatibility problems, consider putting your listener, datasource, etc. classes in a jar file in the Tomcat lib directory, so they're included y the common classloader.
Regards.
What you are trying to do is not going to work. J2EE applications are not allowed to modify the JNDI environment provided by the application server (J2EE spec, section 5.2.2) and the Tomcat JNDI documentation also states, that each web applications gets each own read-only JNDI environment. I'm not sure why binding/rebinding your datasource is not failing immediately and why it's working within the same web application, but even such application-internal usage of the JNDI environment is undocumented behaviour, which I would not rely on.
A couple people have already commented on this, but I think the answer to your question is: Tomcat has a file called server.xml that you need to use. A good reference I have used before is below:
http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html
Resources defined here will be visible to all apps deployed (if set up correctly). If you set up a JNDI resource in your apps context or web xml file, it should only be available to your app.
JNDI context are private to each webapp. Context created in one app can't be accessed by others.
Try to create an entry in GlobalNamingResources and links in both webapps using <ResourceLink> to see if it works.
I used this setup before to read from both apps but never tried to write from one. So not sure if it will work.
Actually, it is possible to access others JNDI resources, if the servlet implements org.apache.catalina.ContainerServlet. This interface has a org.apache.catalina.Wrapper attribute, that is "populated" by the container itself.
through that, I created a simple application to monitor resources.
BUT, I would like to do that in a listener, so my resource monitor could start when the container starts. Anyone knows a way?

Categories

Resources