I'm trying to do something that should be pretty easy but some how I keep failing...
the idea is to take existing java class from java repository (in our case java sun)
modify it a bit.. recompile the class and use the modified class in our project
the steps (I took String.java from java.lang for example)
modify String String.java by adding:
public int zzz() {
return 123;
}
just under the class constructors.
recompile String.java
javac -d String String.java
jar -cf the compiled files
this is the the result of: jar -vtf String.jar:
0 Wed May 22 10:31:06 IDT 2013 META-INF/
68 Wed May 22 10:31:06 IDT 2013 META-INF/MANIFEST.MF
9763 Wed May 22 10:30:44 IDT 2013 java/lang/String$1.class
1232 Wed May 22 10:26:04 IDT 2013 java/lang/String$CaseInsensitiveComparator.class
17269 Wed May 22 10:26:04 IDT 2013 java/lang/String.class
write short main class:
public class main {
/**
* #param args
*/
public static void main(java.lang.String[] args) {
// TODO Auto-generated method stub
java.lang.String s = new java.lang.String(" xxx ");
s = s.concat("bla bla");
System.out.println(s);
System.out.println(s.zzz());
}
}
(I get the same behavior when trying java.lang.String and just String.)
5.compile my main.java with the modified String class
javac -Xbootclasspath/p:String.jar main.java
6.run main
java -Xbootclasspath/p:String.jar main
that gives us the following output:
myusername#machinename:~/work/sand_box$ java -Xbootclasspath/p:String.jar main
xxx bla bla
Exception in thread "main" java.lang.NoSuchMethodError: java.lang.String.<init>([CZ)V
at java.lang.Integer.toString(Integer.java:333)
at java.lang.Integer.toString(Integer.java:136)
at java.lang.String.valueOf(String.java:2948)
at java.io.PrintStream.print(PrintStream.java:597)
at java.io.PrintStream.println(PrintStream.java:736)
at main.main(main.java:12)
I can't figure out what am I doing wrong
can someone please shed some light on this please?
10x to all the contributors out there.
From my pt of view it would be better to just extend String instead of recompiling a modified version of a JDK class
e.g.: public class String extends java.lang.String
That way you'll create a new String class in your package
Of course, depending on your needs this may not be the best option
But in general I think it's no good idea to modify JDK classes directly - at least if you don't plan to include (and recompile) all the SDK from source by yourself
KR
Florian
Related
I am frustrated to find a solution to the issue as explained in the question title. To help you understand clearly my problem, I am presenting below a little detail.
I have some files having an accent in their names which are storing in a directory. Running ls -lht, it cannot show the files correctly. The accents are decoded incorrectly. But if I press the tab key to apply autocompletion from the terminal, the file name can be shown as expected. See the snippet below.
tc_pst03#login-01: 6] ls -lht
total 704K
-rw-r----- 1 tc_pst03 pst_pub 86K Oct 9 00:27 Li??2012_Modelling osteomyelitis_Control Model.cps
-rw-r----- 1 tc_pst03 pst_pub 46K Oct 9 00:27 Li??2012_Modelling osteomyelitis_Control Model.xml
-rw-r----- 1 tc_pst03 pst_pub 11K Oct 9 00:27 Li??2012_Modelling osteomyelitis_Control Model.sedml
tc_pst03#login-01: 6] mv Liò2012_Modelling\ osteomyelitis_Control\ Model.
When using a Java snippet to get all those files, I get the results which are Li??2012... instead of Liò. I have looked for shared solutions in our communities but no solution works for my problem. Below is the Java snippet I have tried to get the list of those files.
List<File> get(String modelId, int revisionNumber) throws ModelException {
File modelDirectory = new File(modelCacheDir, modelId)
File revisionDirectory
List returnedFiles = new LinkedList<File>()
try {
revisionDirectory = new File(modelDirectory, revisionNumber.toString())
if (!revisionDirectory.exists()) {
throw new FileNotFoundException()
} else {
returnedFiles = Files.list(revisionDirectory.toPath())*.toFile() //revisionDirectory.listFiles().toList()
}
if (returnedFiles?.isEmpty()) {
String message = """The cache directory of this model ${modelId} revision ${revisionNumber} is empty. \
The model cache builder will be launched again."""
throwModelException(modelId, message)
}
} catch (FileNotFoundException me) {
String message = """The files associated with this model ${modelId}, \
revision ${revisionNumber} hasn't been cached yet"""
throwModelException(modelId, message)
}
return returnedFiles
}
I suspected JVM does use the default charset so I manually enable UTF-8 by defining it in JAVA_TOOLS_OPTIONS: export JAVA_TOOLS_OPTIONS=-Dfile.encoding="UTF-8".
Some results are printed below:
[
._Li?2012_Modelling osteomyelitis_Control Model.xml4483388255187556135.tmp,
._Li?2012_Modelling osteomyelitis_Control Model.xml8578169841449575225.tmp,
Li��2012_Modelling osteomyelitis_Control Model.sedml,
._Li?2012_Modelling osteomyelitis_Control Model.xml1056906750418910165.tmp,
Li��2012_Modelling osteomyelitis_Control Model.xml,
Li��2012_Modelling osteomyelitis_Control Model.cps]
I need to get those files' names to compare with the same files names persisted in the database. However, the file names getting from the file system are decoded improperly so it is never equal to the ones already saved in the database.
Do anyone know why the issue is happening. Any ideas? Thanks!
While I've tried to use the following code snippet with the Groovy in-operator explanation the VerifyError has occured. Have you guys any idea about?
The code and console output is below.
class Hello extends ArrayList {
boolean isCase(Object val) {
return val == 66
}
static void main(args) {
def myList = new Hello()
myList << 55
assert 66 in myList
assert !myList.contains(66)
}
}
The error log:
Exception in thread "main" java.lang.VerifyError: (class: Hello, method: super$1$stream signature: ()Ljava/util/stream/Stream;) Illegal use of nonvirtual function call
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:259)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:116)
The code origin from the topic How does the Groovy in operator work?.
Update:
Groovy Version: 1.8.6 JVM: 1.6.0_45 Vendor: Sun Microsystems Inc. OS: Linux
Check this out.
It's for Java, but generally problem is, that you are using wrong library versions. The class is there, but different version than expected.
http://craftingjava.blogspot.co.uk/2012/08/3-reasons-for-javalangverfiyerror.html
Probably you have messed up Groovy or Java SDK installations.
public byte[] transform(ClassLoader loader, String className, Class<?> clazz,
ProtectionDomain domain, byte[] bytes)
throws IllegalClassFormatException {
return inspectClass(className, clazz, bytes);
}
private byte[] inspectClass(String name, Class<?> clazz, byte[] b) {
System.out.println("here"); //OK I see this print
ClassPool pool = ClassPool.getDefault();
System.out.println("inclass"); //can't see it !!
}
What can happen in ClassPool.getDefault();?
I had the same problem, and found ClassPool.getDefault was not throwing Exception, but Throwable.
In fact, it was throwing java.lang.NoClassDefFoundError.
In my manifest, I had:
Premain-Class: timing.TimingTransform
Boot-Class-Path: lib/javassist.jar
You likely just need to point the Boot-Class-Path to the javassist.jar file.
In my case, with the Boot-Class-Path above, I needed a lib directory with javassist.jar in it.
The mistake I made initially was putting javassist.jar inside the agent jar file
(THE FOLLOWING IS INCORRECT, FOR DEMONSTRATION PURPOSES ONLY):
0 Mon Oct 24 16:58:14 MST 2011 META-INF/
146 Mon Oct 24 16:58:14 MST 2011 META-INF/MANIFEST.MF
0 Thu Oct 20 14:58:06 MST 2011 timing/
2482 Mon Oct 24 16:58:06 MST 2011 timing/TimingStats.class
8360 Mon Oct 24 16:58:06 MST 2011 timing/TimingTransform.class
0 Tue Oct 18 17:28:24 MST 2011 lib/
645252 Fri Jul 08 18:24:58 MST 2011 lib/javassist.jar
Rather than putting javassist.jar inside the agent jar file, I put it in an external directory accessible to the program. After that change, it worked fine.
When I compile something like this:
public class MyClass
{
void myMethod(String name, String options, String query, String comment)
{
...
}
}
and compile this to a class file, it seems that argument names are lost. That is, when some other Java code references MyClass and wants to call or overwrite myMethod, my IDE (currently Eclipse) seems to get this method signature from the class-file:
void myMethod(String arg0, String arg1, String arg2, String arg3);
I know that Eclipse (and possibly other IDEs too) allows me to provide a link to the source or the Javadoc (as Bishiboosh pointed out) of MyClass and can take advantage of this. But I'm curious if there is some way to tell javac to include the names into the class-file, so that users of that class can see the argument names even if they only have the class file.
Solution for classes
When I compile a class with java -g:vars, the names of parameters are included in the class file. -g:vars seems to be equivalent to Eclipse -> project properties -> Java compiler -> Add variable attributes to generated class files.
This solution was suggested by several authors, but the answer from Nick finally made me believe.
On my machine, Eclipse sometimes used this info, sometimes it didn't, which was probably my fault or a bug in Eclipse, but not a problem with the class files or the compile. Anyway, now I know that the information is definitely present.
But no solution for interfaces
While this works (kind of) fine for classes, it's not working for interfaces.
For me, the logical reason seems to be, that -g:vars only provides the names of local variables, which is what the documentation for javac also states. In the body of a method, it's parameters are very similar to local variables, thus they are covered by -g:vars. interface methods don't have bodies, so they can't have local variables.
My initial question only asked for classes, because I was not aware that there might be any difference.
Class file format
As gid pointed out, the class file format does not support storrage of parameter names. I found a section in the class file spec that descibes a data struture which should holf the parameter names of methods, but this is definitely not used when compiling interfaces.
When compiling a class, I can't tell if the mentioned data structure is used, or if Eclipse infers the parameter names from the usage of parameters inside the method body. An expert could clarify this, but it's not that relevant I think.
To preserve names in the class file for debugging purposes try project properties, Java compiler, then "Add variable attributes to generated class files" (See Eclipse Help).
Compiling the following source:
public class StackOverflowTest {
public void test(String foo, String bar) {
// blah
}
}
Is decompiled into:
// Compiled from StackOverflowTest.java (version 1.5 : 49.0, super bit)
public class StackOverflowTest {
// Method descriptor #6 ()V
// Stack: 1, Locals: 1
public StackOverflowTest();
0 aload_0 [this]
1 invokespecial java.lang.Object() [8]
4 return
Line numbers:
[pc: 0, line: 1]
Local variable table:
[pc: 0, pc: 5] local: this index: 0 type: StackOverflowTest
// Method descriptor #15 (Ljava/lang/String;Ljava/lang/String;)V
// Stack: 0, Locals: 3
public void test(java.lang.String foo, java.lang.String bar);
0 return
Line numbers:
[pc: 0, line: 4]
Local variable table:
[pc: 0, pc: 1] local: this index: 0 type: StackOverflowTest
[pc: 0, pc: 1] local: foo index: 1 type: java.lang.String
[pc: 0, pc: 1] local: bar index: 2 type: java.lang.String
}
See the parameter names are preserved in the class files.
I would suggest you look into how your source is being compiled, which version it is compiled for etc.
EDIT:
Ah, I see this is different for interfaces - they don't seem to have this information available for the debugger which I guess makes sense. I don't think there'll be a way round this, if you just want to see the parameter names when you're editing source you'll need to go the javadoc route as Nagrom_17 suggests (attach the source).
You don't specially need the source to make arg names appear in Eclipse...If you specify the Javadoc, Eclipse will display the args.
It might help to compile with debug support, which stores all names in the .class file.
though I don't know whether Eclipse takes that into account.
Eclipse will pick up the names of arguments if you include debug information in the class file: javac -g:vars should be enough.
There is no support in the class file data structure for storing the parameter names to any method, no matter what javac options you use.
In order to see the original names in an IDE you have to supply them with either the javadoc or the source.
If you have a particular need to get at them at runtime it is possible to add annotations to parameters, but you'll have to create your own as there isn't a standard set to use.
Sorry can't be more helpful
EDIT:
I stand completely corrected...the class file format does clearly have space for named parameters (JLS 4.7)
What I can't see is how the hell you can get at them using java.lang.reflect.*
You don't need a separate Javadoc file you can create 'inline' javadocs in Eclipse using a special comment with two asterisks(*) after the first slash of a multi-line comment.
example code:
public class MyClass
{
/**
* documentation of your method
*
* #param name a String describing the name
* #param options used to describe current option
* #param query
* #param comment
* #return void
*/
void myMethod(String name, String options, String query, String comment)
{
...
}
}
Duplicate:
Tool to read and display Java .class versions
If I have a compiled Java class, is there a way to tell from just the class file what its target version compatibility is? Specifically, I have a number of class files, compiled to Java 6, which are running under Java 5 and giving the the "Unrecognized version" error. I want to be able to look at a class file and find what its target version compatibility is without running the JVM. Any ideas?
You can use the javap utility that comes with the standard JDK.
javap -verbose MyClass
Compiled from “MyClass.java”
public class MyClass extends java.lang.Object
SourceFile: “MyClass.java”
minor version: 3
major version: 45
I've found this on the net and it works.
Every '.class' file starts off with
the following:
Magic Number [4 bytes]
Version Information [4 bytes]
A hexdump of a '.class' file compiled
with each of the following options
reveals:
javac -target 1.1 ==> CA FE BA BE 00 03 00 2D
javac -target 1.2 ==> CA FE BA BE 00 00 00 2E
javac -target 1.3 ==> CA FE BA BE 00 00 00 2F
javac -target 1.4 ==> CA FE BA BE 00 00 00 30
Perhaps you could use this information
to write your own '.class' file
version checking utility, using Java,
or perhaps a scripting or shell
language ;) !
I hope this helps.
Anthony Borla
From: http://bytes.com/groups/java/16603-how-determine-java-bytecode-version
Linux/Unix users have a nice tool out of the standard toolbox: file utility. Modern versions can detect the Java class fersion version and even output Java version for known class file types.
Example output:
com/sample/Tracker.class: compiled Java class data, version 45.3
com/sample/TestListener.class: compiled Java class data, version 49.0 (Java 1.5)
And it fits very nicely into the standard Unix scripting toolchain.
Taken from: http://twit88.com/blog/2008/09/22/java-check-class-version/
try {
String filename = "C:\\MyClass.class";
DataInputStream in = new DataInputStream(new FileInputStream(filename));
int magic = in.readInt();
if (magic != 0xcafebabe) {
log.info(filename + " is not a valid class!");
}
int minor = in.readUnsignedShort();
int major = in.readUnsignedShort();
log.info(filename + ": " + major + " . " + minor);
in.close();
} catch (IOException e) {
log.info("Exception: " + e.getMessage(), e);
}
You can look at the byte offset 6 and 7 in the file (in a hex dump probably), which tells you which version is used. I think the Bytecode Visualizer (eclipse plugin) can see which version a class file is made for.
Further reading