How do I load resources when using a classloader? - java

I'm using JarFile and JarURLConnection to load files out of a jar file. I'm then taking the classes, and loading them via BCEL (ByteCode Engineering Library, apache library). I cant just directly use a class loader because im modifying some classes slightly with the BCEL. I need to load the classes by their bytes into my bcel loader. However, one of the classes I'm loading references a resource. This resource is inside of the jar, so I can get the file (When iterating over the entries in the JarFile, I ignore the regular files, and take the class files for loading later). But just having the file won't do me any good, as the class loads it as a resource. Is there any way I can take that resource from the jar (well I can take it and load it into a byte[], the next part is the issue) and dynamically add it as a resource for my program, so that the classes that I load wont be missing their resources?
Got a lot of stuff here, if anythings confusing, ask in comments, I might've said something wrong, or missed something altogether :) Thanks
I'll show a little of my class loader here (extends ClassLoader):
#Override
public URL getResource(String name) {
System.out.println("LOADING RESOURCE: " + name);
try {
return new URL(null, name, new Handler(files));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Now, it is printing out "LOADING RESOURCE: filename", but its then giving me a MalformedURLException (I have no protocol atm, just a file path, that's not a true valid path, but it's just an attempt to give it to my Handler class below).
class Handler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
#Override
public void connect() throws IOException {
}
#Override
public InputStream getInputStream() throws IOException {
System.out.println("IS: " + url);
return /*method to get input steam*/;
}
};
}
}
The /*method to get input steam*/ is set in my real code, but that's not relevant here. So any further ideas with this?

Related

Unable to make getResourceAsStream() to load the .properties resource - using Netbeans IDE

First of, my sincere apologies for bringing up an oft repeated question in this forum; but I cannot figure out my mistake(s).
I have two .properties files that I am trying to load unsuccessfully. Here's the folder structure I have - unless there is a compelling reason otherwise or it is contrary to the best practice, I like to keep this structure:
As you notice my DAO code is under zencs.dbutils package and my .properties files are respectively under zencs.resources.properties.db* packages.
The reason I do it this way because eventually this will connect to and manage multiple data sources - my DAO code will evolve to handle them dynamically (not yet so). I want to set up all data source properties in one place
My Project properties are set as follows:
Now in my DAO class I have a method initProperties(), called by getConnection(), that is trying to reference these properties files through getResourceAsStream(). Please see below code that I tried:
public class DAO {
Connection conn = null;
public Properties properties = new Properties();
public Properties dbConnect = new Properties();
private void initProperties() {
InputStream inputDBdrivers = getClass().getResourceAsStream("snowflakeConnect.properties");
if (inputDBdrivers != null) {
try{
dbConnect.load(inputDBdrivers);
inputDBdrivers.close();
} catch(IOException ioex) {
System.err.println(ioex.getStackTrace().toString());
}
} else {
System.out.println("snowflakeConnect.properties file not found! Terminating Application normally...");
System.exit(0);
}
InputStream inputDBprops = getClass().getResourceAsStream("snowflake.properties");
if (inputDBprops != null) {
try{
properties.load(inputDBprops);
inputDBprops.close();
} catch(IOException ioex) {
System.err.println(ioex.getStackTrace().toString());
}
} else {
System.out.println("snowflake.properties file not found! Terminating Application normally...");
System.exit(0);
}
}
Connection getConnection() throws SQLException {
// build connection properties
initProperties();
try {
Class.forName(dbConnect.getProperty("driver"));
} catch (ClassNotFoundException cnfex) {
System.err.println("ERROR: getConnection() :: Snowflake Class not found: " + cnfex.getMessage());
}
return DriverManager.getConnection(dbConnect.getProperty("connectStr"), properties);
}
public DAO() {
try {
this.conn = getConnection();
} catch (SQLException sqlex) {
Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, sqlex);
}
}
}
When I am executing it, the error says "snowflakeConnect.properties file not found! Terminating Application normally..."
My evaluation is that the code in the above form resolving the files to be in zencs/dbutils/ and the ClassLoader cannot find them there obviously.
I tried full absolute path (out of desperation though it expects relative); I tried relative path with "../resources/properties/{dbdrivers | dbutils}/filename.properties" with no success. With the relative path it is resolving to "zencs/dbutils/../resources/properties/dbdrivers/snowflakeConnect.properties" for ClassLoader...
Am I NOT setting the resources folder and everything underneath it correctly?
Obviously my comprehension of how it should resolve is flawed. Can you please help with what I might have not understood and how should I go about this issue?
Thanks a bunch!
You could try to use getResourceAsStream() including your package name like this:
InputStream inputDBdrivers = getClass().getResourceAsStream("/zencs/resources/properties/dbdrivers/snowflakeConnect.properties");
InputStream inputDBprops = getClass().getResourceAsStream("/zencs/resources/properties/dbutils/snowflake.properties");
The leading slash is usually the key part here. It could help to remove that as well but you said you've tried that already so I guess that's not what you're looking for.

