Skip to Content

Currently I am working for SAP Netweaver Developer Studio which is based on an open source IDE. At a certain point, I had to call a http URL through a proxy and highly appreciated the new Java 5.0 functionalities for proxying. The proxy needed authentication with a generated password which may occasionally expire. When this happened I noticed a popup which certainly was not of my origin asking for the proxies user and password. This definitely was not what I desired since in that case I wanted to ask for the user and to generate the password anew.

What had happened? After a few minutes of debugging, I found that the popup came from functionality of a (purposedly unnamed 😉 ) project as part of the basic IDE installation. This component made use of the possibility to pass http authentication handling via java.net.Authenticator#setDefault(java.net.Authenticator). At least with Sun’s jre, java.net.HttpURLConnection queries the Authenticator if a http request fails with authentication required or proxy authentication required. Since I had some minor encounters with static context already this made me think about the implications of static context in general.

So, what is bad with the authenticator and static context in general?

  • Even though the IDE uses separate class loaders for each component any java.* class will be forced to be loaded through a parent (system) class loader, so that no one can create its own context on these classes.
  • As a result, everybody is forced to use either what is provided at the time being called or to override any previous setting. Especially in component based environments, this is a problem: You cannot determine whether your component is loaded first or another component possibly doing the same setting.
  • Even worse, in a multithreaded environment, you cannot safely set and reset the property while your critical part is running. Sadly, for the concrete Authenticator example, there is no getter, which prevents temporarily setting/resetting the property.
  • The IDE I was based on is an open platform (and I would consider being this the case for any pluggable multi-component platform like a J2EE-engine). It is always at least difficult to foresee which the needs of your consumers will be.
  • From the location where the Authenticator was provided I would conclude that the authenticator is not a one provided by the generic components but by one of the (optional) tools. Such a central function is better to be provided by more generic components. This illustrates nicely that core functionalities can be easily provided by non-core components via static context.

What is the way out of these difficulties? I will try to give a couple of counter measures in their order of precedence (so try out the first on the list and if this is not possible, the next, and so on):

  1. Avoid static context wherever you can. Static context has several drawbacks:
      1. The class using it cannot be refactored to an interface. This can hinder extensibility for quite a bit.
      2. The context may rely on static initializers. I do not like them very much. If they fail the class cannot be loaded, which might drain down ones component at best and the entire application at worst.
      3. For memory consuming objects as context, these become subject to garbage collection only if the entire class object becomes collected. So the lifetime of the object might be much larger than needed.
      4. Special safeguarding for the context object is needed if you want to circumvent the problems described in the previous sections (a proposal how to do this is shown below).

  2. In component based environments, one can provide a hook for the context object, i.e some API functionality wraps the static context. In the concrete example that would mean that an authenticator is set for the platform and that there is a hook to change it temporarily. The drawback of this against the previous suggestion is obvious: Every contributor must obey the contract to use that API. This makes the pattern breakable easily.
  3. If one is the owner of the component using static context and it is inevitable to do so one can safeguard the object by providing the possibility to change it through a wrapper class which ensures that it can be changed only by one client at a time. A proposal is shown below.

First an interface which defines the basic functions for a protected object.



/**

  1. Intended to ensure exclusive write access to an object.

  1. @param <P> the protected object.

*/
public interface IExclusiveUsageLock

{
     /**
     

  1. Retrieves the currently set object.

     

  1. @return the currently set object.

      */
     public P get();
     /**
     

  1. Sets the protected field to the temporary object.

     

  1. @param temporaryObject the temporary object to set.

     

  1. @return true if the object has been set successfully, false if not.

     

  1. The latter should be returned if the lock is not aquired yet.

      */
     public boolean set(P temporaryObject);
     /**
     

  1. Resets the field to the value before this lock has been aquired.

     

  1. @return true if the object has reset successfully, false if not.

     

  1. The latter should be returned if the lock is not aquired yet.

      */
     public boolean reset();
}

A possible abstract implementation which enables the API code to release the lock, if desired.



