10

I have a Lotus Notes database which is performing some interaction with a remote web service. I've written a custom Java class to perform the interaction.

The class methods can be executed from one of three locations depending on the user's setup:

  1. Within a Java script library called via a Lotus Notes Java agent
  2. Within a JAR file located in the user's "jvm/lib/ext" directory
  3. Within a JAR file located in a custom directory within the user's "jvm/lib" directory (for example, "jvm/lib/custom_dir"). The Lotus Notes JVM is aware of the custom directory via usage of the "JavaUserClassesExt" local notes.ini variable.

Within my class I would simply like to be able to return the location the current class is executing from. So if it's executing from either option 2 or option 3, then return the JAR file path. If it's executing from option 1 then return something else which I can handle.

I've tried the following.

getProtectionDomain() method

getClass().getProtectionDomain().getCodeSource().getLocation()

Which results in:

java.security.AccessControlException: Access denied (java.lang.RuntimePermission getProtectionDomain)

There is no option to change any security settings on any clients running this.

Class.getResource method

String myName = "/" + getClass().getName().replace('.', '/') + ".class";
URL myResourceURL = getClass().getResource(myName);

Result: myResourceURL is ALWAYS null.

ClassLoader.getResource method

String myName2 = getClass().getName().replace('.', '/') + ".class";
ClassLoader myCL = getClass().getClassLoader();
URL myResourceURL2 = myCL.getResource(myName);

Result: myResourceURL2 is ALWAYS null.

a) Where am I going wrong above?

and

b) how do I get the location of the currently executing class using a different method?

Community
  • 1
  • 1
lee_mcmullen
  • 2,801
  • 32
  • 39

3 Answers3

4

I have managed to overcome this by wrapping my code within a "AccessController.doPrivileged" block i.e:

final String[] myLocationViaProtectionDomain = {null};
AccessController.doPrivileged(new PrivilegedAction(){
    public Object run(){
        myLocationViaProtectionDomain[0] = getClass().getProtectionDomain().getCodeSource().getLocation().toString();
        debug("myLocationViaProtectionDomain: " + myLocationViaProtectionDomain[0]);
        return null;
    }
});

Interestingly this approach works exactly as I want when the JAR file is located within the client's JVM directory i.e. points 2 and 3 from my original post. However when the same code runs when the code is being executed from within a Java script library, the following exception is thrown:

java.security.AccessControlException: Access denied (java.lang.RuntimePermission getProtectionDomain)

Which is fine because at least there is a differentiation there between 1 and (2 & 3) which I can then handle properly.

This solution was pointed out to me by the excellent Mikkel from lekkimworld.com (Twitter: @lekkim) so big thanks go out to him.

lee_mcmullen
  • 2,801
  • 32
  • 39
1

I am not sure if this helps, but we "switch" our user.dir (System property) to make sure we run "out of" a certain directory for our application. In our case we have a web start application that we kick off through Lotus Notes (which calls a BAT file, which invokes Java Web Start (JAWS) ...)

But anyway, what we do is the following.

//Normally you don't ever want to do this......
Class clazz = ClassLoader.class;
Field field = clazz.getDeclaredField("sys_paths");
field.setAccessible(true);
field.setClass(clazz, null);
try {
     //Basically we read in the path and then loop through and remove our current
     //directory which is where we tend to "kick off from" rather than where we want
     //to run from.
     String minusCurrentDir = removeCurrentDirectoryFromPath(System.getProperty("java.library.path");
     System.setProperty("java.library.path", minusCurrentDir);
}
finally {
     field.setAccessible(true);
}

Now later on you can access and modify the user.dir property or ask it where it is. This may give you access to what you want (?) Or at least maybe the top code can help.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris Aldrich
  • 1,904
  • 1
  • 22
  • 37
  • Thanks for your response Chris but I managed to get what I need by wrapping my code in an `AccessController.doPrivileged` call. See my answer below for details. – lee_mcmullen Jul 21 '11 at 15:20
  • You should probably mark that as the answer to your question then. It helps others to know what it is that you need to do. – Chris Aldrich Jul 21 '11 at 16:35
  • Thanks for the heads up. Still pretty new to StackOverflow so wasn't aware of proper etiquette. I've marked the answer and will bear it in mind in future! – lee_mcmullen Jul 21 '11 at 23:42
0

Your last line of code has myCL.getResouce(myName), where it should use myName2 instead. That could possibly be causing a problem. If it's using the version of the name with a leading '/', then you won't get anything from a ClassLoader.getResource() call. It has to have no leading slash. If that doesn't help, you could also try:

ClassLoader.getSystemResource(myName2); // name without leading slash

If that still doesn't work, it could be a security issue. According to ClassLoader.getResource() docs, it can return null if "the invoker doesn't have adequate privileges to get the resource". I just know Java, not Lotus, so I don't know what kind of security environment it runs the code in.

Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
  • Thanks for your comment Ryan, well spotted. The usage of myName vs myName2 was just a typo in my original description i.e. I was getting the same behaviour when using the correct variable name without the preceding slash. – lee_mcmullen Jul 20 '11 at 15:49