Translate package+class name to .class filename - java

I'm writing a program which analyzes .class files. I want to attach package and class name to my output.
My plan is to write a function which takes the package and class name as input and finds the corresponding .class file (so the user doesn't have to enter that as well, and can't get it wrong), where 'find' should be read as 'give me some data I can use as arguments for BCEL's ClassParser constructor' (either a filename, or a name of a zip file and a name within the zip file).
How do I go about that? Does java come with something which does that? I understand name resolution to be done in the context of a CLASSPATH, so the user should probably supply one of those as well; that's fine.
Note: solutions should not execute any code from the .class file. Just the bytes, ma'm ;-)

Well, if you are using BCEL, everything is already there, see ClassPath:
import java.io.IOException;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.util.ClassPath;
public class BcelTest {
public static void main(String[] args) throws IOException {
String classPath=System.getProperty("java.class.path");
// demonstrating with our own class path examplary for an arbitrary path String
ClassPath cp=new ClassPath(classPath);
ClassPath.ClassFile cf=cp.getClassFile(BcelTest.class.getName());
ClassParser p=new ClassParser(cf.getInputStream(), cf.getPath());
JavaClass jc = p.parse();
System.out.println(jc);
// or just using our own system path explicitly
cf=ClassPath.SYSTEM_CLASS_PATH.getClassFile("java.lang.Object");
p=new ClassParser(cf.getInputStream(), cf.getPath());
jc = p.parse();
System.out.println(jc);
}
}

If you already have the classfile, just replace every / in the class name with the directory separator for your system and append .class.
If you want to go from Java class name to bytecode class name, most classes are pretty simple. Prepend the package + '.' and replace '.' with '/'. For inner and nested classes, I'm not sure if there is a simple algorithm.

I want to thank the fine folks in irc://freenode.org/##java for guiding me towards the following recipe:
// replace 'Throwable' with something else in production code :-)
JavaClass recipe() throws Throwable {
URLClassLoader loader = new URLClassLoader(new URL[] {
new URL("file:///usr/share/java/js.jar"),
});
String path = "org/mozilla/classfile/ByteCode.class";
InputStream resource = loader.getResourceAsStream(path);
String fake_filename = "<" + path + ">";
ClassParser classparser = new ClassParser(resource, fake_filename);
JavaClass java_class = classparser.parse();
// System.out.format("%s\n", jc);
return java_class;
}
One could de-hardcode the classpath and the value of path by computing strings in the right format based on user input.
I'm going to use the answer I have accepted because it takes less work on my part, but I want to mention this answer because it is more generally applicable (i.e. with fewer dependencies).

Related

Reading File from another Package in Java using getResourceAsStream()

I am trying to read a file called "numbers.txt" which is filled with int numbers (one int per line, no empty lines). I want to put the numbers into an array and return this array (method readIntsFromFile). Unfoutnetly the File does not get found, so the InputStream cls returns null. If I put the file "numbers.txt" into the same folder as the Class ReadFileInput it works but I do not know why it does not work if I put the "numbers.txt" file into the resources folder. I thought getResourceAsStream() can get it there? The main just creates a new class Object and calls the class function with the parameter "numbers.txt" and saves the result in an array.
Here is the Class with the method readIntsFromFile:
package impl;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
public class ReadFileInput {
public int[] readIntsFromFile(String path) {
ArrayList<Integer> arrList = new ArrayList<>();
try {
InputStream cls = ReadFileInput.class.getResourceAsStream(path);
System.out.println("Get Resources as Stream: " + cls);
//Put cls in ArrayList
}
cls.close();
} catch (Exception e) {
System.out.println(e);
}
int[] arr = arrList.stream().mapToInt(Integer::intValue).toArray();
System.out.println(arr);
return arr;
}
}
Do you know how it could work?
All the best!
The string value you pass to SomeClass.class.getResource() (as well as getResourceAsStream, of course) has two 'modes'.
It looks 'relative to the package of SomeClass', which means, effectively, if you ask for e.g. "img/load.png", and SomeClass is in the package com.foo, you're really asking for "/com/foo/img/load.png" - this is relative mode.
You want to go into absolute mode: You want the 'classpath source' that provided the SomeClass.class file that the JVM used to load the code for SomeClass, and then look in that same place for, say, /img/load.png from there. In other words, if SomeClass's code was obtained by loading the bytecode from jar file entry /com/foo/SomeClass.class in jarfile myapp.jar, you want the data obtained by loading the jar file entry /img/load.png from jarfile myapp.jar. You can do that - simply start with a slash.
Given that your numbers.txt file appears to be in the 'root' of resources, that would strongly suggest the string you pass to .getResourceAsStream() should be "/numbers.txt". Note the leading slash.
NB: It's not quite a path. .. and such do not work. Weirdly, com.foo.numbers.txt would, if the jar file contains /com/foo/numbers.txt.

Given only the full path to a .class file, how can I load its Class object?

I'm working on an application that needs to be able to do some analysis on Java code; some through the text and some through reflection. The user should be able to input a directory and the program will pull all .java and .class files from it. I have no problem with the .java files but when I try getting the class of the .class file it doesn't work.
I've looked at using ClassLoader with URL. What I've been able to find so far has given me this
URL url = this.openFile().toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass(this.path);
return cls;
path is just a string containing the actual path of the .class file in question, e.g. Users/me/Documents/Application/out/production/MyPackage/MyClass.class. From what I understand from my own reading, this method ties me to knowing the package structure of the input, but in general I don't. All I have is the absolute path of the .class file. Is there a way, just using this path (or some simple transformation of it) that I can load into my program the actual MyClass class object and start doing reflection on it?
You have 2 options:
Use a byte code library to first read the file, so you can find out what the actual class name is.
E.g. in your example it is probably MyPackage.MyClass, but you can't know that from the fully qualified file name.
Implement your own ClassLoader subclass, and call defineClass(byte[] b, int off, int len).
Recommend using option 2.

Reading a file from resource package

I have read many posts on here and done many Google searches but I am still unable to read a file from another java package within my current project.
In one of my classes I have the following code, however the input stream is always null. I have tried using the context class loader, as well as many other solutions but to no avail.
InputStream is = this.getClass().getResourceAsStream( "opcodes.txt" );
System.out.println(is);
Another attempt was:
InputStream is = ClassName.class.getResourcceAsStream("/resources/opcodes.txt");
which also returned null.
Any help or explanation for why I can not find the file within a resource package would be great.
p.s. I am using eclipse if that makes a difference.
EDIT: If I use an OpenFileDialog to find the file, I am able to open and read it, so the file does exist and is not corrupt.
The documentation of the getResourceAsStream() method, found here:
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)
says that:
"
Before delegation, an absolute resource name is constructed from the given resource name using this algorithm:
If the name begins with a '/' ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'.
Otherwise, the absolute name is of the following form:
modified_package_name/name
Where the modified_package_name is the package name of this object with '/' substituted for '.' ('\u002e').
"
Your first attempt would have succeeded had the resource been located within the same package as the one the class you're invoking the method from is located. Your second attempt fails because, like the documentation says, the name of the file you give ("/resources/opcodes.txt") is replaced by "resources/opcodes.txt". I guess it means that the method will now search for package resources WITHIN the package where the class that invokes the method is located and not outside of it. Since you don't have such an inner package, the method returns null.
A workaround would be to define a class within the package where your resource is located. That class could be empty for all you care. Just call:
ClassWithinResourcePackage.class.getResourceAsStream("opcodes.txt");
from within the class you currently invoke the method from, and it works.
I also tried to use the ".." syntax I know from command line, but it doesn't work. I think the documentation implies the method is looking for a file within the same package only.
Resource files are generally placed in src/main/resources/ directory in your project. These resources are also packed in the jar alongside the compiled class files with .class extension.
I have created a sample maven project with the directory hierarchy discussed above.
The resource file sample-resource.properties can be accessed in the program as below:
package temp;
import java.io.InputStream;
import java.util.Scanner;
public class ResourceTest {
public static void main(String[] args) {
InputStream inputStream = ResourceTest.class.getResourceAsStream("/sample-resource.properties");
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
}
}
This would read and print the contents of the resource file:

Am I accessing these resource files in java incorrectly?