/**

  1. Possible implementation template.

  1. @param <P> the protected object.

*/
public abstract class ExclusiveUsageLock

implements IExclusiveUsageLock

{
     private final P oldObject;
     private P currentObject;
     private volatile boolean isAquired = false;
     /**
     

  1. Constructs the lock with the object given.

     

  1. @param oldObject the protected object at the time the lock is constructed.

      */
     protected ExclusiveUsageLock(P oldObject) {
          this.oldObject = oldObject;
          this.currentObject = oldObject;
     }
     /**
     

  1. Aquires the lock.

     

  1. Should be overridden and called by subclasses to ensure access.

      */
     protected void aquire() {
          this.isAquired = true;
     }
     /**
     

  1. Retrieves whether this lock is aquired currently.

     

  1. @return true if the lock is aquired.

      */
     public synchronized boolean isAquired() {
          return this.isAquired;
     }


     /**
     

  1. {@inheritDoc}

     

  1. @return {@inheritDoc}

      */
     public synchronized P get() {
          return this.currentObject;
     }
     /**
     

  1. {@inheritDoc}

     

  1. @param temporaryObject {@inheritDoc}

      */
     public synchronized boolean set(P temporaryObject) {
          if (this.isAquired) {
               this.currentObject = temporaryObject;
               return true;
          }
          return false;
     }


     /**
     

  1. {@inheritDoc}

      */
     public synchronized boolean reset() {
          if (this.isAquired) {
               this.currentObject = this.oldObject;
               this.isAquired = false;
               return true;
          }
          return false;
     }
}


Finally lets assume a service class having a static context:



/**

  1. Example for usage of global handlers and their locking.

*/
public class Example {
     private static HandlerExclusiveUsageLock defaultHandlerLock = null;
     
     /**
     

  1. Sets the default handler if none has been set yet.

     

  1. If the method returns false try using {@link #getDefaultHandlerLock()}.

     

  1. @param defaultHandler the default handler to set.

     

  1. @return true if has been set successfully, false if already

     

  1. been set.

      */
     public static synchronized boolean setDefault(Handler defaultHandler) {
          if (defaultHandlerLock == null) {
               defaultHandlerLock = new HandlerExclusiveUsageLock(defaultHandler);
               return true;
          }
          else {
               return false;
          }
     }
     /**
     

  1. Retrieves the lock for the default handler.

     

  1. Note that this implementation is not completely fail safe but a trade-off

     

  1. for not creating too much objects. If another client gets the lock a previous

     

  1. client is free to change or reset the handler again. To prevent this a new

     

  1. lock object has to be created for each call to this method.

     

  1. @return the lock or null if no default has been set yet or

     

  1. the lock cannot be aquired.

      */
     public static synchronized IExclusiveUsageLock getDefaultHandlerLock() {
          if ((defaultHandlerLock != null) && !defaultHandlerLock.isAquired()) {
               defaultHandlerLock.aquire();
               return defaultHandlerLock;
          }
          else {
               return null;
          }
     }
     /**
     

  1. Does something with the handler.

      */
     public static void doSomething() {
          Handler h = defaultHandlerLock.get();
          h.doIt();
     }
     


     /**
     

  1. Example for a static thing to be set.

      */
     public static interface Handler {
          /**
          

  1. Method example.

           */
          public void doIt();
     }
     
     /**
     

  1. Concrete implementation of the lock.

      */
     private static class HandlerExclusiveUsageLock extends ExclusiveUsageLock {
          /**
          

  1. Initializes with the handler given.

          

  1. @param h the handler.

           */
          private HandlerExclusiveUsageLock(Handler h) {
               super(h);
          }
          /**
          

  1. Overriden to gain access.

           */
          protected void aquire() {
               super.aquire();
          }
     }
}


The above pattern shown above is meant only as a hint how one may implement the desired functionality. As indicated in the javadocs there may some flaws in the concrete example. I only wish to point the way to go ;-).

As one may note, analogue problems can arise with singletons or singleton like objects.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply