43

I would like to create my own custom annotation. My framework is stand alone Java application. When someone annotate his pojo class a "hidden" code behind will trigger methods.

For example, today in Java EE we have @MessageDriven annotation. And when you annotate your class with @MessageDriven and in addition implement MessageListener Interface there is a behind code that will trigger onMessage(Message msg). when a message arrives from a Queue/Topic.

How do I create an annotation (@MyMessageDriven) which could be added to a pojo and also implement MyCustomMessageListener.

The result which I desire is a trigger of "hidden" code (of mine) which will trigger a method of an implemented interface (exactly as it works with the sample i Wrote below).

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
rayman
  • 20,786
  • 45
  • 148
  • 246

2 Answers2

39

I recommend to read this blog entry (snapshot on archive.org) up to the point where the author remembers (s)he has access to Spring's component scan feature.

The initial issue is to scan the class path to find classes with the custom annotation. Once this is done, you have the objects in your standalone application through which using object.getClass().getAnnotations(), you can then inject the listeners or custom behavior you need to add to the objects holding the custom annotations.

Let's say you have the following custom annotation:

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMessageDriven {}

And you use it some class in you application:

@MyMessageDriven
public class MyObject {}

Now, in the appropriate location in your application, you should have a method to give out all classes carrying MyMessageDriven:

Set<Class<?>> findAllMessageDrivenClasses() {
  final StopWatch sw = new StopWatch();
  sw.start();
  final Reflections reflections = new Reflections("org.projectx", new TypeAnnotationsScanner());
  Set<Class<?>> allMessageDrivens = reflections.getTypesAnnotatedWith(MyMessageDriven.class); // NOTE HERE
  sw.stop();
  return allMessageDrivens;
}

Having this, I assume that there is a point in your application that either (1) you have access to the objects in your application, or (2) there is a visitor or iterator pattern on all the objects in the application. So, in some point, I assume that we have all targeted objects as objects:

Set<Class<?>> msgDrivenClasses = findAllMessageDrivenClasses();
for (Object o : objects) {
  if (msgDrivenClasses.contains(o.getClass()) {
    invokeTheMessageListener(o);
  }
}

On the other hand, there should be some implementation of MyMessageListener that is available when the objects having MyMessageDriven are found:

void invokeTheMessageListener(Object o) {
  theMessageListener.onMessage(o);
}

This answer is tailored from the blog entry so please refer to the blog for configuration of libraries. And, last but not least, this is a sample code for the problem and it can be refactored to more pattern-compatible and elegant style.

Update: There is a requirement that the targeted objects should be aware of their own listeners. So, I'd suggest the following approach. Let's have an interface MyMessageListenerAware:

interface MyMessageListenerAware {
  MyMessageListener getMyMessageListener();
}

// and this is the original MyMessageListener
interface MyMessageListener {
  void onMessage(Object o);
}

Now, the target objects should implement the above interface:

class MySampleObject implements MyMessageListenerAware {

  public MyMesssageListener getMyMessageLisener() {
    return mySampleObjectImplementationOfMyMessageListener;
  }

}

Having this, the method invokeTheMessageListener becomes like:

void invokeMessageListener(Object o) {
  if (o instance MyMessageListenerAware) {
    MyMessageListener l = ((MyMessageListenerAware) o).getMyMessageListener();
    l.onMessage(o);
  }
}

Although, I strongly recommend reading about Visitor or Strategy pattern. What you aim to do seems to me like you need certain objects react/act/process to a common object/event in the application but each with their own interpretation/algorithm/implementation.

Simon Zyx
  • 6,503
  • 1
  • 25
  • 37
nobeh
  • 9,784
  • 10
  • 49
  • 66
  • Hi, But I am not using Spring. – rayman Apr 18 '12 at 08:22
  • I mentioned "up to the point". The Spring is just if you have Spring other than that it's still complete. – nobeh Apr 18 '12 at 08:25
  • Yes exactly I want to inject listeners to the object but was hard for me to understand how to do it from your Spring example. maybe you got other reference for me? thanks! – rayman Apr 18 '12 at 08:37
  • Hi thanks for your detailed answer. but tell me where is the part where I implement the listener for each class which is annotated with @MyMessageDriven so when message arrives it will trigger the class onMessage method. – rayman Apr 18 '12 at 10:31
  • What i am trying to reach is exactly like MDB Annotation being implemented at JAVA-EE – rayman Apr 18 '12 at 10:51
  • I think I understand you. what I dont get it how each class which is annotated going to use the 'listening logic' (open connections and listen to Jms topic/queue..). I dont want different 'listening logic' for each listener What I am looking for is that all annotated listeners will be connected to the same 'listening logic'. hope you understand my point.. thanks! – rayman Apr 18 '12 at 13:16
  • So I should again go back to say that before "Update" section should be enough. Put it simply, all the objects that receive the message extend some _base model_ or implement a common interface. So, the message listener could presumably cast the objects and retrieve the information it needs. I hope this makes sense. I suggest you developed for some extent and play around with the ideas to get closer to what you think would fit best. – nobeh Apr 18 '12 at 20:56
  • Hi according to your code you used "StopWatch" why did you use it for? and thats a jboss component if I work on standAlone wont be able to use it. – rayman Apr 19 '12 at 08:29
  • Same with Reflections and TypeAnnotationsScanner. cant find those classes in the regular API. – rayman Apr 19 '12 at 08:35
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/10270/discussion-between-nobeh-and-rayman) – nobeh Apr 19 '12 at 10:13
10

