17

I am refering to the question on changing the classpath programmatically.

I read and found out that there is some function under System class as getproperties where we can retrieve the properties and then also can set it using setProperties().

The answers however I got was that It Wont work. I have not tried this myself, however, i am taking the call.

Just to clarify, then why these setProperty() and getProperty() methods are there if they cannot alter it at run time. Or is this specific to the classpath property only ?

I will appreciate if someone can present a scenario where they are really helpful?

Community
  • 1
  • 1
Nrj
  • 6,723
  • 7
  • 46
  • 58
  • added information about classpath property – VonC Nov 07 '08 at 13:11
  • Note: Beanshell (as well as Ant and Groovy) is able to dynamically load .jars but the problem is that for certain things, trying to load them in beanshells custom classloader isn't appreciated. so, it doesn't always work, but it does most of the time. – djangofan Oct 30 '11 at 00:48

5 Answers5

14

Modify Classpath

Even though you cannot set the classpath using the system properties (because the JVM reads system properties once: at startup), you can still change the classpath by forcibly invoking the addURL method of the classloader. Note that the solution below does not take into consideration the current thread. Consequently, it might not be accurate in all situations.

Example Solution

The original source on Sun's website for the following code has been removed:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;                   

import java.io.File;
import java.io.IOException;

import java.net.URL;
import java.net.URLClassLoader;

/**
 * Allows programs to modify the classpath during runtime.              
 */                                                                     
public class ClassPathUpdater {                                         
  /** Used to find the method signature. */                             
  private static final Class[] PARAMETERS = new Class[]{ URL.class };   

  /** Class containing the private addURL method. */
  private static final Class<?> CLASS_LOADER = URLClassLoader.class;

  /**
   * Adds a new path to the classloader. If the given string points to a file,
   * then that file's parent file (i.e., directory) is used as the
   * directory to add to the classpath. If the given string represents a
   * directory, then the directory is directly added to the classpath.
   *
   * @param s The directory to add to the classpath (or a file, which
   * will relegate to its directory).
   */
  public static void add( String s )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    add( new File( s ) );
  }

  /**
   * Adds a new path to the classloader. If the given file object is
   * a file, then its parent file (i.e., directory) is used as the directory
   * to add to the classpath. If the given string represents a directory,
   * then the directory it represents is added.
   *
   * @param f The directory (or enclosing directory if a file) to add to the
   * classpath.
   */
  public static void add( File f )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    f = f.isDirectory() ? f : f.getParentFile();
    add( f.toURI().toURL() );
  }

  /**
   * Adds a new path to the classloader. The class must point to a directory,
   * not a file.
   *
   * @param url The path to include when searching the classpath.
   */
  public static void add( URL url )
    throws IOException, NoSuchMethodException, IllegalAccessException,
           InvocationTargetException {
    Method method = CLASS_LOADER.getDeclaredMethod( "addURL", PARAMETERS );
    method.setAccessible( true );
    method.invoke( getClassLoader(), new Object[]{ url } );
  }

  private static URLClassLoader getClassLoader() {
    return (URLClassLoader)ClassLoader.getSystemClassLoader();
  }
}

The link no longer works: http://forums.sun.com/thread.jspa?threadID=300557

Example Usage

The following example will add /home/user/dev/java/app/build/com/package to the classpath at runtime:

