Wrong name error when class loaded from random dir - java

I want to make a instance of .class file located into random directory. I tried this
private final String CLASS_FOLDER =
"C:\\Users\\test\\Desktop\\fix\\core\\src\\test\\org\\poc\\";
private Class getClassFromFile(String fullClassName) throws Exception {
URLClassLoader loader = new URLClassLoader(new URL[] {
new URL("file://" + CLASS_FOLDER)
});
return loader.loadClass("Order");
}
When I run the code I get error:
java.lang.NoClassDefFoundError: Order (wrong name: com/solutions/backend/toms/actions/Order)
Looks like a security check for correct package name. Is there nay way to skip this check because I need to load .class files into random directories?

Java classes need to be in a directory hierarchy that matches their package. You cannot drop a Java .class in a "random" directory, that's simply not how classloading works.

Related

Access an external jar file in Java

I'm trying to access a specific class from an external jar library.
I have configured the path in my properties file
test-library = file:/Users/test.user/test/library.jar
test-library2 = file:/Users/test.user/test/library2.jar
Then I try to access it from the code but I get ClassNotFoundErrorException or IllegalArgumentException: URI is not absolute when I modify the path as follows
test-library = /Users/test.user/test/library.jar
test-library2 = /Users/test.user/test/library2.jar
When I locate the jar files in the resources folder and specify the path as follows it works fine.
test-library = classpath:library/library.jar
test-library2 = classpath:library/library2.jar
However I need to access the jar files because when the code is deployed we cannot have the jar files in the resource folder but in an external location.
What is the correct way to specify the path of the external jar files so java could find the classes I need?
This is the code where I'm invoking the external libraries and as I said before it works fine if the jar files are in the resource folder but I need to access the jar files from an external location in the machine.
#Value("${test-library}")
String library;
#Value("${test-library2}")
String library2;
public String testMethod(String text1, String text2) {
try {
log.info("[testMethod] start process");
URLClassLoader urlClassLoader =
new URLClassLoader(
new URL[] {URI.create(library).toURL(), URI.create(library2).toURL()},
this.getClass().getClassLoader());
Class<?> classInstance = Class.forName("pro.test.service.TestService", true, urlClassLoader);
Method getInstanceMethod = classInstance.getMethod("getInstance", String.class, String.class);
Object instance = getInstanceMethod.invoke(null, data1, data2);
Method libraryMethod = classInstance.getMethod("libraryMethod", String.class, String.class);
log.info("[testMethod] end process");
return (String) libraryMethod.invoke(instance, text1, text2);
} catch (Exception e) {
log.error("Error testing library method: ", e);
throw e;
}
}```
Reading the documentation of URLClassLoader we find:
This class loader is used to load classes and resources from a search path of URLs referring to both JAR files and directories. Any jar: scheme URL (see JarURLConnection) is assumed to refer to a JAR file.
and following the above link to JarURLConnection:
The syntax of a JAR URL is:
jar:<url>!/{entry}
. . . If the entry name is omitted, the URL refers to the whole JAR file: jar:http://www.example.com/bar/baz.jar!/ ยน
Putting these together, we get, for the first JAR posted in question:
jar:file:/Users/test.user/test/library.jar!/
1 address changed from foo to example since the first is not accepted by StackOverflow (apparently even not inside a quoted code)

Cannot access Resource files in JavaFX 16 when module-info.java file exists

I just started using Java 16 and can't seem to figure out why I am unable to access resource files. I went through some troubleshooting to narrow down at least where I seem to be having a problem.
I'm using IntelliJ IDEA 2021.2 Build #IU-212.4746.92
I created a new project and I chose JavaFX with OpenJDK 16.
It then creates the project with three main files, an Application class, a Controller class, and a FXML file. Once I create the project, I go into the POM file and I chose version 16 of javaFX-controls and javafx-fxml and I tell it to get the latest version of the other libraries it adds automatically into the POM file.
I also copy two folders from a different project into the resources folder - all copy and pasting is done within IntelliJ.
When I run the application that it put there (called HellpApplication), it works fine. And that application uses class.getResource to grab the fxml file and again ... it works just fine. However, when I try to run this class:
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
public class FileResourcesUtils {
public static void main(String[] args) throws URISyntaxException {
FileResourcesUtils app = new FileResourcesUtils();
String fileName = "StyleSheets/AnchorPane.css";
System.out.println("getResourceAsStream : " + fileName);
InputStream is = app.getFileFromResourceAsStream(fileName);
printInputStream(is);
System.out.println("\ngetResource : " + fileName);
File file = app.getFileFromResource(fileName);
printFile(file);
}
private InputStream getFileFromResourceAsStream(String fileName) {
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(fileName);
if (inputStream == null) {
throw new IllegalArgumentException("file not found! " + fileName);
}
else return inputStream;
}
private File getFileFromResource(String fileName) throws URISyntaxException{
ClassLoader classLoader = getClass().getClassLoader();
URL resource = classLoader.getResource(fileName);
if (resource == null) {
throw new IllegalArgumentException("file not found! " + fileName);
}
else return new File(resource.toURI());}
private static void printInputStream(InputStream is) {
try (InputStreamReader streamReader =
new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)
)
{
String line;
while ((line = reader.readLine()) != null) System.out.println(line);
}
catch (IOException e) {e.printStackTrace();}
}
private static void printFile(File file) {
List<String> lines;
try {
lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
lines.forEach(System.out::println);
}
catch (IOException e) {e.printStackTrace();}
}
}
It throws this error:
getResourceAsStream : StyleSheets/AnchorPane.css
Exception in thread "main" java.lang.IllegalArgumentException: file not found! StyleSheets/AnchorPane.css
at com.simtechdata.test/com.simtechdata.test.FileResourcesUtils.getFileFromResourceAsStream(FileResourcesUtils.java:27)
at com.simtechdata.test/com.simtechdata.test.FileResourcesUtils.main(FileResourcesUtils.java:16)
Then, THE ONLY THING I HAVE TO DO ... is DELETE module-info.java, then that class runs perfectly! HOWEVER, without module-info.java there, I cannot run any FX code...
Here is module-info.java:
module com.simtechdata.test {
requires javafx.controls;
requires javafx.fxml;
opens com.simtechdata.test to javafx.fxml;
exports com.simtechdata.test;
}
Why did I use this class? Because I'm having the exact same problem trying to access resource files in my JavaFX application and this class was the easiest way for me to demonstrate the problem and show that it is specifically connected to the module-info file. When I run my own JavaFX code, it works just fine, until I try to access a resource file like a style sheet to throw onto a control.
What I would like to know is ... what sort of magic trick do I need to do in order to be able to access resource files from my JavaFX 16 application? What am I missing?
I'vr tried all of these different ways of getting to the resource file, but each one gives the same error:
String CSS_ANCHOR_PANE = this.getClass().getResource("StyleSheets/AnchorPane.css").toExternalForm();
String CSS_ANCHOR_PANE = ClassName.getClass().getResource("StyleSheets/AnchorPane.css").toExternalForm();
ClassLoader resource = ClassLoader.getSystemClassLoader();
String CSS_ANCHOR_PANE = resource.getResource("StyleSheets/AnchorPane.css").toExternalForm();
Class<?> resource = this.getClass();
String CSS_ANCHOR_PANE = resource.getResource("StyleSheets/AnchorPane.css").toExternalForm();
And here is a screenshot of the resource folder tree:
Any ideas?
This question is really answered in numerous other places around the StackOverflow site, but I'll pull together various solutions here.
You basically have three options:
Use the getResource() (or getResourceAsStream(), though the former is slightly preferable) method defined in Class, instead of in ClassLoader with the correct path. This avoids any issues with modularity.
Make the project non-modular (i.e. don't define a module-info.java file) and specify the modules to be added as runtime parameters to the JVM. This allows you to use either ClassLoader's getResource() or Class's getResource() method.
Create a modular project and use ClassLoader's getResouce() method, and open the package containing the resource unconditionally.
Solution 1 is detailed here. Since the style sheet you are loading is in the StyleSheets package1, and the class you are loading it from appears (from the stack trace) to be in an unrelated package com.simtechdata.test, you should provide an absolute path:
String CSS_ANCHOR_PANE = this.getClass().getResource("/StyleSheets/AnchorPane.css").toExternalForm();
For solution 2, use
String CSS_ANCHOR_PANE = this.getClass().getClassLoader().getResource("StyleSheets/AnchorPane.css").toExternalForm();
Remove the module-info.java file, and run the application with the JVM arguments
--module-path="/path/to/javafx/modules" --add-modules="javafx.controls,javafx.fxml"
(Replace /path/to/javafx/modules with the actual file system path to the JavaFX modules. The javafx.fxml module is only required in the add-modules parameter if you are using FXML.)
For solution 3, use
String CSS_ANCHOR_PANE = this.getClass().getClassLoader().getResource("StyleSheets/AnchorPane.css").toExternalForm();
Since your resource is in the StyleSheets package1 you need to open that package unconditionally. Add the line
opens StyleSheets ;
to your module-info.java file. See https://stackoverflow.com/a/48790851/2189127
(1) It's very strongly advised to follow standard naming conventions; I'm not even sure if solution 3 will work with a non-standard package name (package names should be all lower-case).
You write a lot about JavaFX but instead you should concentrate on the real problem. As you have shown yourself via your test program this problem is a module system problem and not a JavaFX problem.
You can get rid of this problem by just not using the module system. Just wrap your main method into something like this:
class MyAppLauncher {public static void main(String[] args) {MyApp.main(args);}}
The assumption is that MyApp is your main class which extends Application. The use MyAppLauncher to start your JavaFX program and remove all module system related options.

Loading .class files via URLClassLoader - NoClassDefFoundError

I'm aware this question has been asked multiple times (such as here), but none of the answers appear to work for me.
This is a homework assignment, I'm supposed to "hack" several class files via the reflection API, but I can't even get them to load.
There are three .class files (Inscription.class, Decoder.class, Safe.class) I put in D:\class\. Then I try to load them via an URLClassLoader:
public void Load() throws MalformedURLException {
ClassLoader loader = this.getClass().getClassLoader();
File classFolder = new File("D://class//");
// classFolder.exists() returns true at this point.
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{classFolder.toURI().toURL()},loader);
// urlClassLoader.classes is empty at this point, which is suspicous
urlClassLoader.loadClass("Safe");
// throws NoClassDefFoundError (wrong name: ea_6_1/Safe)
// Interestingly, it seems to find a package name (ea_6_1)
urlClassLoader.loadClass("ea_6_1.Safe");
// throws ClassNotFoundException
}
I also tried to load the files one by one, but apparently this shouldn't work, since URLClassLoader only accepts directories or JAR files:
URL inscription = loader.getResource("Inscription.class");
URL safe = loader.getResource("Safe.class");
URL decoder = loader.getResource("Decoder.class");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{inscription, safe, decoder});
// Exact same behavior as above.
My classpath configuration looks like this:
Is this a configuration issue or am I using the URLClassLoader wrong? Is there maybe another way of loading class files?
It appears someone has moved .class files around, without preserving the required directory structure.
A Java class declared with package ea_6_1; must reside in a directory named ea_6_1 (in every Java implementation I know of, at least).

Path of the folder inside the jar file

I have jar file langdetect.jar.
It has a hierarchy shown in image
There is a class LanguageDetection at com/langdetect package.
I need to access the path of the profiles.sm folder from above class while executing the jar file.
Thanks in advance.
Jars are nothing else than Zip files and Java provides support for handling those.
Java 6 (and earlier)
You can open the jar file as a ZipFile and iterate over the entries of it. Each entry has a full path name inside the file, there is no such thing as relative path names. Though you have to take care, that all entries - although being absolute in the zip file - do not start with a '/', if you need this, you have to add it. The following snippet will get you the path of a class file. The className has to end with .class, i.e. LanguageDetection.class
String getPath(String jar, String className) throws IOException {
final ZipFile zf = new ZipFile(jar);
try {
for (ZipEntry ze : Collections.list(zf.entries())) {
final String path = ze.getName();
if (path.endsWith(className)) {
final StringBuilder buf = new StringBuilder(path);
buf.delete(path.lastIndexOf('/'), path.length()); //removes the name of the class to get the path only
if (!path.startsWith("/")) { //you may omit this part if leading / is not required
buf.insert(0, '/');
}
return buf.toString();
}
}
} finally {
zf.close();
}
return null;
}
Java 7/8
You may open the JAR file using the Java7 FileSystem support for JAR files. This allows you to operate on the jar file as if it would be normal FileSystem. So you could walk the fileTree until you have found your file and the get the Path from it. The following example uses Java8 Streams and Lambdas, a version for Java7 could be derived from this but would be a bit larger.
Path jarFile = ...;
Map<String, String> env = new HashMap<String, String>() {{
put("create", "false");
}};
try(FileSystem zipFs = newFileSystem(URI.create("jar:" + jarFileFile.toUri()), env)) {
Optional<Path> path = Files.walk(zipFs.getPath("/"))
.filter(p -> p.getFileName().toString().startsWith("LanguageDetection"))
.map(Path::getParent)
.findFirst();
path.ifPresent(System.out::println);
}
Your particular Problem
The above solutions are for finding the path inside a Jar or Zip, but may possibly not be the solution to your problem.
Im not sure, whether I understand your problem correctly. As far as I see it, you'd like to have access to the path inside the classfolder for any purpose. The problem with that is, that the Class/Resource lookup mechanism doesn't apply to folders, only files. The concept that is close is a package, but that is always bound to a class.
So you always need a concrete file to be accessed via getResource() method. For example MyClass.class.getResource(/path/to/resource.txt).
If the resources are located in a profiles.sm folder relative to a class and its package, i.e. in /com/languagedetect/profile.sm/ you could build the path from the reference class, for example the class LanguageDetection in that package and derive the absolute path from this to the profiles.sm path:
String basePath = "/" + LanguageDetection.class.getPackage().getName().replaceAll("\\.", "/") + "/profiles.sm/";
URL resource = LanguageDetection.class.getResource(basePath + "myResource.txt");
If there is only one profiles.sm in the root of the jar, simply go for
String basePath = "/profiles.sm/";
URL resource = LanguageDetection.class.getResource(basePath + "myResource.txt");
If you have multiple jars with a resource in /profiles.sm, you could gain access to all of those via the classloader and then extract the Jar file from the URL of the class
for(URL u : Collections.list(LanguageDetection.class.getClassLoader().getResources("/profiles.sm/yourResource"))){
System.out.println(u);
}
In any case it's not possible without accessing the zip/jar file to browse the contents of this path or folder because Java does not support browsing for classes or resources inside a package/folder in classpath. You may use the Reflections lib for that or extend the ClassLoader example above by additionally reading the content of the detected jars using the zip example from above.

java code to instantiate another java file

I want to write a program to dynamically invoke a method inside another Java class (uncompiled) whose file name with location is given. For this I've used the following code but it wasn't working.
//folder location of my java file to be loaded
String url = "C:/Temp/testcases/test.java";
//name of the java file to be loaded
String classname = "test.java";
this.class.classLoader.rootLoader.addURL(new URL(url+str));
Class.forName(str).newInstance();
The above instance is unable to invoke the method inside the java file that I want to load dynamically. What is the error in it?
The class loader is only able to load compiled classes. It's not able to open Java source files, compile them on the fly, and load their class.
Moreover, a class name is not the same as a file name.
I agree with your answer. The error in the above code is with the new URL(C://...). Had I mentioned the package name wrt path correctly, it should have worked. Anyways I was dealing with groovy files so I found this code more efficient than the normal class.forname that I've mentioned above.
def sourceString = (new File(C:/xyz.groovy)).getText()
ClassLoader parent = getClass().getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);
Class groovyClass = loader.parseClass(sourceString);
template = groovyClass.newInstance()

Categories