multiple versions of javamail jar causing NoSuchProviderException - java

I have a standalone library ( jar file) that is added to the application server classpath to monitor all the running applications in the JVM and send email when some issue happens.
the jar file have all the dependencies bundled in the jar (using the maven-shade-plugin) including the javamail jar file.
When one of the other webapplications in the appserver have javamail as a dependency, some dependency conflict happens with the javamail in my jar file and I get an exception
MessagingException :javax.mail.NoSuchProviderException: No provider for smtp
How can I solve this conflict knowing that I can't modify the running appplications. I only have control over my jar file.
Edit:
I debugged through the javamail API. and I found out that the javax.mail.Session construct a new object of type com.sun.mail.smtp.SMTPTransport using reflection and pass two parameters to the constructor, but rather than using SMTPTransport located in the same jar file it tries to use SMTPTransport from the other mail.jar located in one of the deployed apps.

In tomcat 7, by default web applications will look first for classes on this order:
first on WEB-INF/classes
then inside jars in WEB-INF/lib
and if the class was not found only then in YOUR_SERVER_HOME/lib
Then on the system classloaders, as described here
Class versions from other WARs (on classes / WEB-INF/lib) cannot be visible from inside another WAR.
Can you let us know of further info that could help track down the problem:
what does the standalone library do in more detail, how does it monitor the other applications?
how is the standalone library added to the server classpath? Is it copied on YOUR_SERVER_HOME/lib, or is a folder added to the server classpath via shared.loader or common.loader properties of catalina.properties
does each WAR application provide it's own mail jars on WEB-INF/lib, or is the library published at the server level as a JNDI resource such as mentioned here on section Install the JavaMail libraries
Is any of the applications in the server NOT running in the default classloading mode that I mentioned above, and is using delegate = true (meaning it will look on the server first and only then on the WAR) ? here is how this would be configured, check for a Resource element in the context.xml or server.xml
Some suggestions for possible solutions:
Install JavaMail on tomcat 7 as a JNDI resource following the installation instructions here on section Install the JavaMail libraries. Have all applications use the JNDI resource as in the tomcat docs, and remove java mail from the standalone library using a shade plugin exclusion. This will always work and it's the most recommended solution.
If you are using Java 7, try to use JHades to troubleshoot the classpath of the different applications. You can do static analysis on the WARs for duplicate classes, or view what are the versions used at runtime of a given class, and which other versions are available.
Since there is no control over the deployment settings of the other applications or their contents, use of JNDI, etc., a solution that will work for sure in all environments and with any application is to refactor the tool so that it does not require mail.jar to be installed on the server. The tool can write an email request to for example a file or a database table, and another WAR deployed on the server would poll the table file and send the mail. Or a bash/bat script is called that itself sends the email on a separate java process.
Performance analysis tools such as Dynatrace are based on JVM agents and use a similar mechanism that does not need the introduction of libraries at the server level, the agent collects data and sends it to a collecting process that stores it somewhere for further analisys, treatment such as send alarms via emails, etc.
Hope this helps, in general I don't see any way to deploy libraries to a EE server and be sure that they will never cause problems to any application on different server types and different application classloading settings.
The best is probably adapt the tool so that it relies on a minimum of libraries deployed on the server, breaking it up into separate modules with only the collecting module running on the server or look into alternatives such as Dynatrace.

I'm not familiar with the maven-shade-plugin. Does it package the original jar files in a new jar file? Does it extract the contents of the original jar files and insert the contents into the new jar file? If the latter, it may only be including the class files and not the resource files in META-INF that configure the JavaMail providers.
Of course, the best approach would be to arrange for only one copy of the JavaMail jar file to be included in the server's classpath.

Upgrade the JavaMail version included with Tomcat 7 to JavaMail to 1.5.3 which contains the fix for Bug K6668/E144 -skip unusable Store and Transport classes. You can download the latest snapshot and official releases from the JavaMail reference implementation home page.

Related

Deploying multiple release .jar on Oracle WebLogic Server

