Using URLClassLoader to load .class file - java

I'm aware that this question has been asked before:
How to use URLClassLoader to load a *.class file?
However I don't really understand due to the lack of example. I'm currently working on a project and trying to load user-given .class objects which can be located in any directories on the machine.
//Create URL to hash function class file
URL url_hashFunctionPath = new URL("file:///" + _sHashFunctionFilePath);
//Packet URL to a URL array to be used by URLClassLoader
URL[] urlA_hashFunctionPath = {url_hashFunctionPath};
//Load URL for hash function via a URL class loader
URLClassLoader urlCl_hashFunctionClassLoader = new URLClassLoader(urlA_hashFunctionPath);
//Load user hash function into class to initialize later (TEST: HARD CODE FOR NOW)
m_classUserHashClass = urlCl_hashFunctionClassLoader.loadClass(_sUserHashClassName);
The last line gave me a ClassNotFoundException, from my experiment & understanding the user-given class function has to be in the classpath?
PS: 1st time posting questions feel free to correct me where I did not follow the appropriate manner.
//SOLUTION
The solution that I arrived at with the generous help of [WillShackleford][1], this solution can load the a .class file in a given filepath. For more information refer to code and their given comments.
//The absolute file path to the class that is to be loaded (_sHashFunctionFilePath = absolute file path)
String pathToClassFile = _sHashFunctionFilePath;
System.out.println("File to class: " + _sHashFunctionFilePath);
//Declare the process builder to execute class file at run time (Provided filepath to class)
ProcessBuilder pb = new ProcessBuilder("javap", pathToClassFile);
try
{
//Start the process builder
Process p = pb.start();
//Declare string to hold class name
String classname = null;
//Declare buffer reader to read the class file & get class name
try(BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())))
{
String line;
while(null != (line = br.readLine()))
{
if(line.startsWith("public class"))
{
classname = line.split(" ")[2];
break;
}
}
System.out.println("classname = " + classname);
}
catch(IOException _error)
{
}
//Declare file path to directory containing class to be loaded
String pathToPackageBase = pathToClassFile.substring(0, pathToClassFile.length() - (classname + ".class").length());
System.out.println("pathToPackageBase = " + pathToPackageBase);
try
{
//Create class to hold the class to be loaded via a URL class loader
Class clss = new URLClassLoader(
new URL[]{new File(pathToPackageBase).toURI().toURL()}
).loadClass(classname);
//Create ab object/instance of said class
Object test = clss.newInstance();
//Declare & create requested method from user hash function class (Method is work & takes no arguments)
Method method = clss.getMethod("work", null);
method.invoke(test, null);
}

In the directory /home/shackle/somedir/classes/pkg I have a file Test.class created from a java file with package pkg; eg :
package pkg;
public class Test {
public String toString() {
return "secret_string";
}
}
Then I load it with :
System.out.println(new URLClassLoader(
new URL[]{new File("/home/shackle/somedir/classes").toURI().toURL()}
).loadClass("pkg.Test").newInstance().toString());
Notice that I do not put the pkg/Test in the URL string but the load class argument has the pkg. prefix.
You can get the class name directly from the file like this:
Class clsReaderClss = ClassLoader.getSystemClassLoader().loadClass("jdk.internal.org.objectweb.asm.ClassReader");
System.out.println("clsReaderClss = " + clsReaderClss);
Constructor con = clsReaderClss.getConstructor(InputStream.class);
Object reader = con.newInstance(new FileInputStream(directFile));
Method m = clsReaderClss.getMethod("getClassName");
String name = m.invoke(reader).toString().replace('/', '.');
System.out.println("name = " + name);
An alternative that doesn't require access to internal classes.
String pathToClassFile = "/home/shackle/somedir/classes/pkg/Test.class";
ProcessBuilder pb = new ProcessBuilder("javap",pathToClassFile);
Process p = pb.start();
String classname = null;
try(BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
String line;
while(null != (line = br.readLine())) {
if(line.startsWith("public class")) {
classname = line.split(" ")[2];
break;
}
}
}
System.out.println("classname = " + classname);
Class can then be loaded with:
String pathToPackageBase = pathToClassFile.substring(0, pathToClassFile.length() - (classname + ".class").length());
System.out.println("pathToPackagBase = " + pathToPackageBase);
Class clss = new URLClassLoader(
new URL[]{new File(pathToPackageBase).toURI().toURL()}
).loadClass(classname);
System.out.println(clss.newInstance().toString());