create an annotation something like this:

 public @interface MyMessageDriven{
 }

And you have an interface that can apply annotation like this:

public interface MyMessagListener {

    public void message();
}



@MyMessageDriven  
public class MyMessage implements MyMessagListener  {
   public void message(){
     System.out.println(" I am executed")
   }
} 

Load the above class using classloader and using reflections check the annotation is presrent.

if it is present, use loaded instance to execute it.

  Object obj = ClassLoader.getSystemClassLoader().loadClass("MyMessage").newInstance();
  MyMessagListener mml =  (MyMessagListener) obj;
  mml.message();

Listener implementation you can put in MyMessage class or some other class that implements MessageListener.

In this case, need to provide implementation for message() what it is going to do.

But this class should be loaded and more important thing here is how your MyMessage class is loaded.

That is based on the meta data present in the MyMessage class.Similar way, in the real time scenario as well this is how it works.

Annotation is a metadata to a class that says based on the supplied data, do something.Had this metadata not present in the MyMessage class, you need not execute message() method.

Hope this will help you.

Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140
UVM
  • 9,776
  • 6
  • 41
  • 66
  • Hi, But two things: where do I put my listener implementation? and how do I inject it into MyMessage class. second when/where do I need to use the execution code you wrote below? – rayman Apr 18 '12 at 09:03
  • In normal MDBs, we are executing some business logic, right.That is executed automatically.So how this happens is your MDB is loaded.2.Once it is loaded, check whether annotation is present.3.If annotation is present, you cast this loaded class to MyMessageListener interface.That is the code I have shown you.But keep it mind that all these mechanism are happening inside the framework that is abstracted.I am talking that scenario only.4.Now framework, is doing some operation like , getting the message from queue or do some operation.5.Then it is calling messagelistener.message() method. – UVM Apr 18 '12 at 09:50
  • Could you show where do I implement the "Listening" logic. and how it will be connected to the annotated classes. – rayman Apr 18 '12 at 10:33
  • Just imagine framework as the code that loads MyMessage class.Just visualize this piece of code has extracted from a similar framework.Now "Listening" means an event just fired due to some action.That is what you are going to write inside the message method.Because it is an implementation of a contract.Here you DO NOT WORRY about how the listener is getting invoked.That part is handled automatically by the above of piece of code when the MyMessageListener class is having that attribute. – UVM Apr 19 '12 at 09:25