try {
  ClassPathUpdater.add( "/home/user/dev/java/app/build/com/package/Filename.class" );
}
catch( Exception e ) {
  e.printStackTrace();
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
  • Your first link is down.... And btw, this seems to assume that the application's classloader is of type URLClassLoader, which may not be the case isn't it? – Pacerier Aug 29 '14 at 05:36
  • @Pacerier: Thank you. It is possible to have a custom URLClassLoader. The solution could be made more robust by finding all subclasses of `ClassLoader` (http://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html) and attempt to call their `loadClass` method using a binary class name. – Dave Jarvis Aug 29 '14 at 05:56
  • I mean the default application classloader is provided by the **system** which may not be a URLClassLoader. (it may have been something else totally). – Pacerier Aug 29 '14 at 06:00
  • You are correct. The JVM implementation has two loaders: a boot loader and a user class loader. Array classes are not loaded by ClassLoaders. http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.3 For most practical purposes, however, JVMs come with an URLClassLoader that can be used to inject new class paths at runtime. – Dave Jarvis Aug 29 '14 at 06:10
14

You can certainly set any system properties you want at any point of time. The question is, will it have any effect? In the case of classpath, the answer is NO. The system class loader is initialized at a very early point in the startup sequence. It copies the classpath into its own data structures, and the classpath property is not read again. Changing it affect nothing in the system.

The reason for this may be two-fold. The lesser reason is performance. You may need to have some sort of data structure built for quick lookup of resources, and re-parsing classpath every time may be inefficient. The more important reason is security. You don't want a rogue class change the classpath under you and load compromised version of another class.

10

System.setProperty can be used to set some security or protocol handler at the beginning of a program. Like:

/*
Add the URL handler to the handler property. This informs 
IBMJSSE what URL handler to use to handle the safkeyring 
support. In this case IBMJCE.
*/
System.setProperty("java.protocol.handler.pkgs", "com.ibm.crypto.provider");

or for using SSL:

System.setProperty("javax.net.ssl.keyStore", context.getRealPath(KEYSTORE));
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", context.getRealPath(TRUSTSTORE));
System.setProperty("javax.net.debug", "ssl");
HttpClient httpClient = new HttpClient();
GetMethod httpGet = new GetMethod("https://something.com");
httpClient.executeMethod(httpGet);
return new String(httpGet.getResponseBody());

But beware, because it changes the environment at runtime for ALL applications running in the same jvm.
If for example one application needs to run with saxon and the other with xalan and both make use of System.setProperty to set the transformerFactory, then you will run into trouble

As said in Monitored System.setProperty article,
System.setProperty() can be an evil call.

  • It is 100% thread-hostile
  • It contains super-global variables
  • It is extremely difficult to debug when these variables mysteriously change at runtime

Regarding the classpath property, as I said in a previous question, it can not be easily changed as runtime.

In particular, java System property java.class.path is used to build a linked link when the JRE is instantiated, then is not re-read. Therefore, changes you make to the property don't really do anything to the existing virtual machine.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thanks VonC, informative indeed. However why the behavior is different for class path property. When you say at the beginning of program, i assume the program has been executed and it is kind of first thing it is doing. Correct ? – Nrj Nov 07 '08 at 09:41
  • Yeah, the java.class.path system property is more of an informational courtesy to the developer. As with all properties, you're allowed to change it via setProperty(), but doing so does nothing more than altering a String in a map. – jodonnell Nov 07 '08 at 13:31
  • I think java.security.properties is another system property that is read on JVM startup and not referenced again. I suppose the ability to change either property at runtime would cause more problems than it would solve. – bhavanki Nov 07 '08 at 14:41
  • Is there a distinction between "super"-global variables and just normal global variables? :-) – JesperE Jul 29 '09 at 17:16
  • 1
    @JesperE: Yes, it comes from the scope in which a global variable is known. a super global variable is known throughout the program without any import or prefix needed (like `$_GET` in PHP). Here a super variable is known from all apps running within the JVM, and not just from one app like a normal global variable would. – VonC Jul 29 '09 at 18:06
3

There is also a way to change java.library.path in runtime, to do that, just do:

System.setProperty( "java.library.path", newPath);
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null); // that's the key.

When this private static field in ClassLoader class is set to null, on next attempt to load native library ClassLoader will be initialized again using the new value in java.library.path.

Zorkus
  • 484
  • 1
  • 4
  • 13
0

The basic idea of getProperty() is that programs/code can be configured from outside of the JVM, passing properties on the command line using the java -Dfoo=bar syntax.

As you may want to configure certain behaviour in other software components (such as a logging component) in situations where you don't have control over the command line - think being deployed in a Servlet container - setProperty() comes in as a handy way to programmatically alter settings, e.g., before instantiating your logging utility.

The problem that is exhibited by the classpath issue is that programs will typically only read such system properties exactly once, when they are first initialized. So changing the classpath after JVM startup doesn't change anything for you app itself, because the JVM is already initialized, and changing some logging configuration after you have already obtained a Logger instance (or whatever), typically won't have any effect either.

Martin Probst
  • 9,497
  • 6
  • 31
  • 33