Your _sHashFunctionFilePath needs to have the package name of the target class removed from it, so the ClassLoader will look in _sHashFunctionFilePath + package.name + HashFunction.class as the path to the file. If you don't do that, the ClassLoader won't be able to find the file.
So if the target class is my.great.HashFunction in HashFunction.class, then it needs to be in a directory called my/great/ if you want to use URLClassLoader. Then, you'd use /path/to as the file:/// URL for your URLClassLoader if the .class file was actually found in /path/to/my/great/HashFunction.class.

Related

Copying the content of a list into a folder

I have an arrayList "rules" containing Rules. Each Rule is an XML file and have some properties such as filename...
I want to copy the Rules from the arraylist to a folder named AllMRG. I tried the code between comments but I get the message "Source 'RG6.31.xml' does not exist".
I changed the code by the following, but there is still a problem with 'RG6.31.xml' and the folder AllMRG is empty even though the arrayList contains many Rules!
First attemption:
File AllMRGFolder = new File("AllMRG");
for(int p = 0; p < rules.size(); p++) {
/* File MRGFile = new File(rules.get(p).fileName);
FileUtils.copyFileToDirectory(MRGFile, AllMRGFolder); */
File MRGFile = new File("AllMRG/" + rules.get(p).fileName);
if (!MRGFile.exists()) {
FileUtils.copyFileToDirectory(MRGFile, AllMRGFolder);
}
}
Second attemption:
String path = "AllMRG";
for(Rule rule : rules) {
File MRGFile = new File(rule.fileName);
Files.copy(MRGFile.toPath(), (new File(path + MRGFile.getName())).toPath(), StandardCopyOption.REPLACE_EXISTING);
}
PS: Rule is a class
public class Rule implements Comparable{
public String fileName;
public String matches;
public String TPinstances;
public int nbrOfMatches;
public double T;
#Override
public int compareTo(Object o) {
if(o instanceof Rule){
//processing to compare one Rule with another
}
return 0;
}
}
Here is the entire code after having considered Shyam's answer. The same problem persists!
Path directoryPath = Files.createDirectory(Paths.get("AllMGR"));
for(Rule rule : rules) {
Path filePath = directoryPath.resolve(rule.fileName);
Files.createFile(filePath);
File MRGFile = new File(rule.fileName);
String ruleContent = new String(Files.readAllBytes(Paths.get(MRGFile.getPath())));
String fileContent = new String(Files.readAllBytes(filePath));
fileContent=ruleContent;
PrintWriter out13= new PrintWriter("AllMGR/"+rule.fileName+".xml");
out13.print(fileContent);
out13.close();
}
Firstly, you are creating a new File with rule.filename without giving any predefined path. Then, you are building a path like: path + MRGFile.getName() without any path delimiters and trying to copy the file to this location. I don't think this will work.
What can actually help you is, creating a base directory first and then creating individual files in it.
Create base directory:
Path directoryPath = Files.createDirectory(Paths.get("AllMGRDir"));
Then for each of your Rule object you can crate file using:
for(Rule rule : rules) {
Path filePath = directoryPath.resolve(rule.fileName());
Files.createFile(filePath);
// your remaining code
}
The resolve(String other) method resolves the given path. Java doc says that:
Converts a given path string to a Path and resolves it against this
Path in exactly the manner specified by the resolve(Path) method.
For example, suppose that the name separator is "/" and a path
represents "foo/bar", then invoking this method with the path string
"gus" will result in the Path "foo/bar/gus"
Hope this helps.

getResource(...).getFile() method throwing FileNotFoundException if name has white spaces [duplicate]