We are trying to deploy a web service (a .war file) onto an Oracle WebLogic 12.2.1.0.0 Server.
The .war contains some third party .jars, for example log4j-api-2.12.0.jar, that are multiple release .jars (per JEP 238, having Multi-Release: true in MANIFEST.MF).
As such, it includes some Java 9 compiled classes in the META-INF/versions/9 directory. WebLogic 12.2 uses Java 8 only (confirmed version as Java 1.8.0_92 via WebLogic Admin Console).
As I understand it, Java 8 servers are supposed to not see / ignore any classes under the META-INF/versions directory, but this does not seem to be happening. It appears as though Oracle WLS is just scanning the whole .war.
As a result, when we try to deploy the .war, we get errors like this one:
BEA-160248 Unable to parse class file:... (then a long path name into the META-INF/versions/9 directory)
Is there something special we need to do to make this work? I'll accept anything along the lines of:
Configure WLS somehow to ignore the META-INF/versions/9 classes
Configure Maven to somehow build the .war without the META-INF/versions/9 classes that are present in the third-party .jars.
I'm not interested in answers involving manually deleting anything from the .war every time it is built. Also, I'm not sure I can downgrade to earlier versions of the 3rd party .jars at this point. Log4j2, probably could... but there are others.
Thanks!
JEP 238 is from Java 9 and WebLogic Server does not run with Java 9.
After looking into it further, the
BEA-160248 Unable to parse class file...
messages were a red herring. After solving other, unrelated problems, the application does deploy and work as needed, in spite of these error messages appearing in the log.
I would like to add that according to Doc ID 2629550.1:
This message is harmless and can be ignored.

How are Java applications deployed in the "real world"?

