views:

4366

answers:

4

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?

+4  A: 

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.

VonC
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
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
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.
shockwave
Is there a distinction between "super"-global variables and just normal global variables? :-)
JesperE
@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
A: 

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
+5  A: 

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.

Arkadiy
+5  A: 

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

From the Modify Classpath At Runtime thread (with comments, refactoring, and no compile warnings):

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();
  }
}

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