Showing posts with label managedBean. Show all posts
Showing posts with label managedBean. Show all posts

Friday, October 1, 2010

EJB.next Connector/Bean API : JAX-RS and beyond

It isn't commonly known that MessageDrivenBeans (MDBs) are not directly tied to the Java Message Service (JMS). In fact, they are tied to the Java EE Connector Architecture. It's even less commonly known that MDBs are not necessarily asynchronous. It's really the Connector that drives the communication style.
Overall, it is a very cool model that does allow for some pretty impressive and standard extension to any compliant Java EE platform. It is, however, incredibly underused. With a few changes to the model, it could be made to support even things like JAX-RS. Let's break it down.
The touchpoints between the Connector and the MDB are the ActivationSpec/ActivationConfig and the MessageListener interface. Using a fictitious "Email" Connector idea, let's see how this looks.
EmailConsumer (MessageListener) interface*
package org.superemail.connector;

// other imports...
    
public interface EmailConsumer {
    public void receiveEmail(Properties headers, String body);
}
EmailAccountInfo (ActivationSpec) class
package org.superemail.connector;
    
/**
 * This class is basically an old-style JavaBean with get/set for each property
 */
public class EmailAccountInfo implements javax.resource.spi.ActivationSpec {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void validate() throws InvalidPropertyException {
    }
}
That's it for the Connector (aside from the Connector itself). I have left out a little detail on the ActivationSpec, we'll cover that later.
Now for the MDB's side of things. The MDB needs to implement the Connector's EmailConsumer interface and configure the Connector'sEmailAccountInfo JavaBean which is done via the activation-config tag of the ejb-jar.xml or via @ActivationConfigPropertyannotations in the @MessageDriven declaration.
@MessageDriven(activationConfig = 
        {@ActivationConfigProperty(
                propertyName = "address", 
                propertyValue = "dblevins@apache.org")
        })
public class EmailBean implements EmailConsumer {

    @PostConstruct
    public void init() {
    }

    public void receiveEmail(Properties headers, String body) {
        // do your thing!
    }
}
Done. Those are the basics. The Connector supplies a MessageListener interface and an ActivationConfig JavaBean, the MDB implements the interface and configures the JavaBean via the loosely-typed @ActivationConfigProperty.
There are a few things that prevent this model from reaching its true potential:
  • Metadata is loosely typed in the bean code
  • Only class-level metadata is allowed, not method-level
  • Requiring an interface can limit expressiveness
Let's see how life might look if we eliminate the JavaBean and allow the Connector to instead supply an annotation.
package org.superemail.connector;
    
@Target(TYPE)
@Retention(RUNTIME)
@javax.resource.annotation.ActivationSpec
public @interface EmailAccountInfo {
    String address();
}
Side Note: The original javax.resource.spi.ActivationSpec interface has a 'validate()' method on it to validate the JavaBean. A clear update to that part of the API would be to instead use the Bean Validation API.
Which gives us a bean that might look like this:
@MessageDriven
@EmailAccountInfo(address = "dblevins@apache.org")
public static class EmailBean implements EmailConsumer {

    @PostConstruct
    public void init() {
    }

    public void receiveEmail(Properties headers, String body) {
        // do your thing!
    }
}
Now we're getting somewhere!
Ok, let's get rid of that message listener interface and image our Email Connector uses a very JAX-RS inspired API for consuming emails. If you know a little JAX-RS you'll see where I'm going with this.
@MessageDriven
@EmailAccountInfo(address = "dblevins@apache.org")
public static class EmailBean {

    @PostConstruct
    public void init() {
    }

    @Deliver @Header("Subject: {subject}")
    public void receiveEmail(@HeaderParam("subject") String subject, @Body String body) {
        // do your thing!
    }
}
Pretty neat, huh?
Side note: For the rare few of you that speak Connector implementation, instead of giving your Resource Adapter a MessageEndpoint that is a proxy that only implements the MessageListener interface and the standard MessageEndpoint interface, the Container would instead give you an @LocalBean proxy that also implements MessageEndpoint. If you're thinking that an API like that would be great to use... imagine with the above changes you could make that API ... and use it in any compliant Java EE platform. Maybe even submit your own JSR if you came up with something great.
Truthfully speaking, it's the Connector who controls the lifecycle of the bean (MDB). The Connector API already allows for the Connector to say to the Container, "create me a bean" and "destroy this bean". So really, we don't need @MessageDriven anymore. Instead we can do this:
import javax.annotation.ManagedBean;