As a novice to the world of Java programming, this question has always boggled my mind. I first believed that all Java files were compacted into applets and then ran, but I soon realized that this isn't always the case. Could someone explain to me how we actually interweave our Java applications into a real product of everyday life?
TL;DR: How do we implement our code for practical usage?
It depends on the application. There are many options depending on how you want your users to use your app. Usually it's packaged as a jar or a specialized jar (war, ear).
In theory, you could zip the raw directory structure with your .class files in it and provide a shell script/instructions that run the java command for the user. I don't recommend this because it's kind of unprofessional and requires you to maintain a shell script for each OS you want to be able to run the program on.
Jar files are used to package libraries but you can also have a manifest file in it that says, "When someone double clicks/executes this, run this class". That class can start up a GUI or be a headless task that responds to the parameters, etc.
You can have applets, like you said. These programs are run in the user's browser.
You can have a war file, which is a way to package a web application. You give this to a web server and it knows how to deploy it so that you can visit the web pages. An example web server/container is tomcat or jetty.
You can have an ear file which can contain other war files inside it. This is used for applications that need other parts of the javaee functionality (ejbs, jms queues, etc.). An example of an application server is jboss or glassfish.
There's also java web start apps. These are apps you can run by visiting a webpage, but they get downloaded to your computer and run on the user's computer (instead of on the server's backend, like in a war/ear).
There's also javafx. I don't know anything about that though. By skimming the FAQ, it appears to be Java's answer to Adobe's Flex. You configure UI components with an xml configuration. I'm not sure what format JavaFX apps use, but it does say, "Deploy on the desktop or in the browser".
As Sotirios Delimanolis mentioned in a comment below, you can build these files with build systems like Ant or Maven. You can also build them "by hand" with the tools that come with the java/javaee sdk. For example, you should have a jar command in your path if you installed the sdk. Here are some details of these build systems:
Maven
High level (you tell it what to build, not how to build it)
Much more than just a build system. It also has dependency management, etc.
Opinionated (it uses convention over configuration, each config file generates 1 artifact, etc.)
Ant
Low level (you tell it how to build things)
Flexible
Config files can do whatever you want, build as many artifacts as you want
Easy to learn
SDK tools
Always up to date. EG: Very rarely, maven/ant may not be able to set a configuration option
Difficult to remember commands
Very low level
By itself, not repeatable (EG: unless you build a script, you will have to type the jar command yourself each time)
Applets never really caught on and are very rarely used nowadays.
Simple applications can be deployed as "executable" JAR files , which are basically ZIP archives with additional metadata that tells the JVM which class contains the main method to run. They can be run on the command line using the -jar option, or in most desktop environments by double-clicking (this requires a JVM to be installed as well).
Desktop applications can be deployed via Java Web Start or installers like IzPack or Install4J, but Java desktop applications are not very common either.
Most Java software nowadays runs only on servers (web servers or app servers). They are typically deployed as WAR or EAR files, which are also ZIP archives containing classes and other resources. These applications then run inside a server component following the Servlet or EJB standards.
If the application is mean to run on a client, it is packaged as an executable JAR, then further packaged as an Application Bundle (Mac), maybe wrapped in an exe (Windows), or paired with an executable script that will launch the JAR and set any required VM arguments.
If it is part of a web application, then it will be packaged as a WAR or EAR and placed into the appropriate location on the web server.
If it is simply a library, then it is usually packaged as a JAR (non-executable) and distributed as such for integration into larger projects.
applets and then ran, but I soon realized that this isn't always the case
Actually, applets are rare nowadays and their use is discouraged.
Create an executable jar, a war which is dropped into a web server or a library that is used by another project that is one of the previous two.

How to override existing java library or specify exact version to use

I'm using ADF Framework withing webcenter suite 11.1.1.5, that happened to have bundled Apache POI libraries version 3.5.
However current project requires at least 3.7, or even latest version of this library.
I have weblogic server, with predeployed webcenter application and all required libraries for it.
If I bundle Apache POI library version 3.8 inside my project, it gets deployed along, however application still use version 3.5.
Webcenter libraries deployed earler and I can't change this behavior. Is there a way to override it at least for my application, beside including sources of Apache POI in my project?
Thanks.
I am not sure how it is going in weblogic but I can recommend you to take a look in class loaders configuration. As far as I remember there was some kind of class loaders order configuration when deploying web application. Generally you should ask it to use the application level class path first and then use the application server library classes. Open the weblogic administration console and try to find this configuration.
You can try look at weblogic-application.xml file (put in META-INF). In this file it is possible to specify which classes should be loaded from application lib folder and not from weblogic.
Example:
<wls:prefer-application-packages>
<wls:package-name>org.apache.log4j.*</wls:package-name>
</wls:prefer-application-packages>
(this will load log4j from application lib, not weblogic log4j library)
AlexR was pointing in the right way and there are actually several solution to this problem.
1. Install POI libraries into $DOMAIN_HOME/lib/ and restart the server. This is system class path and if you ain't configured otherwise it will override other sources.
This solution is easy, but won't work for all of the cases.
2. Second solution is to use filtering class loader feature, that will change standard lookup from system->application->web to filtering->application->web.
More details are available at https://blogs.oracle.com/jamesbayer/entry/sharing_a_class_with_a
In short, you can use adminserver:port:/wls-cat (Classloader Analysis Tool) to identify possible class conflict and inspect current classloader configuration and then use gathered info to add to weblogic-application.xml in your application to enable filtering class loader feature. For example, you can add: "org.apache.poi.*" to the filter causing classloader to check your application libs first. And this configuration will affect only current application, so you won't break other applications, that may require old/different version of the old library.

Updating WLS_HOME/server/lib contents without a restart

Does anyone know how to update the content of a WebLogic Server's WLS_HOME/server/lib/ folder without having to restart it?
A colleague of mine did a deployment which involved an updated .jar file that was used by all the .ears in the user_projects directory, so he decided to place it in WLS_HOME/server/lib. Whilst the web apps he deployed to user_projects deployed ok, we noticed in the server log it was getting repeated NoClassDefFound exceptions for classes we knew were in the updated .jar - and it didn't pick up the new classes till after a restart.
I need ideally to be able to deploy new shared libraries to this /lib folder without having to restart the server as that involves an outage.
Does anyone know how to do this?
This is not possible. You should move to shared Java EE libraries if you need more flexibility. For your current situation, the only thing you can do, provided you have a clustered environment, is to do a rolling restart of the instances to avoid downtime.
From Understanding WebLogic Server Application Classloading :
WebLogic Server includes a lib subdirectory, located in the domain directory, that you can use to add one or more JAR files to the WebLogic Server system classpath when servers start up. The lib subdirectory is intended for JAR files that change infrequently and are required by all or most applications deployed in the server, or by WebLogic Server itself. For example, you might use the lib directory to store third-party utility classes that are required by all deployments in a domain. You can also use it to apply patches to WebLogic Server.
The lib directory is not recommended as a general-purpose method for sharing a JARs between one or two applications deployed in a domain, or for sharing JARs that need to be updated periodically. If you update a JAR in the lib directory, you must reboot all servers in the domain in order for applications to realize the change. If you need to share a JAR file or Java EE modules among several applications, use the Java EE libraries feature described in Creating Shared Java EE Libraries and Optional Packages.

How to add /usr/share/java libs to webapp's classpath?

Summary
Is that possible for webapps deployed on linux + tomcat5.5 to use/see all /usr/share/java/ jars automatically?
Details
I'm packaging my java webapp for Ubuntu (yet the question is related to any linux-based distro) and going to make it depend on tomcat.
I'm going to put context descriptor (an xml file) to /usr/share/tomcat5.5/conf/Catalina/localhost/ to make my app deployed.
Having my web dir here: /usr/share/<appname>/web, how can I enable my app to use java jar libs installed in the system (/usr/share/java)?
I can't just symlink /usr/share/java -> <webdir>/WEB-INF/lib, since I have my custom jars need to be placed in lib dir.
Bad Solution
The solution I've found so far is to symlink each required jar to <webdir>/WEB-INF/lib/.
This is not so good, because I have to symlink a lot of jars and even worse to symlink all jars my direct dependency lib (jar) requires (and so on). In case my direct dependency lib changes its list of required jars I'll have to maintain that symlinks.
According to the Tomcat classloading documentation, you need to put any shared libs that should be available to all Tomcat apps in the $CATALINA_BASE/shared/lib library -- so one way to do what you're looking to do is to move your libraries from /usr/share/java to $CATALINA_BASE/shared/lib.
BUt if I'm not misunderstanding that same documentation, Tomcat also makes the system-wide CLASSPATH variable's contents available to the classloader at launch, so if your directory -- /usr/share/java -- were included in the system-wide CLASSPATH variable, then that should work too. I've never done this, though; Tomcat's method of making the contents of $CATALINA_BASE/shared/lib available Tomcat-wide has always served me perfectly.
entzik's answer lead me to the following solution.
I'm going to use modified "bad solution" (see question).
Modifications are following:
Depend on specific package version for all dependencies (affects "control" file while packaging for deb)example: libcommons-io-java ( = 1.3.1) instead of just libcommons-io-java
Symlink to actual jar files in `/usr/share/java` and not "generalized" onesexample: webdir/WEB-INF/lib/commons-io.jar -> /usr/share/java/commons-io-1.3.1.jarand notwebdir/WEB-INF/lib/commons-io.jar -> /usr/share/java/commons-io.jar
This modifications ensure webapp is not broken if administrator installed new version of a library (commons-io for example).
The downside is this approach clearly inflates system with used-by-only-one-app versions of libraries and may lead to problem some other application/library can't install due to version conflict. I guess both potential problems are minor if we are speaking about libraries.
You have two options, one is to let the classloader provide the libraries to all java programs and the other is to let the classloader provide the libraries to all tomcat contexts.
Add your symlinks to /usr/lib/jvm/java-1.5.0-sun-1.5.0.11/jre/lib (note you may need to specify a different version in this path) to allow all java programs access to these libraries or add them to Tomcat's shared libraries at var/lib/tomcat5.5/shared/libs (again, the version number may be different) for access by all Tomcat contexts.
I should also note that these directory locations were taken from Ubuntu "Feisty".
You should not do that. Java EE applications are the supposed to be self sufficient and not depend on any resources outside the deployment package other than those provided by the container. So you should take the libs you need from that directory and add it to your war or ear package.
This guarantees that your application will behave the same wherever you deploy it and you will not be subject to unexpected changes in the versions of the libs in /usr/share/java....

Categories