I was wondering if it's possible to include a jar in the classpath when compiling instead of executing. At the moment I am just checking to see if my PostgreSQL driver can be found
everything is in the same location for testing purposes so
program/
DriverCheck.java
DriverCheck.class
postgresql-longassname.jar
DriverCheck.java contains
public class DriverCheck {
public static void main(String[] args) {
try {
Class.forName("org.postgresql.Driver");
System.out.println(Driver Found);
} catch(Exception log) {
System.out.println(Driver Not Found);
}
}
}
If I compile and execute this as normal
# javac DriverCheck.java
# java -cp ".:./postgresql-longassname.jar" DriverCheck
It works as I get the output
Driver Found
However if I try to compile and execute in this manner
# javac -cp ".:./postgresql-longassname.jar" DriverCheck.java
# java DriverCheck
It does not work as I get the output
Driver Not Found
Why is this and is there a way for me to use the second method for including jars?
Why is this and is there a way for me to use the second method for including jars?
It's because specifying the classpath for compilation just tells the compiler where to find types. The compiler doesn't copy those types into its output - so if you want the same resources available when you execute, you need to specify the classpath at execution time.
In this case the compiler doesn't need the extra jar file at all - nothing in your source code refers to it... but you do need it at execution time... which is why your original approach works and your second approach doesn't.
Related
I'm putting together some (Python) scripts to help me automate some of my grading of hundreds of simple student Java repos. Not all of them have the same directory structure or naming of files. I've traversed them all and compiled them and if I make assumptions I can run them and test them, etc. But I'd like to know if there's a way I could find the "main" .class that has the main() method in it, so that I don't have to make assumptions about their file naming (which wouldn't work all the time anyway).
I'm aware of reflection, so yes, I know I could write another simple helper Java program to assist me in identifying it myself. But I was wondering if anything already exists (java command line option, tool from the jdk, etc.) to test a .class file to see if it is has the main() method in it.
I was wondering if anything already exists (java command line option, tool from the JDK, etc.) to test a .class file to see if it is has the main() method in it.
There is no tool or option in Java SE that does that directly.
I know I could write another simple helper Java program to assist me ...
It would be simpler to write a shell script that iterates a file tree, finds .class files, calls javap on them, and greps for a method with the appropriate main method signature.
Or you could do something similar on the source code tree.
(In retrospect, you should have set the assignment requirements so that the students had to use a specified class and package name for the class containing their main method. But it is too late for that now ...)
In the C++ days, distributing the headers files to use a shared object file was a big deal. People would get one or the other without both, and there was always the chance you'd get mis-matched versions.
Java fixed that with javap which prints the methods (and other major interfaces) of a compiled .class file.
To test if a class file has a main, run
javap SomeFile.class
which will list all public interfaces. Within that list, see if it has the "main entry point"
public static void main(java.lang.String[])
Now to handle this in mass, simply create a Python script that:
Locates all the relevant classes.
Runs javap on the class.
Reads the output for a method that matches (at the beginning, as there can be a variable number of Exceptions at the end "public static void main(java.lang.String[])
And you'll find all entry points.
Keep in mind that sometimes a single library or JAR file has many entry points, some of which are not intended as the primary entry point.
Well simply calling java -cp . <file> will either completely blow out if the class doesn't have a main method or will run the relevant code. Now, if the code fails to run right and errors out you may see it as the same effect as not having a main method.
public class HasMain {
public static void main(String[] args) {
System.out.println("Hit main");
}
}
public class HasDoIt {
public static void doIt(String[] args) {
System.out.println("Hit doIt");
}
}
public class WillBlowUp {
public static void main(String[] args) {
System.out.println("Hit blowUp");
throw new IllegalStateException("oops");
}
}
Using PowerShell:
PS D:\Development\sandbox> javac HasMain.java
PS D:\Development\sandbox> javac HasDoIt.java
PS D:\Development\sandbox> javac WillBlowUp.java
PS D:\Development\sandbox> java -cp . HasMain
Hit main
PS D:\Development\sandbox> $?
True
PS D:\Development\sandbox> java -cp . HasDoIt
Error: Main method not found in class HasDoIt, please define the main method as:
public static void main(String[] args)
or a JavaFX application class must extend javafx.application.Application
PS D:\Development\sandbox> $?
False
PS D:\Development\sandbox> java -cp . WillBlowUp
Hit blowUp
Exception in thread "main" java.lang.IllegalStateException: oops
at WillBlowUp.main(WillBlowUp.java:4)
PS D:\Development\sandbox> $?
False
So simply checking return values could be a quick way to test if the class has what you want, albeit any exit(1) type return will throw a false-false
Assume we have a set of projects "p1.jar", "p2.jar", etc, all them using library "foo-1.0.0.jar".
Assume a new version "foo-1.0.1.jar" is created. As an additional check, all projects p1, p2, ... are recompiled using new foo-1.0.1.jar. No compilation errors appears.
Now, in the production environment, foo-1.0.0.jar is replaced by foo-1.0.1.jar. No update of p1.jar, p2.jar, ... is done. Unfortunately, everything crashes.
It is possible to do some other checks to be 100% sure new library can replace old one?
Obviously, we are not talking about check of code errors introduced in new library or changes in the functionality (by example, if in new version function "sum" does a subtraction instead of an addition). But at least check changes in arguments, results, available classes, etc.
One example is, foo-1.0.0.jar contains method:
int m1() {
// some code
return 0; // always return 0, and ignored by the users
}
that in foo-1.0.1 it is changed to:
void m1() {
// some code
}
Unfortunately, the result of the method is part of the name mangling in Java (not in C) and p1.jar will fail with "NoSuchMethod" exception when looking for "int m1()".
You might use japicmp to compare the new and old library archive for API changes.
But there is still the risk that a dependency of the library you check would not be detected.
Find below an example to demonstrate changes which might be undetected.
ApiClass1.java
class ApiClass {
public static final int FOO = 23;
public static void version() {
System.out.println("version 1 FOO: " + FOO);
}
}
ApiClass2.java
class ApiClass {
public static final int FOO = 42;
public static void version() {
System.out.println("version 2 FOO: " + FOO);
}
}
ApiDemo.java
class ApiDemo {
public static void main(String...args) {
ApiClass.version();
System.out.println("ApiClass.FOO: " + ApiClass.FOO);
}
}
compile and build library for API version 1
javac ApiClass1.java
jar cf api_v1.jar ApiClass.class
compile and build library for API version 2
javac ApiClass2.java
jar cf api_v2.jar ApiClass.class
compile, build and run your code with API v1
javac -cp api_v1.jar ApiDemo.java
java -cp api_v1.jar:. ApiDemo
output
version 1 FOO: 23
ApiClass.FOO: 23
run your code with API v2, without recompilation
java -cp api_v2.jar:. ApiDemo
version 2 FOO: 42
ApiClass.FOO: 23
The only reliable way is to compile your code against the new version, run all your unit-/integration test and check if they pass.
Unpack both jars, old and new, with jar x
Get class contents with javap
Compare contents from two jars
For example, if you have foo-1.0.0.jar and foo-1.0.1.jar in current directory, you may run this bash script:
# unpack old jar
mkdir foo-1.0.0
cd foo-1.0.0
jar xf ../foo-1.0.0.jar
# unpack new jar
mkdir ../foo-1.0.1
cd ../foo-1.0.1
jar xf ../foo-1.0.1.jar
# get unpacked classes contents
cd ..
for file in `find foo-1.0.0 foo-1.0.1 -name '*.class'`; do javap $file > $file.txt; done
# remove redundant .class files
find foo-1.0.0 foo-1.0.1 -name '*.class' | xargs rm
# compare jar contents
diff -r foo-1.0.0 foo-1.0.1
Sure, that's not fully automated method, you still should to check diff output manually. Still, that's better, than nothing.
Eclipse happily generates bytecode for Java classes that have compilation errors. The offending methods just throw exceptions when invoked at runtime.
Does anyone know how I can stop Eclipse from generating bytecode when compilation errors are present? I'd rather not have runnable code at all in the presence of errors.
As an example, consider the following code:
public class Err {
public static void main(String[] args) {
methodWithoutCompilationError();
// methodWithCompilationError();
}
private static void methodWithoutCompilationError() {
System.out.println("No error here, folks.");
}
private static void methodWithCompilationError() {
System.out.println("This method doesn't compile." + );
}
}
It runs fine, even with the compilation error. Only when I uncomment the second line of main do I see there was a problem compiling.
The reason Eclipse does this is that the compile errors might be resolved by editing another java source file. For example, if a reference to a method name in another class is misspelled, the fix could be that you correct the spelling of the reference or the fix could be that you change the name of the method. Eclipse doesn't know which you'll choose to do so it compiles the referencing class anyway in case you decide to change to other file. I'm not even sure it could otherwise know when to compile all of the classes again.
As a result, Eclipse will always compile the edited java source to the extent possible whenever you change the source file.
I'm guessing at some point you enabled the 'Continue launch if project contains errors' option. Try setting it to 'Prompt':
Try checking the "Treat above errors like fatal compile errors (make compiled code not executable)" checkbox.
When developing an application, we normally use different functions from different classes. Now a function can be written in a way, where only the different functions are called. So
I just want to know suppose I am having a function where I have called some other functions and also having unique code in that function. So how I will know for a bigger project, that from which line for a particular function, exception is occurring in Java.
At most, right now I can detect only from which function exception is occurring. But I need specific portion(specific line) of that function where the exception is occurring.
Any helpful answer will be appreciated.......Thanks in advance......!!!!!
catch(Exception e){
e.printStackTrace();
}
this would do the trick.
The rest of answers written here are perfectly relevant and true, but there is a little gap. You must remember to include in your binary files the relevant debug information, or if not, the JVM will not know which line number caused the exception. Remember to set the debug flag when compiling your code, have a look at this from the javac help:
C:\>\j2sdk1.5.0\bin\javac
Usage: javac <options> <source files>
where possible options include:
-g Generate all debugging info
-g:none Generate no debugging info
-g:{lines,vars,source} Generate only some debugging info
You would need to compile with javac -g xxx.java, or at least javac -g:lines xxx.java in order to have that information on execution time. If you compile your code by any other means (Eclipse or any other IDE compiler, ant, maven, gradle...) you should have a look at your automation tool compiler doc in order to know how to instruct its compiler to generate debug information along with your binaries.
You look at the stacktrace. For example:
java.io.FileNotFoundException: test.txt
at java.io.FileInputStream.<init>(FileInputStream.java)
at FooBar.test(FooBar.java:19)
at FooBar.main(FooBar.java:7)
To get the execution order you have to read it from bottom to top.
Here
the program starts at the method main() of class FooBar
main() calls the method FooBar.test() in line 7 of FooBar.java
test() calls the FileInputStream constructor in line 19 of FooBar.java
the FileInputStream constructor throws an FileNotFoundException
You can print the stacktrace of an exception using:
try {
..
} catch (Exception e) {
e.printStackTrace();
}
try
{
//your code that may have error
}
catch(Exception ex){
ex.printStackTrace();
}
suppose there's error occur then it will show something like this :
Exception in thread "main" java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
it means error at line number 16 in Book.java.
look this article
java -classpath ../classes;../jar;. parserTester
How can i get the functionality in the above command programmatically? Like, is it possible to run as:
java parserTester
and get the same result? I tried using URLClassLoader but it modifies the classpath and does not add to it.
Thanx!
Thanks for the response Milhous. But that is what i am trying to do.. How is it possible to get the jar into the classpath first? I tried using a custom classloader too :(
That works.. But sorry that i need to run it only as:
java parserTester
I would like to know if such a thing is possible???
It needs to be so bcoz i have parserTester.java and .class in a separate folder. I need to retain the file structure. The parserTester makes use of a jar in a separate jar folder.
You can use a java.net.URLClassLoader to load classes with any program defined list of URL's you wish:
public class URLClassLoader
extends SecureClassLoader
This class loader is used to load
classes and resources from a search
path of URLs referring to both JAR
files and directories. Any URL that
ends with a '/' is assumed to refer to
a directory. Otherwise, the URL is
assumed to refer to a JAR file which
will be opened as needed.
The AccessControlContext of the thread
that created the instance of
URLClassLoader will be used when
subsequently loading classes and
resources.
The classes that are loaded are by
default granted permission only to
access the URLs specified when the
URLClassLoader was created.
Since:
1.2
And a little fancy footwork can extend it to support using wildcarded pathnames to pick up entire directories of JARs (this code has some references to utility methods, but their implementation should be obvious in the context):
/**
* Add classPath to this loader's classpath.
* <p>
* The classpath may contain elements that include a generic file base name. A generic basename
* is a filename without the extension that may begin and/or end with an asterisk. Use of the
* asterisk denotes a partial match. Any files with an extension of ".jar" whose base name match
* the specified basename will be added to this class loaders classpath. The case of the filename is ignored.
* For example "/somedir/*abc" means all files in somedir that end with "abc.jar", "/somedir/abc*"
* means all files that start with "abc" and end with ".jar", and "/somedir/*abc*" means all files
* that contain "abc" and end with ".jar".
*
*/
public void addClassPath(String cp) {
String seps=File.pathSeparator; // separators
if(!File.pathSeparator.equals(";")) { seps+=";"; } // want to accept both system separator and ';'
for(StringTokenizer st=new StringTokenizer(cp,seps,false); st.hasMoreTokens(); ) {
String pe=st.nextToken();
File fe;
String bn=null;
if(pe.length()==0) { continue; }
fe=new File(pe);
if(fe.getName().indexOf('*')!=-1) {
bn=fe.getName();
fe=fe.getParentFile();
}
if(!fe.isAbsolute() && pe.charAt(0)!='/' && pe.charAt(0)!='\\') { fe=new File(rootPath,fe.getPath()); }
try { fe=fe.getCanonicalFile(); }
catch(IOException thr) {
log.diagln("Skipping non-existent classpath element '"+fe+"' ("+thr+").");
continue;
}
if(!GenUtil.isBlank(bn)) {
fe=new File(fe,bn);
}
if(classPathElements.contains(fe.getPath())) {
log.diagln("Skipping duplicate classpath element '"+fe+"'.");
continue;
}
else {
classPathElements.add(fe.getPath());
}
if(!GenUtil.isBlank(bn)) {
addJars(fe.getParentFile(),bn);
}
else if(!fe.exists()) { // s/never be due getCanonicalFile() above
log.diagln("Could not find classpath element '"+fe+"'");
}
else if(fe.isDirectory()) {
addURL(createUrl(fe));
}
else if(fe.getName().toLowerCase().endsWith(".zip") || fe.getName().toLowerCase().endsWith(".jar")) {
addURL(createUrl(fe));
}
else {
log.diagln("ClassPath element '"+fe+"' is not an existing directory and is not a file ending with '.zip' or '.jar'");
}
}
log.diagln("Class loader is using classpath: \""+classPath+"\".");
}
/**
* Adds a set of JAR files using a generic base name to this loader's classpath. See #link:addClassPath(String) for
* details of the generic base name.
*/
public void addJars(File dir, String nam) {
String[] jars; // matching jar files
if(nam.endsWith(".jar")) { nam=nam.substring(0,(nam.length()-4)); }
if(!dir.exists()) {
log.diagln("Could not find directory for Class Path element '"+dir+File.separator+nam+".jar'");
return;
}
if(!dir.canRead()) {
log.error("Could not read directory for Class Path element '"+dir+File.separator+nam+".jar'");
return;
}
FileSelector fs=new FileSelector(true).add("BaseName","EG",nam,true).add("Name","EW",".jar",true);
if((jars=dir.list(fs))==null) {
log.error("Error accessing directory for Class Path element '"+dir+File.separator+nam+".jar'");
}
else if(jars.length==0) {
log.diagln("No JAR files match specification '"+new File(dir,nam)+".jar'");
}
else {
log.diagln("Adding files matching specification '"+dir+File.separator+nam+".jar'");
Arrays.sort(jars,String.CASE_INSENSITIVE_ORDER);
for(int xa=0; xa<jars.length; xa++) { addURL(createUrl(new File(dir,jars[xa]))); }
}
}
private URL createUrl(File fe) {
try {
URL url=fe.toURI().toURL();
log.diagln("Added URL: '"+url.toString()+"'");
if(classPath.length()>0) { classPath+=File.pathSeparator; }
this.classPath+=fe.getPath();
return url;
}
catch(MalformedURLException thr) {
log.diagln("Classpath element '"+fe+"' could not be used to create a valid file system URL");
return null;
}
}
I have to agree with the other two posters, it sounds like you're overcomplicating a test class.
It's not that unusual to have the .java and .class files in separate folders, while depending on jar files in yet a third, without programmatically changing the classpath.
If you're doing it because you don't want to have to type the classpath on the command line everytime, I would suggest a shell script or batch file. Better yet, an IDE.
The question I really have is why are you doing trying to manage the classpath in code?
You could implement your own class loader, but that class/jar has to be in the classpath for it to be executed.
try
java -cp *.jar:. myClass
or
export CLASSPATH=./lib/tool.jar:.
java myClass
or
java -jar file.jar
You can write a batch file or shell script file to export the classpath and run the java program.
In Windows,
set classpath=%classpath%;../classes;../jars/*
java ParserTester
In Unix,
export classpath=%classpath%:../classes:../jars/*
java ParserTester
If you name the file name as parser.bat or parser.sh, you can just run that by calling parser in respective OS.
From java 1.6, you can include all the jars in a directory into the classpath just by saying /*
If you are trying to generate a java file dynamically, compile and add into the classpath, set the directory into which the class file gets generated in the classpath beforehand. It should load the class.
If you are modifying the already generated java class, basically recompiling after modification and if you want to load the new class, you need to use your custom class loader to avoid the caching of the class.
I think what you want is an "Execution Wrapper" or a platform specific "Launcher"... typically this component is used to detect your OS and architecture and dependencies and then makes adjustments before launching your application. It is an old school design pattern (talking 80's and earlier) but is still used a lot today. The idea is that you program can be system and environment agnostic and the launcher will make preparations and tell the software everything it needs to know. Many modern open source programs do this with Shell scripts and Batch Files, etc... Apache Tomcat for example. You could just as easily make the wrapper in java an have it launch the software with a command line exec (be sure to add " &" to the end of you exec command in *NIX so your wrapper can exit leaving only your software running... also lets you close the shell window without killing the process)
Did I understand right?! The only reason you have it that you want to launch your class without specifying the classpath and load it at runtime? ...
java parserTester
instead of
java -classpath ../classes;../jar;. parserTester
Probably I didn't get your reason. But if "that's" what you want you can do the following ( although it does not make much sense to me )
Launch the class
From the main method lauch another class an programatically set the classpath there.
End of history.
Something like the following "java -pseudo code "
public static void main( String [] args ) {
String classpath = "classes;../jar";
Runtime.getRuntime().execute("java + classpath + " parserTester ");
}
Please tell me if I get it right. If you want to do something else I would gladly help.
Excellent good post, in my case I did this to work well (note: Windows specific):
set classpath=%classpath%;../lib/*
java -cp %classpath% com.test.MyClass