Thursday, October 19, 2017

Java Annotation, Reflection and Inheritance

Annotation

Annotation is most commonly used to store extra information in classes that could be used at runtime by frameworks.

An annotation is defined as meta-data type. attributes could be added to store specific values when the annotation is used.

Reflection

Frameworks use the Java reflection to retrieve annotations.

Inheritance

Annotation Subclass

It is not possible to extend an annotation to create a annotation type hierarchy. However a new annotation could be created to substitute an annotation with specific attribute values when it is used frequently. Or a new annotation could be used to represent multiple annotations if they are frequently used together.

such as:

@Category(value="Food")
public @interface FoodCategory {}

@Action1
@Action2
@Action3
public @interface ActionGroup1 {}


Annotation Type Declaration Inheritance

If the @Inherited annotation is used, the Annotation declaration is inherited in class hierarchy only. The annotation declaration on an interface is not inherited.

However, the Annotations declared on interfaces are able to be used by their sub types by using reflection to search its type hierarchy. If the interfaces are searched, it is going to be a tree search!

Here are some tests that demo the usages of get annotations in type hierarchies through reflection:



@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Inherited
public @interface MyAnnotation {
       String name();
       String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@MyAnnotation(name = "usage", value = "extends")
public @interface MyAnnotationExt {
       String name();
       String value();
}

public class AnnotationUsages {

       @MyAnnotation(name = "name1", value = "value1")
       public void getAnnotation() {
             try {
                    Class<? extends AnnotationUsages> cls = this.getClass();
                    Method method = cls.getMethod("getAnnotation");
                    MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
                    System.out.println(myAnnotation);
             } catch (SecurityException | NoSuchMethodException e) {
                    e.printStackTrace();
             }
       }

       @MyAnnotationExt(name = "name2", value = "value2")
       public void getAnnotationOfAnnotation() {
             try {
                    Class<? extends AnnotationUsages> cls = this.getClass();
                    Method mth = cls.getMethod("getAnnotationOfAnnotation");
                    Annotation[] annotations = mth.getAnnotations();
                    for (Annotation annotation : annotations) {
                           System.out.println(annotation.toString());
                    }
                    MyAnnotationExt myAnnotationExt = mth.getAnnotation(MyAnnotationExt.class);
                    annotations = myAnnotationExt.annotationType().getAnnotations();
                    for (Annotation annotation : annotations) {
                           System.out.println(annotation.toString());
                    }
             } catch (SecurityException | NoSuchMethodException e) {
                    e.printStackTrace();
             }
       }

       // Annotations over annotation

       @Test
       public void testMethodAnnotations() {
             AnnotationUsages usages = new AnnotationUsages();
             usages.getAnnotation();
             usages.getAnnotationOfAnnotation();
       }

       // Annotations over type hierarchy

       @Test
       public void testTypeAnnotations() {
             MyClass1 cls1 = new MyClass1();
             cls1.getTypeAnnotation();
             MyClass2 cls2 = new MyClass2();
             cls2.getTypeAnnotation();
             MyClass3 cls3 = new MyClass3();
             cls3.getTypeAnnotation();
             MyInterface myInterfaceInstance = new MyClass3();
             System.out.println("===MyInterface===");
             try {
                    Class<?> myInterface = myInterfaceInstance.getClass().getInterfaces()[0];
                    Annotation[] annotations = myInterface.getAnnotations();
                    for (Annotation annotation : annotations) {
                           System.out.println(annotation.toString());
                    }
             } catch (SecurityException e) {
                    e.printStackTrace();
             }
       }

       @MyAnnotation(name = "name1", value = "value1")
       @MyAnnotationExt(name = "name2", value = "value2")
       interface MyInterface {
             public void getTypeAnnotation();
       }

       @MyAnnotation(name = "name10", value = "value10")
       @MyAnnotationExt(name = "name20", value = "value20")
       public static class MyClass1 {
             public void getTypeAnnotation() {
                    try {
                           System.out.println("===MyClass1===");
                           Class<? extends MyClass1> cls = this.getClass();
                           Annotation[] annotations = cls.getAnnotations();
                           for (Annotation annotation : annotations) {
                                 System.out.println(annotation.toString());
                           }
                    } catch (SecurityException e) {
                           e.printStackTrace();
                    }
             }
       }

       public static class MyClass2 extends MyClass1 {
             @Override
             public void getTypeAnnotation() {
                    System.out.println("===MyClass2===");
                    try {
                           Class<? extends MyClass2> cls = this.getClass();
                           Annotation[] annotations = cls.getAnnotations();
                           for (Annotation annotation : annotations) {
                                 System.out.println(annotation.toString());
                           }
                    } catch (SecurityException e) {
                           e.printStackTrace();
                    }
             }
       }

       public static class MyClass3 implements MyInterface {
             @Override
             public void getTypeAnnotation() {
                    System.out.println("===MyClass3===");
                    try {
                           Class<? extends MyClass3> cls = this.getClass();
                           Annotation[] annotations = cls.getAnnotations();
                           for (Annotation annotation : annotations) {
                                 System.out.println(annotation.toString());
                           }
                    } catch (SecurityException e) {
                           e.printStackTrace();
                    }
             }
       }
}





 The results:


@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=name1, value=value1)
@com.americanexpress.ssae.sample.annotation.MyAnnotationExt(name=name2, value=value2)
@java.lang.annotation.Retention(value=RUNTIME)
@java.lang.annotation.Target(value=[TYPE, METHOD, PARAMETER, FIELD, ANNOTATION_TYPE])
@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=usage, value=extends)

