jms-spec
  1. jms-spec
  2. JMS_SPEC-116

Take advantage of EJB 3.2's RA improvement for MDBs

    Details

    • Type: New Feature New Feature
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Labels:
      None

      Description

      In a late change to the EJB 3.2 spec, a new feature around building MDBs without requiring method-level implementations has been added, specifically for use within the RA.

      I am proposing that the JMS spec take advantage of this new feature in the following ways:

      The description references this email thread from the EJB Spec: http://java.net/projects/ejb-spec/lists/jsr345-experts/archive/2013-03/message/49

      1. Introduce a new interface "javax.jms.JMSMessageEndpoint" (or similar name) that can be used as a marker interface required by MessageEndpointFactory.getEndpointClass(). It shall have no methods, not extend any other interface but simply exist to be a marker that the given class will have its public methods exposed as potential targets for messages received by the RA.

      2. Introduce a new annotation, or possibly several annotations, to represent the configuration available to these methods. I believe we should support something more fluid (e.g. compiler friendly) than the existing ActivationConfigProperty set.

      3. Currently, the onMessage method is defined by looking for a method named "onMessage" that takes a "Message" object as an argument. This algorithm should be changed to also look for any instance of "JMSMessageEndpoint", find any method that is annotated as XXX (whatever is defined in 2) as a possible target, then depending on there being a match between that takes anything that derives from "Message" and only pass appropriate messages to it.

      Some down sides:

      1. The EG has already voted to not require an RA with every implementation.
      2. This is a late change, so is the EJB equivalent.
      3. Currently, MDB behavior and RA aren't necessarily defined within the JMS spec.

        Issue Links

          Activity

          John D. Ament created issue -
          Nigel Deakin made changes -
          Field Original Value New Value
          Tags ra messagedriven jms21-forreview-major messagedriven ra
          Nigel Deakin made changes -
          Summary Take advantage of EJB 3.2's RA improviement for MDBs Take advantage of EJB 3.2's RA improvement for MDBs
          Hide
          Nigel Deakin added a comment -

          This interesting change comes too late to make any corresponding changes to JMS 2.0. I have added this issue to the list of issues which will be considered for JMS 2.1 and tagged it accordingly.

          I'll also start a thread on the expert group to discuss this. Feel free to join in (or to contact me (Nigel) directly).

          Show
          Nigel Deakin added a comment - This interesting change comes too late to make any corresponding changes to JMS 2.0. I have added this issue to the list of issues which will be considered for JMS 2.1 and tagged it accordingly. I'll also start a thread on the expert group to discuss this. Feel free to join in (or to contact me (Nigel) directly).
          Nigel Deakin made changes -
          Link This issue is related to JMS_SPEC-134 [ JMS_SPEC-134 ]
          Hide
          dblevins added a comment -

          Hoping I can get some time to work on this in JMS 2.1. Adding some of the examples I've shown in presentations.

          Before

          Example JMS MDB with what we have up until EJB 3.1. It has some issues:

          • Only allows for a single method to consume messages. This results in an explosion of MDBs.
          • Loosely typed. The metadata is also passed via essentially a StringString map. Too easy to spell properties wrong or use the wrong argument format.
          • Undocumented. It isn't clear without a google search what the acceptable properties might be.
          • Overly generic method signature. Casting is required to do anything useful.
          • Unclear portability. Vendors may add properties, but it isn't clear from looking at the code which are portable.
          import javax.ejb.ActivationConfigProperty;
          import javax.ejb.MessageDriven;
          import javax.jms.JMSException;
          import javax.jms.Message;
          import javax.jms.MessageListener;
          import javax.jms.ObjectMessage;
          
          @MessageDriven(activationConfig = {
                  @ActivationConfigProperty(propertyName = "maxSessions", propertyValue = "3"),
                  @ActivationConfigProperty(propertyName = "maxMessagesPerSessions", propertyValue = "1"),
                  @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
                  @ActivationConfigProperty(propertyName = "destination", propertyValue = "TASK.QUEUE")
          })
          public class BuildTasksMessageListener implements MessageListener {
          
              @Override
              public void onMessage(Message message) {
                  try {
          
                      if (!(message instanceof ObjectMessage)) {
                          throw new JMSException("Expected ObjectMessage, received " + message.getJMSType());
                      }
          
                      final ObjectMessage objectMessage = (ObjectMessage) message;
          
                      final BuildTask buildTask = (BuildTask) objectMessage.getObject();
          
                      doSomethingUseful(buildTask);
          
                  } catch (JMSException e) {
                      // Why can't I throw a JMS Exception
                      throw new RuntimeException(e);
                  }
              }
          
              // This is the only "useful" code in the class
              private void doSomethingUseful(BuildTask buildTask) {
                  System.out.println(buildTask);
              }
          }
          

          After

          Example JMS MDB with what we could do now with the changes added to EJB 3.2. Benefits:

          • Possible to consume multiple destinations in a single MDB
          • Strongly typed. You cannot misspell an annotation or use the wrong input type without a compile error.
          • Documented. Most IDEs allow you to easily explore the classes and annotations inside a package. Browse away and see your configuration choices.
          • User-defined method signature. If you need an ObjectMessage use it as the parameter. Better yet, use the value from inside the ObjectMessage as the parameter.
          • Clearer portability. Any annotations on the listener method not from javax.* are clearly vendor-specific
          import javax.ejb.ActivationConfigProperty;
          import javax.ejb.MessageDriven;
          import javax.jms.JMSException;
          import javax.jms.ObjectMessage;
          import javax.jms.foo.Destination;
          import javax.jms.foo.DestinationType;
          import javax.jms.foo.MaxMessagesPerSession;
          import javax.jms.foo.MaxSessions;
          import javax.jms.foo.MessageType;
          
          @MessageDriven
          @MaxSessions(3)
          @MaxMessagesPerSession(1)
          public class BuildTasksMessageListener {
          
              @Destination("TASK.QUEUE")
              @DestinationType(javax.jms.Queue.class)
              @MessageType(ObjectMessage.class)
              public void processBuildTask(BuildTask buildTask) throws JMSException {
          
                  System.out.println("Something useful " + buildTask);
              }
          
              @Destination("BUILD.TOPIC")
              @DestinationType(javax.jms.Topic.class)
              @MessageType(ObjectMessage.class)
              public void processBuildTask(BuildNotification notification) throws JMSException {
          
                  System.out.println("Something happened " + notification);
              }
          
          }
          

          The actual annotations we use would of course be defined by the group, but this is just to get the creative juices flowing.

          Show
          dblevins added a comment - Hoping I can get some time to work on this in JMS 2.1. Adding some of the examples I've shown in presentations. Before Example JMS MDB with what we have up until EJB 3.1. It has some issues: Only allows for a single method to consume messages. This results in an explosion of MDBs. Loosely typed. The metadata is also passed via essentially a String String map. Too easy to spell properties wrong or use the wrong argument format. Undocumented. It isn't clear without a google search what the acceptable properties might be. Overly generic method signature. Casting is required to do anything useful. Unclear portability. Vendors may add properties, but it isn't clear from looking at the code which are portable. import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "maxSessions" , propertyValue = "3" ), @ActivationConfigProperty(propertyName = "maxMessagesPerSessions" , propertyValue = "1" ), @ActivationConfigProperty(propertyName = "destinationType" , propertyValue = "javax.jms.Queue" ), @ActivationConfigProperty(propertyName = "destination" , propertyValue = "TASK.QUEUE" ) }) public class BuildTasksMessageListener implements MessageListener { @Override public void onMessage(Message message) { try { if (!(message instanceof ObjectMessage)) { throw new JMSException( "Expected ObjectMessage, received " + message.getJMSType()); } final ObjectMessage objectMessage = (ObjectMessage) message; final BuildTask buildTask = (BuildTask) objectMessage.getObject(); doSomethingUseful(buildTask); } catch (JMSException e) { // Why can't I throw a JMS Exception throw new RuntimeException(e); } } // This is the only "useful" code in the class private void doSomethingUseful(BuildTask buildTask) { System .out.println(buildTask); } } After Example JMS MDB with what we could do now with the changes added to EJB 3.2. Benefits: Possible to consume multiple destinations in a single MDB Strongly typed. You cannot misspell an annotation or use the wrong input type without a compile error. Documented. Most IDEs allow you to easily explore the classes and annotations inside a package. Browse away and see your configuration choices. User-defined method signature. If you need an ObjectMessage use it as the parameter. Better yet, use the value from inside the ObjectMessage as the parameter. Clearer portability. Any annotations on the listener method not from javax.* are clearly vendor-specific import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.jms.JMSException; import javax.jms.ObjectMessage; import javax.jms.foo.Destination; import javax.jms.foo.DestinationType; import javax.jms.foo.MaxMessagesPerSession; import javax.jms.foo.MaxSessions; import javax.jms.foo.MessageType; @MessageDriven @MaxSessions(3) @MaxMessagesPerSession(1) public class BuildTasksMessageListener { @Destination( "TASK.QUEUE" ) @DestinationType(javax.jms.Queue.class) @MessageType(ObjectMessage.class) public void processBuildTask(BuildTask buildTask) throws JMSException { System .out.println( "Something useful " + buildTask); } @Destination( "BUILD.TOPIC" ) @DestinationType(javax.jms.Topic.class) @MessageType(ObjectMessage.class) public void processBuildTask(BuildNotification notification) throws JMSException { System .out.println( "Something happened " + notification); } } The actual annotations we use would of course be defined by the group, but this is just to get the creative juices flowing.
          Hide
          rdohna added a comment -

          David,

          Most of the annotations you proposed could be optional, all but @MessageDriven.

          • @MaxSessions and @MaxMessagesPerSession could be configured in the container (and have useful defaults).
          • @MessageType could be managed by the RA: If it's a text message, it could try to unmarshal it, e.g. by using a JAX-RS MessageBodyReader... this would probably require a Content-Type JMS message property. Even a MapMessage could be converted into an object when certain conventions are followed.
          • @DestinationType is a property of the destination. Adding it would just make sure it's correct; or it would allow the RA to actually create it on the fly.
          • @Destination could have a useful default: E.g. fully qualified class name of the MDB.

          In the Message-API it's allowed to pass multiple arguments and the name of the method is used also, which is esp. handy when it's a verb with actual semantic (like delete). E.g. if the BuildTask has the properties jobName and revisionNumber and you want to start it, the method signature would be public void start(String jobName, long revisionNumber). If the message is a MapMessage, the names jobName and revisionNumber would be used to read a String and a long. If it's a TextMessage containing a xml body, it would look like <start><jobName>build</jobName><revisionNumber>1234243</revisionNumber></start>.

          It's quite short, does it make sense? What do you think?

          Show
          rdohna added a comment - David, Most of the annotations you proposed could be optional, all but @MessageDriven . @MaxSessions and @MaxMessagesPerSession could be configured in the container (and have useful defaults). @MessageType could be managed by the RA: If it's a text message, it could try to unmarshal it, e.g. by using a JAX-RS MessageBodyReader ... this would probably require a Content-Type JMS message property. Even a MapMessage could be converted into an object when certain conventions are followed. @DestinationType is a property of the destination. Adding it would just make sure it's correct; or it would allow the RA to actually create it on the fly. @Destination could have a useful default: E.g. fully qualified class name of the MDB. In the Message-API it's allowed to pass multiple arguments and the name of the method is used also, which is esp. handy when it's a verb with actual semantic (like delete ). E.g. if the BuildTask has the properties jobName and revisionNumber and you want to start it, the method signature would be public void start(String jobName, long revisionNumber) . If the message is a MapMessage , the names jobName and revisionNumber would be used to read a String and a long. If it's a TextMessage containing a xml body, it would look like <start><jobName>build</jobName><revisionNumber>1234243</revisionNumber></start> . It's quite short, does it make sense? What do you think?
          Nigel Deakin made changes -
          Assignee Nigel Deakin [ nigeldeakin ]

            People

            • Assignee:
              Nigel Deakin
              Reporter:
              John D. Ament
            • Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

              • Created:
                Updated: