[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 927
  • Last Modified:

Java load appropriate .jar file at runtime

I am writing an application that uses a third-party .jar file, let's call it x.jar.

On my machine, I can add x.jar to my Netbeans project so I have the classes available while developing and testing (for code completion, parameter checking, etc.).  

At run time, when installed on a client's machine, the client's x.jar file may be different than the one I have, so I don't want to send out my x.jar file, but have my program load their x.jar file (I have a routine that can determine the full path to the client's x.jar file at runtime).

I'm not sure how to go about doing this.  Do I do a custom classloader?  Do I have to set the classpath at runtime?


I'm looking for a solution that
1. allows me to continue to develop in Netbeans (and have the classes show up in the editor) 2. does the runtime loading.
3. whille developing/testing, the program can also load x.jar the same way it would at the client's runtime]
4. Note: when I add x.jar as a dependency in Netbeans, when I do a "build", it copies the x.jar file to the distribution directory, which I don't need.
0
amp834
Asked:
amp834
  • 23
  • 18
  • 13
3 Solutions
 
for_yanCommented:
That's waht people write:

The java.net.URLClassLoader is a "catch-all" sort of class loader that
you can pass URLs to. Just call addURL() whenever you need to add
something to the classpath - but keep in mind it appends to the list,
so you can't use it to "replace" classes on the preset classpath
(passed to the constructor or factory method).

http://www.velocityreviews.com/forums/t129750-changing-the-classpath-at-runtime.html

So if it is so, you can add the folder after you detremine where your x.jar is
0
 
amp834Author Commented:
thanks for_yan, unfortunately your answer leaves most of my questions unanswered.
0
 
for_yanCommented:
Well, I thought this is the most difficult issue - how to add a jar on the fly.
In order to test it, I'd remove that jar from Netbeans dependencies
and emulate the situation how you would do it on the client - discover the location and add it on the fly.
If it works the way they suhggested - it allows you both to test and to deploy
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
amp834Author Commented:
If I'm using class  PackageX.ClassX from x.jar, and I remove x.jar from Netbeans dependencies, Netbeans & java build process will complain that PackageX.ClassX is undefined.  How do I get around that problem?
0
 
for_yanCommented:
You refrence this ckalss inside Class.forName(...)
like they write in that discussin which I quoteted
0
 
for_yanCommented:
Then Netbeans should not complain about that class at building statge
And after you add the jar at runtime this Class.forName("...") should work
0
 
amp834Author Commented:
You are referring to this?   http://www.rgagnon.com/javadetails/java-0409.html

With this proposed solution, I would not be able to get code completion and parameter lists while coding.  Is that correct?  (If so, this is almost like programming in assembly language!)
0
 
for_yanCommented:
no, i'm taking about this:

That's waht people write:

The java.net.URLClassLoader is a "catch-all" sort of class loader that
you can pass URLs to. Just call addURL() whenever you need to add
something to the classpath - but keep in mind it appends to the list,
so you can't use it to "replace" classes on the preset classpath
(passed to the constructor or factory method).

http://www.velocityreviews.com/forums/t129750-changing-the-classpath-at-runtime.html

So if it is so, you can add the folder after you detremine where your x.jar is
0
 
objectsCommented:
Sounds like more of a deployment process

just don't distribute x.jar when you deploy application to client
And have the client deploy use their own jar instead.
0
 
amp834Author Commented:
>just don't distribute x.jar when you deploy application to client
I can probably do this with my installer, but I'd rather do it in Netbeans if possible.  Is there a way to tell Netbeans not to put x.jar into the distribution directory?

>And have the client deploy use their own jar instead.
This is where the classloader comes in.

0
 
for_yanCommented:
You just don't use x.jar in your netbeans deepndencies - try to do it as you
plan to do it on your client side.
After all what't the value of testing and debugging one thing and then to deploy another
So don't add x.jar tio depndencies.
Use your procedure to discover it - add it to CLASSPATH at runtime as they are writing above,
and then use Class.forName(...) to refer to the class(es) you nweed from x.jar.
If you use refer to class through Clas.forName() then you'll not need it at the build time.
Try this way - it is interesting if it works.
0
 
amp834Author Commented:
If I don't use x.jar in the dependencies, I don't get code completion, and would be coding in the dark.
0
 
for_yanCommented:
Well, add to dependencies while you are typing so you'll get code completion but do everything in fact relying on
that Class.forName() and then remove them before building when testing this particular feature
 
0
 
for_yanCommented:
Frankly, my cmain concern is to test this addURL(...)  feature which they writing about.
I never did it myself and it would be an inreteresing option if it really works
Maybe you want to make sure that iit works first using some small and simple
project.
0
 
amp834Author Commented:
I will test it, and will use objects's suggestion to remove the x.jar from the distribution, and will post the results here in a day or two.
0
 
objectsCommented:
> I can probably do this with my installer, but I'd rather do it in Netbeans if possible.  Is there a way to tell Netbeans not to put x.jar into the distribution directory?

just modify the build script

> This is where the classloader comes in.

Include the client jar in the classpath and it will find it
0
 
amp834Author Commented:
objects,

I've never edited the build script in Netbeans, how do you do it, and does it keep the script updated as the project changes, or do I have to manually maintain the script from that point on?


I'm still unclear about the classpath, I package the java program into a .exe file.  In my main.jar, is there anything I can write to update the classpath so it will find x.jar from a place I located at runtime?  

In the app, when the user presses some gui buttons, I do the class-loading of x.jar, and if it fails, I tell the user "this particular functionality (that x.jar provides) is not available", and never try to call any of the functions that depend on x.jar.  

I tried removing x.jar from the distribution, and the .exe will not even start (even though it doesn't use x.jar until much later, after the user presses some buttons).

Or do I need to bite the bullet and make everything class.forName(xxx) instead of PackageX.ClassX.  If so, this would be a very complicated solution!
0
 
objectsCommented:
Do you have any control over where the user puts x.jar?
0
 
amp834Author Commented:
no, it's an optional component, but I can find where it's installed if it is installed.  (If they install a separate package, I can find x.jar's location in the registry and would know where to load it from)

I thought about copying x.jar into my app's runtime directory, it would work (except if they update the optional package, I would have to get the new x.jar).
0
 
for_yanCommented:
If your customers tend to install this x.jar into the finite number of places,
you probably can include all possible paths to the classpath
and in this way cover great majority of your customers.

I stil don't understand,
why you are reluctant to try addURL(..) - if that works it would resolve  your problems.
And it is indeed interesting to see if it is usable in general. Maybe a useful option in many cases.
0
 
amp834Author Commented:
for_yan, maybe I don't understand the addURL() method, can you provide a code snippet.

What I'm doing is this, and my app won't even run if the .jar file is not in my dist directory:
                    String jarFullPath ...;
                    java.net.URL url = new java.net.URL("jar:file:/" + jarFullPath + "!/");
                    // new File("C:/xtras/xercesImpl.jar").toURL();
                    java.net.URLClassLoader ucl = new java.net.URLClassLoader(new java.net.URL[]{url});
                    Object c = Class.forName("PackageX.ClassX", true, ucl).newInstance();
0
 
objectsCommented:
I'd be telling the client to copy the jar(s) to directory xyz if they want the application to use it :)
Would not seem too big an ask really.
0
 
for_yanCommented:
OK, I think that is basically waht you want - so you determine in some way where they
keep this jar and include it at runtime. And then access the clsess you need using Class.forName()
So did you test it - if it works this way?

If it does, then I think you resolved your problem?Am I not right?

As it is convenient for you to debug the rest of the code
with static classptah in Netbeans - then do it. And then before
generating jar for deployment remove x.jar from your dependencies?
Isn't it a reasonable plan?
Please, explain what I'm missing.
0
 
amp834Author Commented:
objects, my app is supposed to be a point-and-click install.  One problem is they can install the optional component months later.

for_yan, can you provide a code snippet for the "Addurl", and a snippet for the Class.forName() so I'm sure I understand what you are proposing.
0
 
objectsCommented:
> objects, my app is supposed to be a point-and-click install.  One problem is they can install the optional component months later.

sure, but if they are installing optional components then its reasonable that they install them to a known location. (your app could even handle installing them)
0
 
objectsCommented:
That way you could also easily install multiple components
0
 
objectsCommented:
Alternatively as your app knows where the jars are in can copy them to a standard location on startup and load them from there. Will make things a whole lot simpler.
0
 
for_yanCommented:

I was raeading that using ClassPathHacker class below you can modify CLASSPATH
at runtime.

http://code.google.com/p/classpath-hacker/source/browse/trunk/src/main/java/dnl/utils/cp/ClassPathHacker.java?r=2

There is also discussion here:
http://www.java-forums.org/advanced-java/32732-adding-classpath-runtime-classpathhacker-java-safe.html
although people have reservations that it is not good to mess with classpath
at runtime it looks that it still works.


So, I understtsand it in a simle way let's think you determined location of their x.jar
and you want to use class xpackage.MyClass.class from this jar:

String pathFile = "...x.jar";

Then you can call:

ClassPathHacker.addFile(new File(pathFile));
and then

MyClass mc = Class.forName("xpackage.MyClass").newInstance();

is supposed to be working

I din't try it myself, and frankly it sounds to good to be true for me either,
but if some people are writing that thsi is how they modify
CLASSPATH dynamically, seemds at least easy to try




import java.io.IOException;
import java.io.File;
import java.net.URLClassLoader;
import java.net.URL;
import java.lang.reflect.Method;

public class ClassPathHacker {

   private static final Class[] parameters = new Class[]{URL.class};

   public static void addFile(String s) throws IOException {
      File f = new File(s);
      addFile(f);
   }//end method

   public static void addFile(File f) throws IOException {
      addURL(f.toURL());
   }//end method


   public static void addURL(URL u) throws IOException {

      URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
      Class sysclass = URLClassLoader.class;

      try {
         Method method = sysclass.getDeclaredMethod("addURL", parameters);
         method.setAccessible(true);
         method.invoke(sysloader, new Object[]{u});
      } catch (Throwable t) {
         t.printStackTrace();
         throw new IOException("Error, could not add URL to system classloader");
      }//end try catch

   }//end method

}//end class

Open in new window

0
 
for_yanCommented:
I agree that copying upfront will be simpler - then you forst run this discoverung where this x.jar is
and then copy it and then start your java application
On the other hand your argument that you don't want to package your own x.jar
and their x.jar may be different is not very good argument either - it is stil
better to rely in oyour application on some particular version of this jar,
then to use their local, even if it happens to be in some cases better?
I'd still prefer to package it with my application.
It would probably be much safer
0
 
amp834Author Commented:
The x.jar is an interface/API the 3rd party app provides, and I use it on my machine to program against it.  In actual deployment, the x.jar on the runtime machine may be different than the one I used (it matches the actual version of the 3rd party app they have installed), though the interfaces/classes are still the same.

In testing, when I install my app on another machine that has a different version of the 3rd party app, my app does not run.  Then, I copy the x.jar from the runtime machine's to the runtime machine's my-app directory, and my app runs.

This is my reason for not packaging my own x.jar.  It's really not needed until that component of my app is requested, and if it's not on the runtime machine to begin with, I can't possibly use it anyway.
0
 
amp834Author Commented:
objects,
> but if they are installing optional components then its reasonable that they install them to a known location. (your app could even handle installing them)

I can find the location using the registry, so it is "standard"

>Alternatively as your app knows where the jars are in can copy them to a standard location on startup and load them from there. Will make things a whole lot simpler.

For some reason, my app won't even run if x.jar is not there.  I am looking into this (and was hoping you or for_yan would have some insight into it).  
If I can get the app to run to begin with, then I can implement for_yan's classpath suggestion.
0
 
objectsCommented:
> I can find the location using the registry, so it is "standard"

If you copy it you avoid the mess you will get into with classloaders.

> The x.jar is an interface/API the 3rd party app provides, and I use it on my machine to program against it.

You should be developing to just an interface, that would avoid all these dramas.
If you're using classes from it then you're going to need to deploy it, then copy the client jar to a location higher in the classloader order than your x.jar


0
 
for_yanCommented:
Your application would not even run without x.jar probably because you are using classes from x.jar
directly, not through Class.forName(...)
So it cannot find them and does not start .

I'm not sure I understand correctly, still it seems risky to me to have one jar which you use when
developing applicationa nd then use their jars for execution.
There is potential for one of them being changed so that there will be problems,
and such incompatibility issues difficult to track down after some time, when you are a little bit remote in time
from the development, and they would suddenly change their x.jar
0
 
amp834Author Commented:
>You should be developing to just an interface
This may be fruitful...Would I in effect be defining proxies for everything I want to use in x.jar?

I may have to do that anyway since (more drama) x.jar only runs on 32-bit jvms, and my app needs to run on both 64 and 32 bit jvms, so I have to write an rmi-callable version that talks to x.jar and runs in 32-bit mode.

All of this makes debugging the original app very difficult--it's so much easier to debug using the original classes.

0
 
objectsCommented:
> This may be fruitful...Would I in effect be defining proxies for everything I want to use in x.jar?


you can think of it like that yes. You application should only use interface from x, and not use *any* concrete classes.
0
 
objectsCommented:
consider the folllwoing code. In your application your code should only ever use X and never reference Y

Similiar to how JDBC is used. You never specify the actual database implementation you use in your code

public interface X {
   ...
}

public class Y implements X {
   ...
}

Open in new window

0
 
amp834Author Commented:
The x.jar libraries are already defined and written, obviously I can't go an tell the third part to redefine their functions.  So given that they have defined:

public class Z {
   z1(int a);
   z2(String b, int c);
   ....
}

are you proposing that I write:
public interface IX {
   z1(int a);
   z2(String b, int c);
   ....
}

code against IX

and write what to glue interface IX the Z class?

0
 
objectsCommented:
yes something like that
0
 
amp834Author Commented:
The last part was a question, what would you write for the glue interface?  (I can provide my thoughts, but want to hear yours first!)
0
 
objectsCommented:
not sure what you mean by 'glue'
0
 
amp834Author Commented:
I provided code for classes Z and IX, there is one obvious class missing.  The code for that.  (Keep in mind I cannot change class Z, that's why this "glue" class is needed).

If you don't understand what I'm asking, ignore everything I'm saying and give me code for how to implement the solution, knowing that Z cannot be changed.
0
 
objectsCommented:
If you're code is already using Z then there is nothing you can do. You need to deploy x.jar or your application will not run.
0
 
amp834Author Commented:
The discussion was that the main app uses IX only, and the Glue class would load Z at runtime (using ClassFor, etc.) and provide a "supplier" for the IX methods.

My guess for the Glue class is:
public class Glue Implements IX {
   private Z pz;
   z1(int a) {
       pz.z1(a);
      }
   z2(String b, int c) {
      return pz.z2(b,c);
      }
   static {
      some code for loading Z and setting pz=an instance, and getting "pointers" to various methods and subclasses
      }
   ....
}


// this class cannot be modified
public class Z {
   z1(int a);
   z2(String b, int c);
   ....
}

public interface IX {
   z1(int a);
   z2(String b, int c);
   ....
}
0
 
objectsCommented:
there wouldn't really be a glue class.
you would use a factory to create instance

public static IX getX() {
 ....
}
0
 
objectsCommented:
Have a look at how jdbc and the DriverManager work
0
 
amp834Author Commented:
The probably have code like

class Z implements IX

then have a factory to create a particular implementation of IX.

The class Z that I have to use does not say this, so I can't change it to do so.
0
 
amp834Author Commented:
objects, one last call for clarification (of the last post I made)
0
 
objectsCommented:
I already covered that above. If you can't change that you're already using the class in the your jar then *have* to include it when you deploy. Your app will not run without it
0
 
amp834Author Commented:
Unwinding the long conversation, let me summarize.

The question was:  

If you can't change Z, you dynamically load it, and provide an in-between class that calls the dynamically loaded functions.  I'm looking for code to do Glue below, then I would only call Glue classes.

The reason I ask is because it looks like a messy job, and I want to make sure you don't have a simpler method before I do it this way.

for_yan, feel free to comment here as well!


// this class cannot be modified
public class Z {
   z1(int a);
   z2(String b, int c);
   ....
}

public class Glue {
   void loadClasses() {
       dymanically load Z and find the functions we will use, store their info in member variables f_z1, f_z2, etc.
       }
   z1(int a) {
       return introspection_call(f_z1, a);
       }
   z2(String b, int c) {
      return introspection_call(f_z2, b, c);
      }
   ....
}
0
 
objectsCommented:
class Glue {
   private Class z

   public Glue() {
      z = Class.forName("Z");
   }

   public z1(int a) {
      // use reflection to call method
      // http://helpdesk.objects.com.au/java/how-to-call-a-method-using-reflection
   }
}

you'll get a more flexible solution using interfaces as I outlined above
0
 
amp834Author Commented:
Thanks.  I didn't understand the "interfaces" method you explained, that's why I asked for sample code/pseudo-code.  (Can it be done given that Z cannot be changed?)  Can you give a quick sample?
0
 
objectsCommented:

public XFactory {
   public X getX() {
       ...
}

public interface X {
   ...
}

public class Z implements X {
   ...
}
0
 
amp834Author Commented:
Remember, I don't have access to Z's source code.  Is it still possible to do this?

To refresh your memory, see the post in this thread, 05/20/11 05:19 PM, ID: 35805666
0
 
amp834Author Commented:
I am finally getting around to closing some of the questions I asked!

Thanks for your help and comments.  Not the answer I was hoping for, but sometimes there aren't any!
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 23
  • 18
  • 13
Tackle projects and never again get stuck behind a technical roadblock.
Join Now