===MyClass1===
@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=name10, value=value10)
@com.americanexpress.ssae.sample.annotation.MyAnnotationExt(name=name20, value=value20)
===MyClass2===
@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=name10, value=value10)
===MyClass3===
===MyInterface===
@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=name1, value=value1)
@com.americanexpress.ssae.sample.annotation.MyAnnotationExt(name=name2, value=value2)

Thursday, August 24, 2017

A Concurrent Result Cache to avoid duplicating effort of expensive operation

A Concurrent Result Cache solution adopted from the code sample in the book: Java Concurrency in Practice by Brian Goetz.

There are occasions that an expensive operation needs to be ran in a multi-thread environment. The following code is a solution to be used to avoid running the expensive operations more than once. 

The Interface of expensive computation:


public interface Computable<A, V> {
         public V compute(final A arg) throws InterruptedException;
}


The concurrent result cache implementation:

This implementation guarantees that only one expensive execution will be invoked, other thread will be waiting for the Future to return.

public class ConcurrentResultCache<A, V> implements Computable<A, V> {
         private static final Logger logger = LoggerFactory.getLogger(ConcurrentResultCache.class);
         private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
         private final Computable<A, V> c;

         public ConcurrentResultCache(final Computable<A, V> c) {
                 this.c = c;
         }

         public V compute(final A arg) throws InterruptedException {
                 logger.trace("entered compute...");
                 // waiting result to be available. Intercept any CacellationException.
                 while (true) {
                          logger.trace("before first get");
                          Future<V> f = cache.get(arg);
                          logger.trace("after first get");
                          if (f == null) {
                                  logger.trace("f is null");
                                  // Creating a new task wrapped in a Callable.
                                  Callable<V> eval = new Callable<V>() {
                                           public V call() throws InterruptedException {
                                                   return c.compute(arg);
                                           }
                                  };
                                  FutureTask<V> ft = new FutureTask<V>(eval);
                                  f = cache.putIfAbsent(arg, ft);
                                  logger.trace("after putIfAbsent()");
                                  if (f == null) {
                                           // if the task is put into the cache first time, run the task
                                         // in the SAME thread.
                                           f = ft;
                                           logger.trace("before run()");
                                           ft.run();
                                  }
                          }
                          try {
                                  logger.trace("before second get");
                                  return f.get();
                          } catch (CancellationException e) {
                                  cache.remove(arg, f);
                          } catch (ExecutionException e) {
                                  throw new IllegalStateException("Task execution exception caught.", e);
                          }
                 }
         }
        
         public void remove(final String key) {
                 this.cache.remove(key);
         }
}


One example of expensive operation: Property File upload.


public class PropertyLoader implements Computable<String, Properties> {
         private static final Logger logger = LoggerFactory.getLogger(PropertyLoader.class);

         @Override
         public Properties compute(final String filePath) throws InterruptedException {
                 logger.trace("entered PropertyLoader compute()");
                 return readProperties(filePath);
         }

         private Properties readProperties(final String filePath) {
                 final String propFile = System.getProperty(filePath);
                 final Properties properties = new Properties();
                 try {
                          properties.load(new FileReader(propFile));
                 } catch (IOException e) {
                          logger.error("Failed to load properties.", e);
                 }
                 return properties;
         }
}


A usage example:


public class PropertyUpdateManager {
         private static final Logger logger = LoggerFactory.getLogger(PropertyUpdateManager.class);

         private static final String ENV_PROPERTIES_FILE = "envPropertiesFile";
         private static ConcurrentResultCache<String, Properties> propertiesCache = new ConcurrentResultCache<String, Properties>(
                          new PropertyLoader());

         private static PropertyUpdateConfiguration propertyUpdateConfiguration = null;
         private static boolean cacheCleared = false;

         public static Properties getPropertyMap() {
                 if (propertyUpdateConfiguration == null || isTimeToUpdate(propertyUpdateConfiguration)) {
                          try {
                                  clearPropertiesCache(ENV_PROPERTIES_FILE);
                                  cacheCleared = false;
                                  Properties properties = propertiesCache.compute(DFP_ENV_PROPERTIES_FILE);
                          } catch (InterruptedException e) {
                                  logger.warn("properties update has been interrupted.", e);
                                  properties = new Properties();
                          }
                 }
                 return Properties;
         }

         public static synchronized void clearPropertiesCache(final String key) {
                 if (!cacheCleared) {
                          if (propertiesCache != null) {
                                  propertiesCache.remove(key);
                          }
                          cacheCleared = true;
                 }
         }

         private static boolean isTimeToUpdate(final PropertyUpdateConfiguration original) {
                 final long currentDt = System.currentTimeMillis();
                 final long timeSinceLastUpdate = currentDt - original.getLastUpdateDt();
                 logger.debug("timeSincelastUpdate:{}, updateDurationMs:{}", timeSinceLastUpdate,
                                  original.getUpdateDurationMs());
                 return timeSinceLastUpdate > original.getUpdateDurationMs();
         }
}


Monday, July 24, 2017

Linux Commands

Find processes by user:
ps -efl | grep <username>

Find process statistics by user:
top -U <username>

Find file location:
find / -name <filename> 2>/dev/null

find file with name:
locate -i -q <filename>

Tail a process output to console
find the process id
cd /proc/id
cd fd
tail -f 1