jdeprscan throws cannot find class error - java

I'm trying jdeprscan on my CentOS system. Here are the commands that I'm executing:
export classpath=<PATH/*>:<ANOTHER/PATH/*>:<SOME/OTHER/PATH/*>
jdeprscan --for-removal --verbose --class-path $classpath --release 9 <ANOTHER/PATH>/MyProject.jar
In spite of providing the classpath, I'm getting multiple errors of this sort
Processing class <some/class/in/MyProject.jar>...
error: cannot find class <some/class/in/a/different/jar>
error: cannot resolve Methodref <some/class/in/a/different/jar>.<method>:()Ljava/lang/String;
I've verified that the class mentioned in the error message are pretty much among one of the jars provided in the classpath.
Strangely, it's not the case that I'm getting this error for every other classes referred in the jar that I'm scanning.
Few points for your consideration:
The are 50+ jars in the paths provided in the classpath
The jar that I'm trying to scan is residing in one of the paths mentioned in the classpath
I've tried jdeprscan available in JDK 9 & JDK 10 and getting the same errors
I've tried replacing the * in classpath with *.jar, doesn't help!
Is there anything wrong in the syntax that I'm following or is this a known bug in jdeprscan?

Some preliminary notes on jdeprscsan from Oracle JDK 11 under Windows (I know the question related to JDK 9 on CentOS, but maybe the following applies as well...):
use wildcard "path/to/lib/*.jar" (it will not work without the ".jar" extension)
having a wildcard with more than one path is not supported (i.e. --class-path dir1/;dir2/*.jar throws a parse exception on the wildcard)
JARs in the directory specified by --class-path are added to the classpath and analyzed in alphabetical order, which may cause some error : cannot find class X messages because a JAR a.jar may depend on a JAR b.jar which is not yet loaded.
Based on the above, I found the 3 solutions below. Note that I did the same experiment with jdeprscan from the Oracle JDK 12.0.2 without any improvement.
Solution 1: all classes on the classpath
unzip all JARs in a specific directory (ignore overwritted files such as META-INF/maven/pom.xml), e.g. mylib (note that at this stage, the mylibs directory contains only classes organized by directory packages, and no JARs).
run the following command:
jdeprscan --for-removal --class-path /path/to/mylib /path/to/my-application.jar
Advantage: fast (very manual operations)
Drawback: only analyses the JAR file that has been specified on the command line (my-application.jar)
Solution 2: all JARs + a fat JAR on the classpath
copy all JARs libs into a single mylib directory
extract all files from JARs of mylib (ignore duplicate files) and repack them into a big JAR mylib/00lib.jar (a simple ZIP file renamed to .jar makes the trick)
copy the mylib/00lib.jar to mylib/01lib.jar to ensure that it will be analysed
run the following command:
jdeprscan --for-removal --verbose --class-path path/to/mylib/*.jar path/to/my-application.jar
Advantage: fast (only a few manual operations) + analyses the JARs from mylib
Drawback: some of the jdeprscan messages will be related to the fat JAR 00lib.jar so you will not be able to determine immediately which library uses classes that are deprecated or removed from Java 9 or 11, but you can do it indirectly by looking at the class name, e.g. (a class from the com.atomikos library requires the missing class javax.jms.JMSException):
Processing class com/atomikos/datasource/xa/jms/JmsTransactionalResource...
error: cannot find class javax/jms/JMSException
Solution 3: reorder the JAR files on the classpath
copy all JARs libs into a single mylib directory
run the following command:
jdeprscan --for-removal --class-path path/to/mylib/*.jar path/to/my-application.jar
inspect the log to see error: cannot find class messages that are not supposed to be raised because the JAR exists in the lib directory. For each such library, copy the library with a name that is before the library name which reference it alphabetically speaking. For example, I have a JAR in the lib dir alpha.jar which depends on commons-lang-3.0.jar (which is not yet loaded in the classpath), so I copy commons-lang-3.0.jar to a0commons-lang-3.0.jar so that it will be loaded before alpha.jar. It is important to copy the JAR and not to rename it otherwise it may not be analyzed by jdeprscan (does not occur on every JAR). Once done, go back to step 2 until no error messages produced by library dependencies occurs.
Advantage: gives a clear view of which JAR uses deprecated/removed classes.
Drawback: takes a lot time (manual copy of each individual JARs which cause a classloading issue).
Conclusion
I use jdeprscan with Solution 2. This should be considered as a workaround (is it an incomplete tool documentation or a bug, I don't know...).

If you happen to have a folder in PATH which has classes and JARs, it won't work. You'd have to specify the JARs individually (or get rid of the classes).

Related

Facing NoClassDefFoundError despite jar being in classpath (dynamic compilation)

In a tomcat server, at run time, I am reading java class files and compiling them dynamically using the InMemoryJavaCompiler library which internally uses JavaCompiler .
Code Sample -
InMemoryJavaCompiler.newInstance()
.useOptions("-parameters",
"-classpath", sb.toString(),
"-Xlint:unchecked")
.compile(sourceCodeClassName,
sourceCode.toString());
Here sb (Stringbuilder) indicates the jars read from WEB-INF/lib directory separated by a colon.
The code works in the following scenarios :
If the above compilation code is kept in a standalone class file & the relevant libraries are set in the classpath. The compilation was successful.
I printed the jar files i.e. I printed sb value separately & took a copy of it. I manually ran javac -classpath <sourceCode.java> in the terminal. The compilation was successful.
The code is not working in the following scenarios :
a. When the above code snippet is added as a stand alone program in eclipse, I faced NoClassDefFound error for the interface which the sourceCode was implementing.
b. In Tomcat, the relevant jar is present in contextName/WEB-INF/lib/ and in addition to that the jar is added in -useOptions classpath. Even then, I am facing NoClassDefFound error for the the interface the sourceCode was implementing.
Note : There are no duplicate copies of the jar or multiple versions of the jar present in the lib directory or the classpath value. The interface is ContractInterface present in hyperledger-fabric-shim
The issue was that System classloader was being used. Once I set the classloader using the -useParentClassLoader option with the current thread's classloader, the compilation was successful.

Running an uber jar from sbt assembly results in error: Could not find or load main class

I have a spark job packaged as an uber-jar using the sbt assembly plugin.
The build.sbt specifies a runnable main to be the target of the resulting uber-jar
mainClass in assembly := Some("com.foo.Bar")
After the assembly is correctly created, running the intended command:
java -jar assembly.jar
results in
Error: Could not find or load main class com.foo.Bar
Using the an alternative method, like java -cp assembly.jar com.foo.Bar gives the same error message.
Then, I extracted the contents of the uber-jar in a new directory. I can see my com/foo/ directory and the Bar.class file.
From the root of the extracted directory I tried:
java -cp . com.foo.Bar
and I get a correct result.
Further trying to find the reason of the error, I tried:
java -verbose -jar assembly.jar
I can see the java core classes being loaded, but I don't see any of my packaged classes being loaded.
What can possibly be wrong here?
After an extensive investigation (read: pulling hairs out), it turns out that this behavior is the result of a rogue INDEX.LIST from one of the flattened jar files landing in the META-INF directory of the resulting uber-jar.
Following the JAR file spec, the INDEX.LIST, if present, dictates what packages from the Jar file are to be loaded.
To avoid this, we updated the mergeStrategy with a rule to avoid any pollution of the resulting META-INF directory:
case PathList("META-INF", xs # _*) => MergeStrategy.discard
This fixed the issue and returned my sanity.
Update:
After some extra searching, it turns out that the default merge strategy takes proper care of INDEX.LIST. This answer applies when the customized merge strategy contains cases that handle the META-INF pathSpec

About this servlet + tomcat example, can not compile servlet using javac

I am currently following the book Head First Servlets and JSP, and I got to the point on page 81 where the author asks to compile the servlet using javac.
I am having problems to execute that line of code. I think that my JAVA_HOME and etc must be set up correctly since I created a sample HelloWorld.java and
compiled it useing javac and it created the correspondent .class file.
I am failing to see the logic of this command, you specify a class path to the servlet-api.jar file and then you give it another path so it can execute the .java file?
I would like to get out of this hole I am in right now. These are the paths to my files:
C:\Users\Carlos L\Tomcat\apache-tomcat-8.0.28\bin\servlet-api.jar
and this is where my BeerSelect.java file is:
C:\Users\Carlos L\Tomcat\My Tomcat Projects\beer-v1\src\com\example\web\BeerSelect.java
so far i have been imputing:
javac -classpath C:\Users\Carlos L\Tomcat\apache-tomcat-8.0.28\bin\servlet-api.jar; classes:. d-classes src\com\example\web\BeerSelect.java
and I am getting this error:
javac: invalid flag: d-classes
usage:javac
This should not be this hard.
First, yes it is valid to compile against one jar and later run against a different one(s). In particular a jar named something-api.jar usually contains only the classes that constitute the Application Program Interface aka API, and is sufficient to compile programs that want to call something, but actually executing those calls requires additional internal classes that are packaged in jars using various names such as something-impl, something-body, plain something, or multiple jars such as something-basic something-core something-addon something-option etc.
Second, your book is apparently using the common (but not required or universal) scheme where the java source code files and compiled class files are in separate, parallel subtrees here named src and classes. Your particular sourcefile is apparently src\com\example\web\BeerSelect.java. The syntax to run the java compiler for this case is:
javac -classpath (classpath) -d classes src\com\example\web\BeerSelect.java
# or abbreviate -classpath as -cp, or use envvar CLASSPATH instead
That's hyphen then d, then a space, then the directory name here classes, then another space and the sourcepath (or multiple sourcepaths).
You shouldn't need to specify classes in the classpath initially, only the servlet-api.jar file. If you later compile some but not all sourcefiles of the src subtree, and previously-compiled classfiles for other sourcefiles are already in the classes subtree, you do need both the servlet-api.jar and the classes directory. On Windows you separate classpath entries by semicolon ; and on Unix you use colon : but you should never mix them. So your case would include:
javac -classpath \path\to\servlet-api.jar;classes (rest as above)
except that your path name apparently includes a space C:\users\Carlos L\... so you must put the value in quotes:
javac -classpath "C:\users\Carlos L\tomcat-8.0.28\bin\servlet-api.jar;classes" (rest as above)`
As an alternative to typing this numerous times, you can put the value in envvar CLASSPATH
set CLASSPATH="C:\users\Carlos L\tomcat-8.0.28\bin\servlet-api.jar;classes"
and then simply do
javac -d classes src\com\example\web\BeerSelect.java
and similarly for any other classes in the the project as you come to them.

Package does not exist error when package was added to classpath

Note, linked solutions (ex. Fatal Error: Unable to find package java.lang in classpath or bootclasspath) do not work.
I get this error, but the package is imported (commons... .jar)
org.apache.commons.lang3.tuple //does not exist import
org.apache.commons.lang3.tuple.MutableTriple
Source code
import org.apache.commons.lang3.tuple.MutableTriple;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
Build code:
export
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home
/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/bin/javac
-target 1.8 -source 1.8 -classpath \ "../lib/commons-lang3-3.4.jar;../lib/httpclient-4.5.jar;../lib/httpcore-4.4.1.jar;../lib/org.json-20120521.jar;../lib/pdfbox-app-2.0.0-20150606.170503-1383.jar;../src/:../lib/commons-lang3-3.4-javadoc.jar;../lib/pdfbox-app-2.0.0-20150606.170503-1383-sources.jar" \ -d output \ ../src/com/tymaf/pdf/*.java
How to fix this problem?
Double check your classpath. Looks like you mixed delimiters ; and :.
Also instead of including jar with compiled classes (library itself). You've included java-docs and sources that are useless in classpath.
../src/:
../lib/commons-lang3-3.4-javadoc.jar;
../lib/pdfbox-app-2.0.0-20150606.170503-1383-sources.jar
Here is my suggestion
How to compile and use .jar extension
.jar extension can be imported different ways depending on your environment and IDE.
Here how it work as native mode from console.
Download the .jar.zip library from
http://www.java2s.com/Code/Jar/c/Downloadcommonslang333jar.htm
Create a folder in your working (project) directory call it libs
Unzip the downloaded file and copy commons-lang3-3.3.jar to your working directory libs
I have also created a class just for testing call it TheNewWork.java and added the 3 imports.
Now from your working directory c:\projects for Compile:
javac -classpath "/Projects/libs/commons-lang3-3.3.jar;" TheNewWork.java
And for running it:
java -classpath "/Projects/libs/commons-lang3-3.3.jar;" TheNewWork
If you have more than one .jar just add ; for Windows and : for Linux. Btw I use windows 10 cmder console and java jdk1.8.0_66. In other OS console you might need to put .:Projects...etc in stead of /Projects...etc. but the idea is the same.
UPDATE
In windows it is possible to set classpath like
set CLASSPATH=%CLASSPATH%;C:\Projects\libs\commons-lang3-3.3.jar
OR in Linux
export CLASSPATH=".:/Projects/libs/commons-lang3-3.3.jar"
Then you can run javac TheNewWork.java but it is personal taste to do it this or the other way. Some things similar is also possible to do in other OS.
Last thing, if you lazy and do neither want to write a full command line nor create a classpath, you could create a batch file with the full command line and run it that way in stead ;)
Some references:
http://docs.oracle.com/javase/8/docs/technotes/tools/windows/classpath.html
https://www3.ntu.edu.sg/home/ehchua/programming/howto/JDK_Howto.html
https://www.chilkatsoft.com/java-classpath-Windows.asp
I hope this solves your problem
Before the solution
After the solution
NOTE
In addition thanks to #MarkPeters notified me on my previous answer: Adding application dependencies directly to the JRE libs is not a good approach, as it makes the JRE suitable for running only one Java application, rather than being a generic runtime. Plus it would complicate whatever deployment the OP wants to do. lib/ext is made for extending the core Java APIs, as described here: docs.oracle.com/javase/tutorial/ext/basics/install.html. Not for normal application dependencies.

How to import commons-io.jar jsp/java

I am getting an exception:
Cannot find symbol: FileUploadException;
I have a piece of code which uses
FileUploadException
The library that needs importing is:
org.apache.commons.fileupload.FileUploadException
The path to my project is :
D:\Projects\website
In the project folder I have each in its folder:
Tomcat, Derby, Website
I have copied:
commons-fileupload.jar and commons-io.jar
into both:
Tomcat/lib and Website/Web-INF/lib
---------------I tried this--------------
just importing the library on its own
import org.apache.commons.fileupload.FileUploadException;
adding the jars to the class path upon build:
javac -cp .;D:Projects\website\Tomcat\lib\commons-fileupload.jar;D:\Projects\website\Tomcat\lib\commons-io.jar com/otrocol/app/*.java
adding them to the Environment variables CLASSPATH
D:Projects\website\Tomcat\lib\commons-fileupload.jar;D:\Projects\website\Tomcat\lib\commons-io.jar
I also tried adding the jars where my .java files are as #Scot Ship suggested
----mentions---
I am not using any IDE
The code contains more unrecognized symbols, but I'm trying to solve one at a time
First time using apache, tomcat, jsp.. please be gentle
Vlad, the web container will automatically look for JARs inside
/WEB-INF/lib
even without any developer intervention. Take note that it's all caps WEB-INF. As long as your JAR is there, it will be in your web application's classpath.
Try to display this in one of your servlets or JSP:
System.getProperty("java.class.path")
and you'll get a better view of what classes and JARs were actually loaded.
Update: After reviewing your question, it appears you're facing issues in compiling the files to begin with and you're doing it outside an IDE.
Take note that when you use -cp in javac like this:
javac -cp .;D:Projects\website\Tomcat\lib\commons-fileupload.jar;D:\Projects\website\Tomcat\lib\commons-io.jar com/otrocol/app/*.java
Whatever value you have set in the CLASSPATH environment variable becomes ignored.
Be absolutely sure that the class FileUploadException is indeed inside one of the JARs you're trying to import: you can view the JAR directly using an unarchiving tool.
Also, change the com/otrocol/app/*.java to com\otrocol\app*.java - you should be using your system delimiter (not that this may affect your problem).
Create a simple HelloWorld in the same location as the file you're compiling, add the SystemOut mentioned above, and compile it the same way you're doing for the concerned file.
Read this http://commons.apache.org/proper/commons-fileupload/faq.html#class-not-found. Probably you have the fileupload jar but you also need commons-io.jar in your classpath as well.

Categories

Resources