EJB 3 Migration
I personally have very little experience with EJB 2, however the company that I work for like so many other companies still use EJB 2. Projects have started and then stopped when trying to migrate to EJB 3 as it can be a very time consuming exercise, especially the UAT testing. I will describe the compatibility, interoperability and migration issues you may face but i am no expert, thus you may have to resort to Goggle to obtain more information.
EJB 3 is completely different to EJB 2, the newer containers fully support EJB 2 but whether your application can run directly from a EJB 3 container without modification would require a bit of luck, it seems that you may have to do something in order to get it to work. Both EJB 3 and EJB 2 can co-exist, which means that the migration path can be easy by slowly migrating your EJB2 components to EJB 3.
The first EJB 3 migration item to be aware of is that if you want to package both EJB 3 and EJB 2 together then you must set the version attribute of the ejb-jar module to 3.0, if you user a older version then the container will not scan for annotations, so no EJB3 beans will be detected.
EJB 2/3 packaging | <?xml version = '1.0' encoding = 'windows-1252'?> |
You can invoke a EJB 2 bean (session or entity) from a EJB 3 bean (session or MDB), you can even use dependency injection.
Injecting a EJB 2 bean | @Stateful Note: ChargeCredit and ChargeCreditHome are the local and remote interfaces, use create method to get a reference to the remote interface and then invoke the desired business method. |
Invoke a EJB 2 entity bean | @Stateless public PlaceBidBean implements PlaceBid { .. @EJB public BidLocalHome bidLocalHome; ... BidLocal bidLocal = bidLocalHome.create(BidDTO); ... } |
Dependency injection/JNDI lookup can be summarized in the table below
EJB 2.x |
EJB 3 |
Define resource-ref in ejb-jar.xml Lookup resource |
Can use dependency injection
@Resource(name="ActionBazaarDS") |
You can also call a EJB 3 bean (session and JPA) from a EJB 2 bean. however if you server does not support EJB 2 dependency injection, you must use good old-fashioned JNDI lookup to access the EJB 3 session bean and the EJB 3 EntityManager from EJB 2 bean.
Using EJB 3 session beans from EJB 2 | <session> <ejb-name>PlaceBidBean</ejb-name> <home>actionbazaar.buslogic.PlaceBidHome</home> <remote>actionbazaar.buslogic.PlaceBid</remote> <ejb-class>actionbazaar.buslogic.PlaceBidBean</ejb-class> <session-type>stateless</session-type> ... <ejb-local-ref> <ejb-ref-name>ejb/CheckCredit</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <local>kabadibazaar.busilogic.CheckCredit</local> <ejb-link>kabadibazaar-ejb.jar#CheckCreditBean</ejb-link> <ejb-local-ref> </session> |
Using the EJB 3 JPA from EJB 2 | <session> <ejb-name>PlaceBidBean</ejb-name> ... <persistence-context-ref> <persistence-context-ref-name> ActionBazaarEntityManager </persistence-context-ref-name> <persistence-unit-name>actionBazaar</persistence-unit-name> </persistence-context-ref> </session> |
There are a number of reasons why you might want to upgrade session beans
All you have to do is the following
Here is a table to summarize the changes between EJB 2 and EJB 3
Components of a Session Bean | EJB 2 | EJB 3 |
Remote or local component interface | Extends either EJBObject or EJBLocalObject | Business interface (POJI) |
Home interface | Extends EJBHome or EJBLocalHome | Optional for maintaining EJB 2 client view. Candidate for removal |
Bean Class | implements javax.ejb.SessionBean | Implements business interface |
The lifecycle methods have changed as well, the table below details both versions
Bean Type | EJB 2 Methods | EJB 3 Methods |
Stateless, Stateful | ejbCreate | Constructor |
Stateless, Stateful | ejbPostCreate | Method annotated with @PostConstruct |
Stateful | ejbPassivate | Method annotated with @PrePassivate |
Stateful | ejbActivate | Method annotated with @PostActivate |
Stateless, Stateful | ejbRemove | Method annotated with @PreDestroy |
Stateful | remove method in home interface | Method annotated with @Remove |
Stateful | create method in home interface | Method annotated with @Init if the EJB 3 bean has a home interface |
Stateless | ejbTimeout | Method annotated with @Timeout |
EJB 2 does not define any default transactions and security settings for EJB's. You have to specify the definitions yourself for each and every bean method in a session bean, if you don't you will see different behaviors in different EJB containers. EJB 3 defines CMT as the default transaction management type for a bean and REQUIRED as the default transaction attribute for bean methods. Therefore you only need to change when REQUIRED needs to be changed.
MDB's (message-driven beans) can be easy migrated to EJB 3, you do not require to implement the javax.ejb.MessageDrivenBean interface and you may use the @MessageDriven and @ActivationConfigProperty annotations instead.
The most difficult task is migrating CMP entity beans to the EJB 3 JPA and requires careful planning.CMP entity beans are coarse-gained data objects where as EJB 3 entities are lightweight domain objects that represent fine-grained data, they also lack remoteness, declarative security and transactions but this is probably a good thing. You should redesign your design model when migrating entities to EJB 3, thus you can take advantage of the OO features that EJB 3 offers.
EJB 2 entity | public abstract class UserBean implements EntityBean { public void ejbActivate() throws EJBException {} public void ejbStore() throws EJBException {} } |
EJB 3 entity | @Entity @Table(name="USERS") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "USER_TYPE", discriminatorType = DiscriminatorType.STRING, length = 1) public class User {} @Entity @DiscriminatorValue(value = "S") public class Seller extends User {} @Entity @DiscriminatorValue(value = "B") public class Bidder extends User {} |
After refactoring the entities you should not need to change the database structure, but you need thoroughly test your application.
DTO (data transfer object) are no longer required because entities are themselves POJO's that can be transferred between clients and servers. Also relationships are defined in XML files which can now be converted to annotations (see Domain Models for more information).
DTO | public class UserDTO implements Serializable { private String userId; private Date birthDate; ... private Collection bids; public UserDTO() {} public String getUserId() { return userId; } public void setUserId(String userId;) { this.userId = userId; } ... } |
EJB 3 entity | @Entity @Table(name = "USERS") public class User implements Serializable { @Id @Column(name = "USER_ID") private String userId; private Date birthDate; ... } |
In EJB 2 the home interface acted as a factory interface to provide methods to create, remove and find entity bean instances. Clients used these methods to persist, remove and query bean instances. In the EJB 3 world applications should use the EntityManager API. You can migrate the home interface to a session facade by moving all factory methods such as create, remove and find to this facade.
UserLocalHome interface | public interface UserLocalHome { |
Session implementation | @Stateless public class UserLocalHomeBean implements UserLocalHome { @PersistenceContext EntityManager em; User create(String userId, String firstName, String lastName, String userType) throws CreateException { User user = new User(userId, firstName, lastName, userType); try { em.persist(user); } catch (Exception ex) { throw new CreateException(e.getMessage()); } return user; } User findByPrimaryKey(String primaryKey) throws FinderException { try { return (User) em.find(User.class, primaryKey); } catch (Exception ex) { throw new FinderException(e.getMessage()); } } } |
Like I said at the beginning, I am not to familiar with EJB 2 and if I do get involved with an EJB 2 I upgrade to EJB 3 I will update this document accordingly