Reading images from external JAR

I have a simple plugin system, that loads external JAR plugins into main application. I am using Mountainblade Modular to do so. Not sure how they do it "under the hood" but probably it's something standard.
This works fine, I instantiate classes from external jar and it all works. Except that some plugins come with icons/images. I am a bit unsure on how do I load/refer to images from that external JAR (with code inside that external JAR, as it is ran in context of the main JAR, kind of)
How should I approach this?
This issue is not as straightforward as it seems to be.
When you load classes from external jar, they are "loaded" into JVM. By "loading" into JVM I mean that JVM is responsible for their storage in memory. Usually it is done like this:
ClassLoader myClassLoader = new MyClassLoader(jarFileContent);
Class myExtClass = myClassLoader.loadClass(myClassName);
Resources from classpath jars can be accessed easily with
InputStream resourceStream = myClass.getResourceAsStream("/myFile.txt");
You can do that, because these jars are in classpath, I mean their location is known. These files are not stored in memory. When resource is accessed, JVM can search for it in classpath jars (for example on file system).
But for external jars it is completely different: jar comes from nowhere, is once processed and forgotten. JVM does not load resources from it in memory. In order to access these files, you have to manually organize their storage. I've done this once so I can share the code. It will help you to understand the basic idea (but probably won't help you with your specific library).
// Method from custom UrlClassLoader class.
// jarContent here is byte array of loaded jar file.
// important notes:
// resources can be accesed only with this custom class loader
// resource content is provided with the help of custom URLStreamHandler
#Override
protected URL findResource(String name) {
JarInputStream jarInputStream;
try {
jarInputStream = new JarInputStream(new ByteArrayInputStream(jarContent));
JarEntry jarEntry;
while (true) {
jarEntry = jarInputStream.getNextJarEntry();
if (jarEntry == null) {
break;
}
if (name.equals(jarEntry.getName())) {
final byte[] bytes = IOUtils.toByteArray(jarInputStream);
return new URL(null, "in-memory-bytes", new URLStreamHandler() {
#Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
#Override
public void connect() throws IOException {
// nothing to do here
}
#Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(bytes);
}
};
}
});
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

JarClassLoader - loading jars dynamically - how to?

I have seen many class loader questions, but still was not able to figure why, the error here.
I am writing a program which uses 2 versions of jars. One is needed to get content from older storage, and another to store content in new storage.
Since, I need either of the jar to be loaded at a time, I used JarClassLoader to create a proxy for adding one jar and loading its classes. But I face ClassNotFoundException.
public class HbaseMigrator implements Runnable {
public void run() {
JarClassLoader jcl = new JarClassLoader();
jcl.add("hadoop-0.13.0-core-modified-1.jar");
Object obj1 = JclObjectFactory.getInstance().create(jcl, "UserMigThreadImpl", toProcessQueue,threadName, latch,DBUtil,lock);
MigThread mig = JclUtils.cast(obj1, MigThread.class, jcl);
Thread.currentThread().setContextClassLoader(jcl);
try {
Method method = MigThread.class.getMethod("callthis", new Class[]{});
method.invoke(mig, new Object[]{});
// mig.callthis();
} catch( Exception e) {
e.printStackTrace();
} catch(Error er) {
er.printStackTrace();
}
}
}
Method called is:
public void callthis() {
DFSUtil = new DFSAccessAPIImpl();
.........
}
This class instantiation internally uses hadoop modified jar, which is not picked up from my classloader and it throws ClassNotFoundException.
What is that I am doing wrong ?
JarClassLoader used here is jcloader :
org.xeustechnologies.jcl.JarClassLoader
I experienced this problem with loading plugins to my application, so I decided to try to load all .class files from all jars in path. Maybe this code snipped from my app will help you.
https://bitbucket.org/rsohlich/plagdetector/src/432b52f252ff7647221b7e91b08731bd9cbe2a70/PlagDetectorSpring/src/main/java/cz/sohlich/app/service/impl/PluginHolderImpl.java

Add txt files to a runnable JAR file

I'm trying to make a runnable jar file and I'm having problems with my .txt files.
My program also have images, but fortunately I've figured out how to manage them. I'm using something like this with them and it works just fine both Eclipse and the jar:
logoLabel.setIcon(new ImageIcon(getClass().getResource("/logo.png")));
My problem is when I've something like this in one of my classes:
try {
employeeList = (TreeSet<Employee>) ListManager.readFile("list/employeeList.txt");
} catch (ClassNotFoundException i) {
i.printStackTrace();
} catch (IOException i) {
i.printStackTrace();
}
And this in the class ListManager that I use to read my lists serialized in the .txt files:
public static Object readFile(String file) throws IOException, ClassNotFoundException {
ObjectInputStream is = new ObjectInputStream(new FileInputStream(file));
Object o = is.readObject();
is.close();
return o;
}
I also have a similar method to write in the files.
I've tried several combinations that I've found here:
How to include text files with Executable Jar
Creating Runnable Jar with external files included
Including a text file inside a jar file and reading it
I've also tried with slash, without slash, using openStream, not using openStream... But or I get a NullPointerException or it doesn't compile at all...
Maybe is something silly or maybe is a concept error that I've of how URL class works, I'm new to programming...
Thank you very much in advance for your advice!
EDIT:
It's me again... The answer Raniz gave was just what I needed and it worked perfect, but now my problem is with the method that I use to write in the files...
public static void writeFile(Object o, String file) throws IOException {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file));
os.writeObject(o);
os.close();
}
try {
ListManager.writeFile(employeeList.getEmployeeList(), "lists/employeeList.txt");
} catch (IOException i) {
i.printStackTrace();
}
Could you help me please? I don't know what I should use to replace FileOutputStream, because I think there is the problem again, am I right?
Thank you very much!
The problem is that you're trying to access a file inside of a JAR archive as a file in the file system (because that's what FileInputStream is for) and that won't work.
You can convert readFile to use an URL instead and let URL handle opening the stream for you:
public static Object readFile(URL url) throws IOException, ClassNotFoundException {
ObjectInputStream is = new ObjectInputStream(url.openStream());
Object o = is.readObject();
is.close();
return o;
}
You should also put your code in a try-statement since it currently doesn't close the streams if an IOException occurs:
public static Object readFile(URL url) throws IOException, ClassNotFoundException {
try(ObjectInputStream is = new ObjectInputStream(url.openStream())) {
Object o = is.readObject();
return o;
}
}
try {
employeeList = (TreeSet<Employee>) ListManager.readFile(getClass().getResource("/list/employeeList.txt"));
} catch (ClassNotFoundException i) {
i.printStackTrace();
} catch (IOException i) {
i.printStackTrace();
}
I also have a similar method to write in the files.
That won't work if the files are inside the JAR so you should probably consider having your files outside your JAR.
Yes, if you want to read resources from inside a jar file, you shouldn't use FileInputStream. Perhaps you should add a readResource method:
public static Object readResource(Class clazz, String resource)
throws IOException, ClassNotFoundException {
try (ObjectInputStream is =
new ObjectInputStream(clazz.getResourceAsStream(resource))) {
return is.readObject();
}
}
(I'd also suggest updating your readFile method to use a try-with-resources block - currently if there's an exception you won't close the stream...)
Note that when you say "I also have a similar method to write in the files" - you won't be able to easily write to a resource in the jar file.

Detect whether Java class is loadable

I have two programs: one CLI program, and one GUI. The GUI is a frontend for the CLI, but also a GUI for another program as well.
I am importing the CLI's classes and extending them in the GUI to add GUI elements to the classes, and all is great.
But now I want to split the CLI that I currently have embedded in the GUI (as an included JAR). The JAR is in a fixed location (/opt/program/prog.jar), and the application will only be used on Linux, so I realize that this breaks traditional Java thought.
I've edited the ClassPath in the Manifest file to reflect this change, and it works fine. However, when I remove the file, the GUI fails to load, citing not being able to load the class.
Is there a way to try to load a class and if it does not work, then do something else? In essence, I'm trying to catch the ClassNotFound exception, but have not had any luck yet.
One common way to check for class existence is to just do a Class.forName("my.Class"). You can wrap that with a try/catch that catches ClassNotFoundException and decide what to do. If you want, you could do that in a wrapper class that has a main(). You could try to load the class and if it succeeds, then call main() on the loaded class and if not, do something else.
public static void main(String arg[]) {
try {
Class.forName("my.OtherMain");
// worked, call it
OtherMain.main();
} catch(ClassNotFoundException e) {
// fallback to some other behavior
doOtherThing();
}
}
Is there a way to try to load a class and if it does not work, then do something else?
Assuming you had a class file in C:\ called Foo.class
public static void main(String[] args) {
File f = new File("c:\\");
if (f.exists()) {
URLClassLoader CLoader;
try {
CLoader = new URLClassLoader(new URL[]{f.toURL()});
Class loadedClass = CLoader.loadClass("Foo");
} catch (ClassNotFoundException ex) {
} catch (MalformedURLException ex) {
}
} else {
//do something else...
}
}

Categories

Resources