Dienstag, 13. Dezember 2011

When the timeout fails in Thread.join

There is a little danger using the join with a timeout. The implementation will call a wait on the thread instance. For this purpose the calling thread A has to get the monitor on the thread B he wants to join. During the wait this monitor is suspended, but after the wait the thread has to enter the monitor again. If now the thread B obtains the monitor itself and holds it for a long time (forever), the timeout of the wait method (inside the join) may be finished, but the monitor can not be regained after the wait. Therefore the thread A will stuck in the join method for a long time (forever) even with the timeout...

public class Test {

  private static final String DATE_PATTERN = "HH:mm:ss.SSS";
  public static FastDateFormat DATE_FORMAT = FastDateFormat.getInstance(DATE_PATTERN);

  public static String time() {
    return DATE_FORMAT.format(new Date());
  }

  public static void doSleep(long ms) {
    try {
      Thread.sleep(ms);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
  }

  public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread() {
      @Override
      public void run() {
        System.out.println(time() + " T: sleep(2000)");
        doSleep(2000);
        System.out.println(time() + " T: sleep(2000) finished");
        System.out.println(time() + " T: sync");
        synchronized (this) {
          System.out.println(time() + " T: sleep(60000)");
          doSleep(60000);
          System.out.println(time() + " T: sleep(60000) finished");
        }
        System.out.println(time() + " T: finished");
      }
    };
    System.out.println(time() + " M: t.start()");
    t.start();
    System.out.println(time() + " M: t.join(5000)");
    t.join(5000);
    System.out.println(time() + " M: finished");
  }
}

Mittwoch, 25. Mai 2011

Configuration Parameter Injection with CDi

CDI Based Configuration

I experimented with a simple possibility to inject parameter values, that can easily configured by external configuration providers.
At the injection point it should be possible to provide default values used if the parameters are not set.
All parameters are referenced by names. The default name of a parameter is the simple class name followed by a dot and the name of the injection point.
It should be possible to overwrite the name of the parameter.
The usage of the parameters should look like the following example:

@Named @RequestScoped public class ExampleBean {

 @Inject @StringParameter("Default Title") String title;
 @Inject @IntParameter(42) int value;
 @Inject @IntParameter(4711) int valueWithDefault;
 @Inject @StringParameter(name="customParamName",value="Default Value") String namedParameter;
 
 //... getters omitted
} 
  

The definition of the parameter uses one of the following qualifier annotations:

@Inherited
@Qualifier
@Target({java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.FIELD })
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
public @interface StringParameter {
 @Nonbinding String name() default "";
 @Nonbinding String value() default ""; // parameter default value
}
  

@Inherited
@Qualifier
@Target({java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.FIELD })
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
public @interface IntParameter {
 @Nonbinding String name() default "";
 @Nonbinding int value() default 0; // parameter default value
}
  

Both qualifiers provide an optional default value and an optional custom name.
The producer method for the injected parameters accesses the injection point to
read the default value and the name from the qualifier annotation of the parameter:


  @javax.inject.Singleton public class ParameterProducer {
 
   private Map<String, Integer> paramMapInt;
 private Map<String, String> paramMapString;
 
   @SuppressWarnings("unchecked") private <T extends Annotation> T getQualifier(Set<Annotation> annotations, Class<T> clazz) {
  for (Annotation qualifier : annotations) {
   if (qualifier.annotationType().equals(clazz)) {
    return (T) qualifier;
   }
  }
  return null;
 }

 @Produces @IntParameter public int createIntegerParameter(InjectionPoint ip) {
  IntParameter qualifier = getQualifier(ip.getQualifiers(), IntParameter.class);
  String parameterName = qualifier.name();
  if (parameterName == null || parameterName.isEmpty()) {
   parameterName = ip.getMember().getDeclaringClass().getSimpleName() + "." + ip.getMember().getName();
  }
  Integer configuredValue = paramMapInt.get(parameterName);
  if (configuredValue != null) {
   return configuredValue;
  } else {
   return qualifier.value();
  }
 }

 @Produces @StringParameter public String createStringParameter(InjectionPoint ip) {
  StringParameter qualifier = getQualifier(ip.getQualifiers(), StringParameter.class);
  String parameterName = qualifier.name();
  if (parameterName == null || parameterName.isEmpty()) {
   parameterName = ip.getMember().getDeclaringClass().getSimpleName() + "." + ip.getMember().getName();
  }
  String configuredValue = paramMapString.get(parameterName);
  if (configuredValue != null) {
   return configuredValue;
  } else {
   return qualifier.value();
  }
 }

  }
  

The configured parameters shall be configured by configuration providers implementing the following interface:


public interface ParameterProvider {
 
 public Map<String, Integer> getIntParameters();

 public Map<String, String> getStringParameters();

}
  

An example of a configuration provider using some hard coded values could be:


public class HardCodedParameterProvider implements ParameterProvider {

 
 @Override public Map<String, Integer> getIntParameters() {
  Map<String, Integer> params = new HashMap<String, Integer>();
  params.put("ExampleBean.value", 4711007);
  return params;
 }

 @Override public Map<String, String> getStringParameters() {
  Map<String, String> params = new HashMap<String, String>();
  params.put("ExampleBean.title", "Hello from Configuration Provider: "+this.getClass().getSimpleName());
  params.put("customParamName", "Configured-Parameter-Value");
  return params;
 }

}
  