I would like to read a resource from within my jar like so:
File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferedReader reader = new BufferedReader(new FileReader(file));
//Read the file
and it works fine when running it in Eclipse, but if I export it to a jar, and then run it, there is an IllegalArgumentException:
Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical
and I really don't know why but with some testing I found if I change
file = new File(getClass().getResource("/file.txt").toURI());
to
file = new File(getClass().getResource("/folder/file.txt").toURI());
then it works the opposite (it works in jar but not eclipse).
I'm using Eclipse and the folder with my file is in a class folder.
Rather than trying to address the resource as a File just ask the ClassLoader to return an InputStream for the resource instead via getResourceAsStream:
try (InputStream in = getClass().getResourceAsStream("/file.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
// Use resource
}
As long as the file.txt resource is available on the classpath then this approach will work the same way regardless of whether the file.txt resource is in a classes/ directory or inside a jar.
The URI is not hierarchical occurs because the URI for a resource within a jar file is going to look something like this: file:/example.jar!/file.txt. You cannot read the entries within a jar (a zip file) like it was a plain old File.
This is explained well by the answers to:
How do I read a resource file from a Java jar file?
Java Jar file: use resource errors: URI is not hierarchical
To access a file in a jar you have two options:
Place the file in directory structure matching your package name (after extracting .jar file, it should be in the same directory as .class file), then access it using getClass().getResourceAsStream("file.txt")
Place the file at the root (after extracting .jar file, it should be in the root), then access it using Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")
The first option may not work when jar is used as a plugin.
I had this problem before and I made fallback way for loading. Basically first way work within .jar file and second way works within eclipse or other IDE.
public class MyClass {
public static InputStream accessFile() {
String resource = "my-file-located-in-resources.txt";
// this is the path within the jar file
InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
if (input == null) {
// this is how we load file within editor (eg eclipse)
input = MyClass.class.getClassLoader().getResourceAsStream(resource);
}
return input;
}
}
Up until now (December 2017), this is the only solution I found which works both inside and outside the IDE.
Use PathMatchingResourcePatternResolver
Note: it works also in spring-boot
In this example I'm reading some files located in src/main/resources/my_folder:
try {
// Get all the files under this inner resource folder: my_folder
String scannedPackage = "my_folder/*";
PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
Resource[] resources = scanner.getResources(scannedPackage);
if (resources == null || resources.length == 0)
log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
else {
for (Resource resource : resources) {
log.info(resource.getFilename());
// Read the file content (I used BufferedReader, but there are other solutions for that):
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
// ...
// ...
}
bufferedReader.close();
}
}
} catch (Exception e) {
throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}
The problem is that certain third party libraries require file pathnames rather than input streams. Most of the answers don't address this issue.
In this case, one workaround is to copy the resource contents into a temporary file. The following example uses jUnit's TemporaryFolder.
private List<String> decomposePath(String path){
List<String> reversed = Lists.newArrayList();
File currFile = new File(path);
while(currFile != null){
reversed.add(currFile.getName());
currFile = currFile.getParentFile();
}
return Lists.reverse(reversed);
}
private String writeResourceToFile(String resourceName) throws IOException {
ClassLoader loader = getClass().getClassLoader();
InputStream configStream = loader.getResourceAsStream(resourceName);
List<String> pathComponents = decomposePath(resourceName);
folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
File tmpFile = folder.newFile(resourceName);
Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
return tmpFile.getAbsolutePath();
}
In my case I finally made it with
import java.lang.Thread;
import java.io.BufferedReader;
import java.io.InputStreamReader;
final BufferedReader in = new BufferedReader(new InputStreamReader(
Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt"))
); // no initial slash in file.txt
Make sure that you work with the correct separator. I replaced all / in a relative path with a File.separator. This worked fine in the IDE, however did not work in the build JAR.
I have found a fix
BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));
Replace "Main" with the java class you coded it in. replace "path" with the path within the jar file.
for example, if you put State1.txt in the package com.issac.state, then type the path as "/com/issac/state/State1" if you run Linux or Mac. If you run Windows then type the path as "\com\issac\state\State1". Don't add the .txt extension to the file unless the File not found exception occurs.
This code works both in Eclipse and in Exported Runnable JAR
private String writeResourceToFile(String resourceName) throws IOException {
File outFile = new File(certPath + File.separator + resourceName);
if (outFile.isFile())
return outFile.getAbsolutePath();
InputStream resourceStream = null;
// Java: In caso di JAR dentro il JAR applicativo
URLClassLoader urlClassLoader = (URLClassLoader)Cypher.class.getClassLoader();
URL url = urlClassLoader.findResource(resourceName);
if (url != null) {
URLConnection conn = url.openConnection();
if (conn != null) {
resourceStream = conn.getInputStream();
}
}
if (resourceStream != null) {
Files.copy(resourceStream, outFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
return outFile.getAbsolutePath();
} else {
System.out.println("Embedded Resource " + resourceName + " not found.");
}
return "";
}
finally i solved errors:
String input_path = "resources\\file.txt";
input_path = input_path.replace("\\", "/"); // doesn't work with back slash
URL file_url = getClass().getClassLoader().getResource(input_path);
String file_path = new URI(file_url.toString().replace(" ","%20")).getSchemeSpecificPart();
InputStream file_inputStream = file_url.openStream();
You can use class loader which will read from classpath as ROOT path (without "/" in the beginning)
InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
For some reason classLoader.getResource() always returned null when I deployed the web application to WildFly 14. getting classLoader from getClass().getClassLoader() or Thread.currentThread().getContextClassLoader() returns null.
getClass().getClassLoader() API doc says,
"Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader."
may be if you are using WildFly and yours web application try this
request.getServletContext().getResource() returned the resource url. Here request is an object of ServletRequest.
If you are using spring, then you can use the the following method to read file from src/main/resources:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;
public String readFileToString(String path) throws IOException {
StringBuilder resultBuilder = new StringBuilder("");
ClassPathResource resource = new ClassPathResource(path);
try (
InputStream inputStream = resource.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
resultBuilder.append(line);
}
}
return resultBuilder.toString();
}
Below code works with Spring boot(kotlin):
val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
If you wanna read as a file, I believe there still is a similar solution:
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("file/test.xml").getFile());

Cannot create file with Java using the Parent and Child parameters of the new File constructor

I am trying to create a file using Java. I want to create this file in a sub-folder of my "Documents" directory. I want this sub-folder to be based today's date.
I thought I new how to use the File class and file.mkdirs() method properly, but I guess I don't.
Here is what I have:
public class FileTest {
private static final String sdfTimestampFormat = "yyyy-MM-dd HH:mm:ss Z";
private static final SimpleDateFormat timestampSDF = new SimpleDateFormat(sdfTimestampFormat);
private static final String sdfDirFormat = "yyyy-MM-dd";
private static final SimpleDateFormat dirSDF = new SimpleDateFormat(sdfDirFormat);
public static void test() throws FileNotFoundException, IOException{
Date rightNow = new Date();
String data = "the quick brown fox jumps over the lazy dog";
String path = System.getProperty("user.home");
String filename = "file.txt";
String directory_name = path + System.getProperty("file.separator") + "Documents" + System.getProperty("file.separator") + dirSDF.format(rightNow);
File file = new File(directory_name, filename);
if(file.mkdirs()){
String outstring = timestampSDF.format(rightNow) + " | " + data + System.getProperty("line.separator");
FileOutputStream fos = new FileOutputStream(file, true);
fos.write(outstring.getBytes());
fos.close();
}
}
}
What is happening is that the following directory is created:
C:\Users\<username>\Documents\2018-08-03\file.txt\
I was under the impression that the Parent parameter of the new File constructor was the base directory, and the Child paramater of the new File constructor was the file itself.
Is this not the case? Do I need two File objects, one for the base directory and another for the file?
What I want is this:
C:\Users\<username>\Documents\2018-08-03\file.txt
Thanks.
mkdirs() will do directories (if they don't exist) for each element in your path.
So you can use file.getParentFile().mkdirs() to not make a directory for your file.txt
Edit: Something to consider
mkdirs() only returns true if it actually created directories. If they already existed or there was a problem creating them it will return false
Since you are trying to run this multiple times to append to your text your logic will not run inside your if-statement
I would change it to:
boolean created = true;
if(!file.getParentFile().exists()) {
created = file.getParentFile().mkdirs();
}
if (created) {
String outstring = timestampSDF.format(rightNow) + " | " + data + System.getProperty("line.separator");
FileOutputStream fos = new FileOutputStream(file, true);
fos.write(outstring.getBytes());
fos.close();
}

Java Load File to pass to other class

What is the best way to load a file into a java application to be passed to another class?
Currently I am using JFileChooser to select a source file (C, C++, Java) which is then passed to an executable called src2srcml. My code runs the src2srcml tool which takes the source file and converts it to an XML which is then stored in my workspace (eclipse). I then want to take that XML file and pass it over to another class to be analysed. As you can see below I am currently trying the getResources method. It can find the file fine but I don't actually know how to pass it to the class UnitXMLReader. GetResources returns a URL to the file but the other class needs the filepath. Is there a better way to find the file?
JButton btnRunSourceCode = new JButton("Run Source Code");
btnRunSourceCode.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//------Check for loaded file ----//
if(filePath == null){
textArea.setText("Please Load a source file (C, C++, Java)");
}
else{
try{
int c;
textArea.setText("Converting Source Code to XML");
String workspace = System.getProperty("user.dir");
String classPath = System.getProperty("java.class.path");
String[] commands = {"/bin/bash", "-c", "cd " + workspace + " && ./src2srcml --position " + selectedFile.getName() + " -o " + classPath + "/xmlParseGUI/targetFile.xml"};
Process src2XML = Runtime.getRuntime().exec(commands);
InputStream in1 = src2XML.getErrorStream();
InputStream in2 = src2XML.getInputStream();
while ((c = in1.read()) != -1 || (c = in2.read()) != -1) {
System.out.print((char)c);
}
src2XML.waitFor();
}
catch(Exception exc){/*src2srcml Fail*/}
}
ParallelXMlGUI c = new ParallelXMlGUI();
Class<? extends ParallelXMlGUI> cls = c.getClass();
// finds resource relative to the class location
URL url = cls.getResource("targetFile.xml");
//UnitXMLReader.ChosenFile = filePath;
//UnitXMLReader.main(null);
System.out.println("Value = " + url);
File file = new File(classPath + "/xmlParseGUI/targetFile.xml);
Java has a file class for handling files. Try creating a new File object that points to that file. Then pass the new File object.
Or you could try relative path:
File file = new File("/smlParseGUI/targetFile.xml");
A couple File methods:
file.getPath();
file.getString();

Weird exception in thread "main" java.io.FileNotFoundException I/O Java

I have this error when I am trying to read the file:
Exception in thread "main" java.io.FileNotFoundException: \src\product.txt (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:120)
at dao.Inventory.readFile(Inventory.java:30)
at view.InventoryView.init(InventoryView.java:33)
at view.InventoryView.<init>(InventoryView.java:21)
at view.InventoryView.main(InventoryView.java:211)
But the thing is, I have the product.txt in my src folder.
My code is the following:
public void readFile() throws IOException {
// input file must be supplied in the first argument
InputStream istream;
File inputFile = new File("\\src\\product.txt");
istream = new FileInputStream(inputFile);
BufferedReader lineReader;
lineReader = new BufferedReader(new InputStreamReader(istream));
String line;
while ((line = lineReader.readLine()) != null) {
StringTokenizer tokens = new StringTokenizer(line, "\t");
// String tmp = tokens.nextToken();
// System.out.println("token " + tmp);
ActionProduct p = new ActionProduct();
prodlist.add(p);
String category = p.getCategory();
category = tokens.nextToken();
System.out.println("got category " +category);
int item = p.getItem();
item = Integer.parseInt(tokens.nextToken());
String name = p.getName();
System.out.println("got name " +name);
double price = p.getPrice();
price = Double.parseDouble(tokens.nextToken());
int units = p.getUnits();
units = Integer.parseInt(tokens.nextToken());
}
}
I don't think anything is wrong with my code. Also, I saw a similar post about a hidden extension like FILE.TXT.TXT, how would you show a hidden extension in MacOSX?? Any suggestions? (Would there be any other problem besides the hidden extension issue?)
/src/product.txt is an absolute path, so the program will try to find the file in the src folder of your root path (/). Use src/product.txt so the program will use this as a relative path.
It's possible (most likely?) that your Java code is not executing inside the parent folder of src, but instead inside a 'class' or a 'bin' folder with the compiled java .class files.
Assuming that 'src' and 'bin' are in the same directory, you could try ..\\src\\product.txt
See also http://en.wikipedia.org/wiki/Path_(computing)
As other commenters stated, the path is absolute and points to
\src\product.txt which is (hopefully) not where
your sources are stored.
The path separator should be set in an OS-independent manner using
the System.getProperty("path.separator") property. On a Unix system, you'll have trouble with hard coded backslashes as path separators. Keep it portable!
String pathSeparator = System.getProperty("path.separator");
String filePath = "." + pathSeparator + "src" + pathSeparator + "product.txt";
File file = new File(filePath);
or better yet:
// this could reside in a non-instantiable helper class somewhere in your project
public static String getRelativePath(String... pathElements) {
StringBuilder builder = new StringBuilder(".");
for (String pathElement : pathElements) {
builder.append(System.getProperty("path.separator");
builder.append(pathElement);
}
return builder.toString();
}
// this is where your code needs a path
...
new File(getRelativePath("src", "product.txt");
...

Categories

Resources