We'd like to have the version of the java source code, in our case the svn $Id$ string, embedded in the generated class file.
We'd like to be able to determine this information from a static inspection of the class file, preferably by running the strings or what command.
A naive attempt to declare a private final static String variable set to this value inside each class didn't result in a legible string embedded in the class file.
You said ... preferably by running the strings or what command ...
Let's assume you have something like this in your code:
private static final String SVN_ID =
"$Id: SvnIdDemo.java 1081 2008-09-30 19:03:23Z john $";
The following Perl one-liner ...
$ perl -nwe 'print "$1\n" if /.*(\$Id:[^\$]+\$).*/' SvnIdDemo.class
... prints the SVN ID string to STDOUT.
$Id: SvnIdDemo.java 1081 2008-09-30 19:03:23Z john $
You could add a method to the bottom of each class with a predefined name. Say you used:
public String extractChaimGeretzVersionNumber() {
return "$Id$";
}
Then you find the version with a program that loads the class and, via reflection, calls the magic method and retrieves the version.
You would either have to have code that inserted the method to the .java files before building the .class and .jar files OR you could have a build step that checked that the magic method was already in every one of them. Fail the build if not found.
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
I am referring below article about the 70 bytes java class file that prints Hello World.
http://www2.sys-con.com/itsg/virtualcd/java/archives/0707/richards/index.html
I downloaded the source code, compiled GenClass5.java and executed it to generate the 70 bytes class file. The generated class doesn't have a name and it has only the extension .class. My question is how do I execute it with java to print Hello World?
You still run java MyClassName, it's just that the classname is now the empty string. To make the shell pass an empty string, put it in quotes:
java ""
Unfortunately, since it depends on the internals of a class that has changed since the article was written, you just get an error:
Error: Main method not found in class , please define the main method as:
public static void main(String[] args)
but this is true even with a full size class.
I am compiling this Java file and I get one class file.
My task is to change the Static content "Hello" in the Class file and replace with "Hi".
How to read the Class file first, and how to replace the static content?
public class test {
public static void main(String[] args) {
System.out.println("Hello");
}
}
Is there any standard code(A template) for that ?
You need to use some Java bytecode instrumentation libraries like ASM. Good to start to read links are:
A Guide to Java Bytecode Manipulation with ASM
How To Modify Constant Pool Using ASM?
Does your solution need to be in Java? You can use Jawa to accomplish the same in Python with a lot less boilerplate than ASM and its equivalents.
Install it: pip install jawa
Then:
from jawa.constants import String
from jawa.classloader import ClassLoader
# Create a ClassLoader for the current directory
# and load your "test" class.
test = ClassLoader('.')['test']
# Find the first String with the value "Hello"
# in the constant pool.
constant = test.constants.find_one(
type_=String,
f=lambda c: c.string.value == 'Hello'
)
# Change it to your new value
constant.string.value = 'Hi'
# ... and save the new class.
with open('test.class', 'wb') as new_test:
test.save(new_test)
Result:
$ java test
Hi
Full documentation is at http://jawa.tkte.ch. Regardless of what tool you end up using it's absolutely required to read the JVM ClassFile specification or you won't really understand what's going on. You can find it at https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html.
Hello I am trying to pass arguments to my ImageJ PlugIn. However it seems no matter what I pass, argument string will be considered as empty by the program. I couldn't find any documentation on the internet about THAT issue.
My Java plugIn looks like this, and compiles fine.
import ij.plugin.PlugIn;
public class Test implements PlugIn {
public void run(String args) {
IJ.log("Starting plugin Test");
IJ.log("args: ." + args + ".");
}
}
I compile, make a .jar file and put it into the ImageJ plugins folder.
I can call it with the ImageJ userInterface (Plugin>Segmentation>Test) and the macro recorder will put the command used:
run("Test");
Then my code is executed, the log window pops-up as expected:
Starting plugin Test
args: ..
I can manually run the same command in a .ijm file, and get the same result.
However, when I run the following macro command:
run("Test", "my_string");
I get the same results in the log window:
Starting plugin Test
args: .. // <- I would like to get "my_string" passed there
Where it should have displayed (at least what I expect it to do)
Starting plugin Test
args: .my_string.
So my question is: how can I pass parameters to PlugIn and especially how to access them?
Many thanks
EDIT
Hey I found a way to bypass that:
Using the Macro.getOptions() : this method will retrieve the string passed in argument to the plugin.
However, I still can't find a way to pass more than 1 string argument. I tried overloading the PlugIn.run() method but it doesn't work at all.
My quick fix is to put all my arguments in 1 string, and separating them by a space. Then I split this string:
String [] arguments = Macro.getOptions().split(" ");
I don't see a more convenient way to get around that. I can't believe how stupid this situation is.
Please, if you have a better solution, feel free to share! Thanks
You are confusing the run(String arg) method in ij.plugin.Plugin with the ImageJ macro command run("command"\[, "options"\]), which calls IJ.run(String command, String options).
In the documentation for ij.plugin.Plugin#run(String arg), it says:
This method is called when the plugin is loaded. 'arg', which may be blank, is the argument specified for this plugin in IJ_Props.txt.
So, arg is an optional argument that you can use in IJ_Props.txt or in the plugins.config file of your plugin to assign different menu commands to different functions of your plugin (see also the excellent documentation on the Fiji wiki).
To make use of the options parameter when running your plugin from macro code, you should use a GenericDialog to get the options, or (as you apparently learned the hard way) use the helper function Macro.getOptions().
Is there a way to convert JAR lib into JAR standalone?
I need to find a standalone java executable that convert PDF into TIFF and I've found these JARs: http://www.icefaces.org/JForum/posts/list/17504.page
Any ideas?
Easiest might be to create another Jar with a Main() entry point, and then just use the java.exe executable to run it:
e.g.
> java.exe -cp MyJarMain.jar;MyPDFJar.jar com.mydomain.MyMain myPDF.pdf
Where MyMain is a class with a Main static method.
You'll need something with a main entry point to pass in and interpret some command line arguments (myPDF.pdf in my made-up example)
You could do an assembly (are you using maven?) and make sure the Main-Class entry in the manifest.mf points to the main class.
Since there is no main-Method, you have to write one, or write a whole new class to call the class/method TiffConver.convertPDF .
The question is, how you're going to use it. From the command line, you need no executable jar. From the Gui, maybe you want to pass a file to be converted by drag and drop? Then you should take the parameter(s) passed to main as Input-PDF-Names (if they end in .pdf) and pass the names iteratively to TiffConverter, for "a.pdf b.pdf" =>
TiffConver.convertPDF ("a.pdf", "a.tiff");
TiffConver.convertPDF ("b.pdf", "b.tiff");
TiffCoverter will silently overwrite existing tiffs, so check that before or change the code there - this is clearly bad habit, and look out for more such things - I didn't.
/*
* Remove target file if exists
*/
File f = new File(tif);
if (f.exists()) {
f.delete();
}
Maybe you wan't to write a swing-wrapper, which let's you choose Files interactively to be converted. This would be a nice idee, if no filename is given.
If the user passes "a.pdf xy.tiff" you could rename the converted file to xy, as additional feature.
Without a main-class, however, a standalone jar would be magic.
However, building a native executale is almost always a bad idea. You loose portability, you don't profit from security- and performance improvements to the JVM or fixed bugs. For multiple programs you need always an independend bugfix, which you might have to manage yourself, if you don't have a package-management as most linux distros have.
after clearing some questions:
public static void main (String [] args) {
if (args.length == 1 && args[0].endsWith (".pdf")) {
String target = args[0].replaceAll (".pdf$", ".tif");
convertPDF (args[0], target);
}
}
This method you put into TiffConvert. It will allow you to convert a simple pdf-File, and generate a tif-File with the same basename but ending in .tif, silently overwriting an existing one of the same name.
I guess you now need to know how to start it?