To add the values of all found parameter providers we have to add some lines to the parameter producer:


  @javax.inject.Singleton public class ParameterProducer {
   
     @Inject Instance<ParameterProvider> parameterProviders;
   
     //...
     
 @PostConstruct public void init() {
  initParameters();
 }

 private void initParameters() {
  paramMapInt = new HashMap<String, Integer>();
  paramMapString = new HashMap<String, String>();
  for (ParameterProvider provider : parameterProviders) {
   Map<String, Integer> intParams = provider.getIntParameters();
   if (intParams != null) {
    paramMapInt.putAll(intParams);
   }
   Map<String, String> stringParams = provider.getStringParameters();
   if (stringParams != null) {
    paramMapString.putAll(stringParams);
   }
  }
 }

  }
  

To further play around with the system we add the possibility to maintain the configuration via JMX.
Therefore we make the parameter provider a managed bean by implementing the interface:


public interface ParameterMonitoringMXBean {

 public Map getIntegerParameters();

 public Map getStringParameters();

 public Integer getIntegerParameter(String parameterName);
 
 public String getStringParameter(String parameterName) ;
 
 public void setIntegerParameter(String parameterName, Integer value);
 
 public void setStringParameter(String parameterName, String value);

 public void resetIntegerParameter(String parameterName);
 
 public void resetStringParameter(String parameterName);

}
  

Furthermore we have to register the managed bean at the managed bean server:


@javax.inject.Singleton public class ParameterProducer implements ParameterMonitoringMXBean {

 @PostConstruct public void init() {
  initParameters();
  registerMxBean();
 }

 protected void registerMxBean() {
  try {
   MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
   ObjectName name = new ObjectName("com.jaw.cfg:type=Configuration");
   if (mbs.isRegistered(name)) {
    mbs.unregisterMBean(name);
   }
  } catch (Exception e) {
   throw new RuntimeException(e);
  }
 }

}
  

Of course the managed bean interface has to be implemented. Here we show only two methods as examples:


@javax.inject.Singleton public class ParameterProducer implements ParameterMonitoringMXBean {

 public Map<String, String> getStringParameters() {
  return paramMapString;
 }

 public void setStringParameter(String parameterName, String value) {
  paramMapString.put(parameterName, value);
 }

}
  

Now its possible to view and manipulate the configuration of the parameters e.g. using the MBeans view in the jConsole.
One last step is to provide the possibility to observe an event indicating a parameter changed via JMX:


public class ParameterChanged {

 private String parameterName;
 private Object oldValue;
 private Object newValue;

 public ParameterChanged(String parameterName, Object oldValue, Object newValue) {
  this.parameterName = parameterName;
  this.oldValue = oldValue;
  this.newValue = newValue;
 }

   // ... getters and setters omitted...

}
  

All JMX methods changing an parameter have to fire the event:


@javax.inject.Singleton public class ParameterProducer implements ParameterMonitoringMXBean {

 @Inject Event<ParameterChanged> parameterChangedEvent;

 public void sendParameterChanged(String parameterName, Object oldValue, Object newValue) {
  Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
  parameterChangedEvent.fire(new ParameterChanged(parameterName, oldValue, newValue));
 }


 public void setStringParameter(String parameterName, String value) {
  sendParameterChanged(parameterName, paramMapString.get(parameterName), value);
  paramMapString.put(parameterName, value);
 }

}
  

Note that setting the context class loader before sending the event is needed, because by default JMX uses another class loader than the application.
An example client observing the fired event may look like:


  @Singleton public class ParameterChangeListener {

 @Inject Logger log; // from somewhere...
 
 public void onParameterChange(@Observes ParameterChanged pc) {
  log.info("parameter "+pc.getParameterName()+" changed from "+pc.getOldValue()+" to "+pc.getNewValue());
 }
  

Note that the observer method must not be defined at session or request scope since JMX calls do not create a
context for these scopes.

Mittwoch, 9. Februar 2011

logging SVN revision numbers with stack trace

did you ever got a stack trace and lack the information regarding the version of the source code?
Using a special annotation referring the SVN revision number and a custom renderer for throwabl's in log4j may help. The annotation may look like:
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Revision {
  public String value();
}
The annotated class:

@Revision("$Revision: 42 $")
public class ExampleClass {
 
public static void main(String[] args) {
    Logger  logger = Logger.getLogger("xxx.example.ExampleClass");
logger.warn("Example exception: ",new Exception());
}
}
Note that the String "$Revision: 42 $" can be set by the SVN keyword substitution.
And the renderer that has to be configured for log4j:

  public static final class RevisionDecoratingThrowableRenderer implements ThrowableRenderer {
    public String[] doRender(final Throwable throwable) {
      StackTraceElement[] elements = throwable.getStackTrace();
      String[] lines = new String[elements.length + 1];
      lines[0] = throwable.toString();
      for (int i = 0; i < elements.length; i++) {
        lines[i + 1] = renderLine(elements[i]);
      }
      return lines;
    }

    private String renderLine(final StackTraceElement element) {
      StringBuilder b = new StringBuilder("\tat ");
      b.append(element);
      b.append(computeRevisionSuffix(element));
      return b.toString();
    }

    protected String computeRevisionSuffix(final StackTraceElement element) {
      String className = element.getClassName();
      Class<?> clazz;
      try {
        clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
      } catch (ClassNotFoundException e1) {
        try {
          clazz = Class.forName(className);
        } catch (ClassNotFoundException e2) {
          try {
            clazz = getClass().getClassLoader().loadClass(className);
          } catch (Throwable t) {
            return "";
          }
        }
      }
      Revision va = clazz.getAnnotation(Revision.class);
      if (va != null) {
        return '[' + (va.value().replaceAll("\\$", "")) + ']';
      }
      return "";
    }
  }
Certainly things can be optimized a bit, but the principle should get clear.