@ManagedBean
@EmailAccountInfo(address = "dblevins@apache.org")
public static class EmailBean {
 // ....
}
That's a little more modern and perhaps a bit clearer.
At this point, we have a generic API to hook arbitrary "Connectors" up to arbitrary "Beans" that are managed by a container. Were this the case when JAX-RS was created, they wouldn't have had to put so much effort into duplicating all the services that people who have managed beans expect: injection of resources, lifecycle callbacks, interceptors.
Summary: A few improvements to the expressiveness of the metadata passed between a Bean and its Connector could kick the MDB/Connector model into the mainstream as was the intention of the API from the beginning. New specifications could be created based on this model and be introduced incrementally as they are developed and needed.
The impact of fully opening up the Bean metadata to the Connector is quite deep and wide and likely not something that will scream at you immediately. Ask yourself, could something like the new EJB 3.1 @Schedule API have been done with a Connector/Bean model like this? To some extent probably it could. Certainly there already existed a few Quartz Connectors out there prior to the introduction of @Schedule.
Take a moment to daydream. What kind of Connectors could you create with this model?


UPDATE - Feb 7th, 2013

Save this proposal.  Without more voices it will be delayed till Java EE 8.  Let's not wait 4 years!  Take action and celebrate the Java Community Process openness by showing your support for including this in Java EE 7.  Act now.

VOTE AND COMMENT HERE http://java.net/jira/browse/EJB_SPEC-60




Saturday, June 12, 2010

If Software is pizza, EJB is the House Special

If you can stand terrible food metaphors and have ever been exposed to EJB positively or negatively, keep reading....

I don't know about you, but I love the simplicity of a good pepperoni pizza now and then. Imagine asking for a plain Pepperoni pizza and being told your only option was to get the House Special and to pick off all the onions, black olives, green pepper and whatever else comes on top. Any person with options would simply go to another pizza shop. Well, that's EJB. Something I hope we can change in EJB 4.0.

To setup the metaphor a bit more, let's say the dough and cheese is the component itself and the proxy to the component. The toppings are the various QOSs (qualities of service) such as transaction management, interceptors, concurrency management, cron-like scheduling, asynchronous method calls, and other goodness. Each bean type such as Stateful, Stateless, Singleton and Message Driven are certain styles of pizza with a fixed set of toppings; say Meat Lovers, Vegetarian, or Hawaiian. The toppings of each style of pizza have been selected carefully to compliment each other perfectly and guarantee good results.

Currently though, if you want to start with a plain pizza and add only the toppings you want or need, you cannot do it with EJB. If you want pepperoni, you need to pick a style of pizza that has pepperoni as a topping and deal with the other toppings that come with it, wanted or not. EJB is somewhat flexible and there are usually ways to neutralize some features that amounts to picking off the toppings you don't like, but it's not the same as if they weren't ever there and you are still going to get an odd tasting and odd looking pizza.

Say you don't really want the transaction management topping. You try to pick off the topping by selecting SUPPORTS as your default transaction attribute so if a transaction is there, your bean participates in it, if not, it still does it's work regardless. All is fine until your bean throws a runtime exception and the container decides to rollback the current transaction on your behalf. Say you instead choose to try and pick off the transactional topping by making your bean use bean-managed transactions. Things work fine except now all transactions are suspended by the container so that the bean cannot mess with the state of what was the current transaction. What you really wanted was your bean to not be transactionally aware in the first place.

Unfortunately if you want one of the other toppings like container managed concurrency, cron-like scheduling, or asynchronous method calls, you have to learn to like the transaction management topping, period.

There will always be a need for specific pizza styles with pre-selected toppings -- who doesn't love a good Greek pizza or a Hawaiian now and then. The EJB spec does that well with its perfectly selected bean types, each sprinkled with the select set of QOSs deemed appropriate for that specific bean type. That said, it really should be possible to start with a plain cheese pizza and just add the toppings you want.

Forget the dough and cheese. EJB should be about the toppings.

Ideally, every QOS the EJB spec has to offer would be an option that can be applied to a plain component. Imagine if there was a "plain cheese pizza" bean type that would by default have no QOSs at all and for all intense purposes be a true POJO. There's nothing to learn or study to code it or use it. It's just a POJO created by the container. Then one day you decide you'd like it to be transaction aware. At that point you annotate it with @TransactionManagement and then go read the self contained "transaction management" section of the spec to see what is now expected of you and the users of the bean.

It may be that certain QOSs cannot be combined, which is in fact the case at times. The concept of a container managed persistence context (i.e. a field like "@PersistenceContext EntityManager myEntityManager") cannot be combined with the new @ConcurrencyManagement annotation because EntityManagers themselves are not guaranteed to be capable of concurrency. As a result EJB Singletons are not allowed to use container managed persistence contexts. That may work for some, but maybe you want a Singleton that uses a container managed persistence contexts and do not need concurrency anyway. Such a thing would be possible if you were able to choose the QOSs for your bean regardless of its lifecycle.

It's time to break up the big specs and let people build their own pizzas.