I have two classes. One is intended to be a common library and another a client that consumes from the library.
The library is structured as below:
public class Library
{
public Library()
{
String pathToResourceA="src/main/resources/A.xls";// A.xls is present within resources
String key="apples";
Resource res= loadResourceBasedOnDoc(pathToResource,key);
...//process resource
}
}
It's corresponding test class
public class LibraryTest
{
#Test
public void testLibrary()
{
new Library();// works as expected- the test passes and the resource is loaded.//ie. A.xls is found in the right place
}
}
However, when I try to access the library from my client in the following manner
import packagename.Library
public class Client{
Library lib;
public Client()
{
lib= new Library();// throws a FileNotFoundException!
}
}
I get a FileNotFoundException. I'm guessing this is something to do with defining the right value to pathToResourceA in class A but cant figure out what it is. Any thoughts would be appreciated. Thanks!
Code for loadResourcesBasedonDoc
protected Resource loadResourceBasedOnDoc(String filename,String password)
{
InputStream in = null;
try {
in = new FileInputStream(filename);
//further in is processed...
The problem is that you are using relative file paths:
String pathToResourceA="src/main/resources/A.xls";// A.xls is present within resources
This is then processed in the loadResourceBasedOnDoc method as follows:
in = new FileInputStream(filename);
This call looks for the file (src/main/resources/A.xls) on the file system. Because the path is relative, it looks for the file in a directory that is relative to the current working directory. In your unit test, the current working directory is probably the directory from which the test is launched. Assuming that is the root of your project, the test likely finds A.xls on the file system within your project.
To solve this, I recommend using full pathnames in the loadResourceBasedOnDoc as follows:
String programLaunchDir = System.getProperty("user.dir")
in = new FileInputStream(programLaunchDir + File.separator + filename);
The user.dir system property should be the current working directory. So if you can enforce that the program is launched from that dir, you are all set. Otherwise, you might want to wrap you program in a batch file or shell script that passes the directory to the program.

.java file path to class name

What's the most efficient way of getting the class(es) created on a .java file? I have the .java file path and I need to get the class full name.
I can only remember:
Compile it with JavaCompiler
Using the file text to parse it with Eclipse ASTParser (or another)
Infer the class name through part of the file path, but I don't know
if this works for every cases
Somehow, use a tool like javap (dind't really thought about this one)
EDIT
I have this file, located at C:\myfolder\MyClass.java (let's ignore package and folder association conventions):
package mypackage.mysubpackage;
public class MyClass
{
// my class implementation here
public class MyInnerClass
{
// my inner class implementation here
}
}
The full name of the classes declared in this file are:
mypackage.mysubpackage.MyClass
mypackage.mysubpackage.MyClass.MyInnerClass (I don't know if this
one it's correct, but let's pretend it is)
How can I get those class when I only have the .java file path (C:\myfolder\MyClass.java) ?
The only way to reliably obtain the names of the classes (mind that it may also define interfaces) files a .java file declares would be to really parse the java language contained in that file.
And even then you will need to know which compiler will be/has been used to compile the .java file, as a java compiler could use any naming convention it likes for anonymous classes (the Oracle compiler uses $1, $2..., but there is no strict need to mimic that behavior).
Considering these obstacles I believe its very hard to do from the .java files contents and simply impossible with the .java files path alone.
The most effective way is Class.forName().getName()
I have the .java file path and I need to get the class full name.
Which means, you know the path of .java file and you want the class name of each class file.
class Filter {
public static void main(String[] a) {
Filter f = new Filter();
String dirName = "D:\\Yourfolder\\"; // assuming your java file are located in D:\Yourfolder\
f.finder(dirName); // call the method for listing all the class file
}
public File[] finder(String dirName) {
File dir = new File(dirName);
return dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String filename) {
if(filename.endsWith(".class"))
{
System.out.println(filename);
}
return filename.endsWith(".class");
}
});
}
}
Replace dirName with your .java directory path.
One approach is to scan the directory tree where your Java source files are located, and for each file ending in ".java", you take its full folder path as a String and convert each dir separator to a '.' character. This will give you the fully qualified class name (FQCN). For example, if the path is: com\foo\fee\Foo.java, that becomes com.foo.fee.Foo.
Of course, this does not give you inner or nested classes and other advanced things, but these are created when you compile.
I have seen this kind of directory scanning in many frameworks, even Spring.
I am working on this in Groovy, so far I have:
File file = new File(rootSourcePath)
file.eachFileRecurse(FILES){
def path =it.getAbsolutePath()
println path
if(path.endsWith(".java")){
// to do the conversion here
}
}
Hope this interpreted your question correctly.
To get the name of the class file Try this
void printClassName(String classname)
{
System.out.println("The class name " + classname +" is " + classname.getClass().getName());
}

Categories