+ All Categories
Home > Documents > To EJB or not to EJB? Take your pick! By Andy Matthys-Pearce, Sun Certified Enterprise Architect for...

To EJB or not to EJB? Take your pick! By Andy Matthys-Pearce, Sun Certified Enterprise Architect for...

Date post: 29-Dec-2015
Category:
Upload: rodney-robbins
View: 216 times
Download: 1 times
Share this document with a friend
39
To EJB or not to EJB? Take your pick! By Andy Matthys-Pearce, Sun Certified Enterprise Architect for the J2EE Platform
Transcript

To EJB or not to EJB? Take your pick!

By Andy Matthys-Pearce,Sun Certified Enterprise Architect

for the J2EE Platform

To EJB or not to EJB?

● 2001 Gartner report concluding that companies had overspent $1 Billion on EJB Servers

● “To EJB or not EJB?”, article on JavaWorld by Humphrey Sheil, also discusses how to decide whether to use EJBhttp://www.javaworld.com/javaworld/jw-12-2001/jw-1207-yesnoejb.html?

● In response, Ed Roman discusses deciding on when EJBs are appropriate for an application in the 2nd edition of Mastering Enterprise JavaBeans http://www2.theserverside.com/resources/article.jsp?l=Is-EJB-Appropriate

Take your pick!

What does this mean?● Deciding whether an application is EJB-centric is typically a very early project decision. Once it is decided to use EJB, the decision is essentially final. The design and code of the components assumes the EJB framework, cementing the decision to use EJB.

But ... You can have a choice! Even at deployment time!

This presentation is about developing enterprise Java applications in a way that is independent of whether EJBs are used, so we can ...

Choose to deploy the application1. with Session EJBs, or2. with Web Services, or3. with simple local Java classes

Typical J2EE Application

HTTP Request

J2EE Application Server

ControllerServlet

Servlet Engine EJB Container

Session EJB

JSP

Database

Entity EJBor

DataAccessObject using JDBCHTTP

Response

Flexibility gained in interaction between controller servlet and business service component

(Business service logic)

“Simple” EJB Client Code

try { Properties props = new Properties(); props.put( Context.PROVIDER_URL, "jnp://localhost:1099" ); props.put( Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces..."); props.put( Context.URL_PKG_PREFIXES, "org.jnp. ..." );

InitialContext ctx = null; String jndiName = null;

ctx = new InitialContext( props ); jndiName = "ejb/CustomerInfo"; CustomerInfoRemoteHome home = (CustomerInfoRemoteHome)ctx.lookup( jndiName ); CustomerInfoRemote customerInfo = home.create(); CustomerDataList customerList = customerInfo.findCustomer( "lastName", "firstName" );

“Simple” Business Client Code, cont.

} catch (NamingException e) { // handle NamingException

} catch (RemoteException e) {

// handle RemoteException

} catch (CreateException e) {

// handle CreateException

} catch (ApplicationException e) {

// handle ApplicationException

} catch (SystemException e) {

// handle SystemException

}

A client for each type of implementation

Business Client withEJB

client code

Business Service implemented

as Session EJB

Business Client with

Web Service client code

Business Service implemented

as Web Service

Business Client with Local

client code

Business Service implemented

as Local Java class

●When the client code knows which kind of business service implementation it is interacting with, we need a version of client code for each type of business service implementation we're interested in using.

Single client for all implementations

Business Service implemented

as Session EJB

Business Client

Business Service implemented

as Web Service

Business Service implemented

as Local Java class

● A single version of the business client code is a key element of enabling the flexibility to deploy an application with or without EJBs

Single business service implementation

Session EJBBusiness Service

Wrapper

Business Client

Web ServiceBusiness Service

WrapperBusiness Service

implemented as

Local Java class

● When all versions of the business service implementation ultimately use a single class which implements the real business logic, the only difference is how the business client accesses the business service

Business Service Interface

Session EJBBusiness Service

Business Client

Web ServiceBusiness Service

Business Service Interface

LocalBusiness Service

ServiceLocator

●Using a Java Interface to abstract the common business service operations considerably simplifies the business client and enforces all implementations to support the same operation signatures●Something needs to know which implementation will be used ...

that's where the ServiceLocator component comes in!

Is it possible?

Can we deploy an EJB-based application in a non-EJB configuration?

●Data access●Entity Beans would not be possible in non-EJB environment●Application could use JDBC/JDO from a DataAccessObject instead of entity beans – valid in all environments

Yes, it is possible if ...1. Data Access is through DataAccessObjects without Entity Beans2. Basic container services are made available in all environments

●Container services●Transaction support●Naming services●Database connection pools●Security●All of these can be supported in a non-EJB environment!

Is it useful?

● Benefits:1. Application design focuses strictly on the business logic provided by the business services rather than concerning the type of implementation2. Option of deploying without EJB Server

●Avoids licensing and vendor dependence●Simplifies the deployment architecture●Better performance

3. Unit test business logic without deploying to EJB Server

5. Easily migrate from Session EJB to Web Service solution

Designing application to be deployed in multiple business service configurations – Session EJB, Web Service, local service:

4. Initially deploy without EJBs and migrate to EJB configuration later

Key patterns

● Service Locator●http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html●Responsible for retrieving remote EJB references for client● Business Delegate

●http://java.sun.com/blueprints/corej2eepatterns/Patterns/BusinessDelegate.html●Instance of the Proxy or Adapter pattern●Hides details of interacting with remote business services ●java.lang.Proxy provides ability to dynamically create Objects that implement an interface, specified at runtime●Introduced in JDK 1.3 – see JavaDocs●Article: http://www.javaworld.com/javaworld/jw-11-2000/jw-1110-

proxy.html

Core J2EE Patterns:

Dynamic Proxy API:

ServiceLocator.getService()

public Object getService( Class interfaceClass );

Example client code:

ServiceLocator locator = ServiceLocator.getInstance();

CustomerInfoService infoService =(CustomerInfoService) locator.getService(

CustomerInfoService.class );

● getService() is ServiceLocator's primary method● Provides client with a very simple way to obtain a business service ref.● All the client needs to know is the business service interface

ServiceLocator responsiblities

1. Decide which implementation to use for a given interface

2. Return an object that implements the interface requested

3. For EJB:●Perform JNDI lookup and call EJBHome create() to obtain a remote EJB reference●Provide way to handle RemoteExceptions outside of client

4. Provide caching for optimization

Deciding which implementation – ServiceLocator.xml

Example:<service interface-class=”com.xyz.transaction.CreditCardService”

locator=”remote-ejb” jndiName=”ejb/CreditCardService” ejbSource=”CCServer-remote”/>

<service interface-class=”com.xyz.customer.CustomerInfoService” locator=”local” implementation-class=”com.xyz.customer.CustomerInfoServiceImpl”/>

● ServiceLocator.xml configuration file maps each business service interface to an appropriate implementation

● Advantage: the implementation of a business service can be changed simply with a change to the configuration in ServiceLocator.xml

Configuration – ServiceLocator.xml<servicelocator>

<locator name=”remote-ejb” class=”org.jlocator.RemoteEjbServiceLocator”/>

<locator name=”local” class=”org.jlocator.LocalServiceLocator”/>

<ejb-source name="CCServer"> <property name="java.naming.factory.initial" value="org.jnp.interfaces.NamingContextFactory"/> <property name="java.naming.provider.url" value="jnp://ccserver.xyz.net:1099"/> <property name="java.naming.factory.url.pkgs" value="org.jboss.naming:org.jnp.interfaces"/> </ejb-source>

<service interface-class=”com.xyz.inventory.customer.CustomerInfoService” locator=”local”

implementation-class=”com.xyz.inventory.customer.CustomerInfoService”/>

<service interface-class=”com.xyz.inventory.trans.PaymentService” locator=”remote-ejb” ejb-source=”CCServer” jndiName=”ejb/PaymentService”/>

</servicelocator>

ServiceLocator Types

Used to locate a local implementation of the

business interface

● ServiceLocator.getService() delegates to the getService() method of the appropriate specific ServiceLocator class

● The specific ServiceLocator type is determined based on the configuration's mapping of business service interface to ServiceLocator type

Used to locate a Web Service that implements

the business interfaceUsed to locate a remote EJB that implements the

business interface

LocalServiceLocator

ServiceLocator

RemoteEjbServiceLocator

WebServiceLocator

What object exactly is returned by getService()?

●Business client calls ServiceLocator.getService():

CustomerInfoService infoService =

locator.getService( CustomerInfoService.class );

● ServiceLocator delegates to a subclass, configured in ServiceConfig object representing service element for interface in ServiceLocator.xml:

public Object getService( Class interfaceClass ) { ServiceConfig serviceConfig =

ServiceLocatorConfig.getConfig( interfaceClass );ServiceLocator specificLocator =

serviceConfig.getLocator();return specificLocator.getService( interfaceClass );

}

● What Object is actually being returned by getService()All the client knows is that it implements the specified interface,but what is it really?

What's Returned by RemoteEjbServiceLocator.getService()?

Can getService() simply return a remote EJB reference?

Since, remote EJB references must throw RemoteException from each method, they cannot implement the business service interfaces whose methods are not declared to throw RemoteException.

The getService() method must return something that implements the business service interface. So the answer is no, we cannot simply return the remote EJB reference.

Also, we don't want clients to have to handle RemoteExceptions, anyway.

Object returned from RemoteEjbServiceLocator.getService():

Business Delegate option

Disadvantage:●We need to write Business Delegate classes for each business service – one for each type of implementation

● CreditCardServiceDelegate implements the business interface, and delegates the calls to the EJB, handling details of remote interaction

<interface>CreditCardService

CreditCardServiceDelegate

CreditCardServiceEJB

● GetService() could return an instance of the Business Delegate class

Create Business Delegate Dynamically with Dynamic Proxy API

● We can avoid creating specific Business Delegate classes by using the Dynamic Proxy API

Dynamic Proxy API

●Implemented in java.lang.reflect.Proxy, since JDK 1.3

●Ability to dynamically create at runtime classes and Objects that implement a set of specified interfaces

●Invocation of methods is handled by an InvocationHandler instance which is specified in the creation of the Proxy instance

Dynamic Proxy API: Proxy.newProxyInstance() and InvocationHandler

● Returns an Object that is instance of dynamically created class● The Class of the Object implements the interfaces specified● Calls to the Objects methods are handled by the InvocationHandler

Proxy.newProxyInstance( ClassLoader loader, Class[]

interfaces,InvocationHandler

handler );

public interface InvocationHandler {

public Object invoke( Object proxy, Method method, Object[]

args );}● We create an instance of InvocationHandler an put execution code inside the invoke() method

RemoteEjbServiceLocator use of Dynamic Proxy

● Get appropriate EJB remote reference via JNDI and EJBHome create()● Create an instance of InvocationHandler and give it handle to remote ref● Create dynamic Proxy object specifying interface and InvocationHandler instance

public Object getService( Class interfaceClass ) { ... // use JNDI and EJBHome create() to get EJB remote ref EJBObject remoteRef =

(EJBObject)getRemoteReference( remoteData );

RemoteEjbServiceProxy ejbProxy = new RemoteEjbServiceProxy( remoteRef );

remoteServiceImpl = Proxy.newProxyInstance(

interfaceClass.getClassLoader(), new Class[] { interfaceClass }, ejbProxy );

...

RemoteEjbServiceProxy.invoke()

public class RemoteEjbServiceProxy implements InvocationHandler {

private EJBObject remoteRef;

public RemoteEjbServiceProxy( EJBObject remoteRef ) { this.remoteRef = remoteRef; }

public Object invoke( Object proxy, Method method, Object[] args ) { String methodName = method.getName(); Class[] parameterTypes = method.getParameterTypes(); Method remoteMethod = remoteRef.getClass().getMethod( methodName,parameterTypes ); try { Object returnObj = remoteMethod.invoke( remoteRef, args ); } catch (InvocationTargetException e) { throw e.getTargetException(); } }}

WebServiceLocator using WebServiceProxy

public WebServiceProxy( String urlStr ) {this.urlStr = urlStr;

}

public Object invoke( Object proxy, Method method, Object[] args ) {

Service service = new Service();Call call = (Call) service.createCall();call.setTargetEndpointAddress( new URL( urlStr ) );call.setOperationName(new QName("http://soapinterop.org/",

method.getName() )); call.invoke( args );

}

● Same pattern used in RemoteEjbServiceLocator/Proxy can be used to interact with Web Services in WebServiceLocator/Proxy

WebServiceProxy:

LocalServiceLocator

● LocalServiceLocator doesn't need to use Dynamic Proxy API ●Implementation class directly implements business interface●No remote interactions

public Object getService( Class interfaceClass ){

ServiceConfig serviceConfig = ServiceLocatorConfig.getConfig( interfaceClass );

Class implClass = serviceConfig.getImplementationClass();return implClass.newInstance();

}

● Note: Since we are not using Dynamic Proxy here, there is no Business Delegate layer to intercept method calls. On to addressing the transaction question ...

Transparent Transaction Support

● Transactions are a critical part of enterprise applications

● Given the complexity of transaction processing, it is safer and more efficient for it to be handled by infrastructure components like in the EJB container, rather than in custom business code

● Transparent transaction support is probably the strongest selling pint for using an EJB-based solution

Transaction Control

EJBObjectBean

Instance

In the EJB Container: Transaction.begin()

Transaction.commit()

Using Business Delegate ( without EJB ):

BusinessDelegate

LocalServiceInstance

Transaction.begin()

Transaction.commit()

Adding transaction support for local business services: LocalTxServiceLocator

● We can add transparent transaction support for the local business services returned by LocalServiceLocator by using Business Delegates created by the Dynamic Proxy API

● Instead of returning an instance of the implementation class, LocalTxServiceLocator returns a Proxy instance, where LocalTxServiceProxy is the InvocationHandler

LocalTxServiceProxy demarcating transactions

public class LocalTxServiceProxy implements InvocationHandler {private Object localImpl;

public LocalTxServiceProxy( Object localImpl ) {this.localImpl = localImpl;

}

public Object invoke( Object proxy, Method method, Object[] args ) {

if ( !transactionStarted() ) { getTransaction().begin(); }

Object returnObj = null;try {

returnObj = method.invoke( localImpl, args );

getTransaction().commit();}catch ( Throwable t ){

getTransaction().rollback();throw t;

}

return returnObj; }

Requirements for Local Transaction Support

1. Naming Service – need to initialize

2. TransactionManager

●Initialize a javax.transaction.TransactionManager instance

●Bind TransactionManager to JNDI

3. javax.transaction.UserTransaction bound to JNDI

4. Data access through javax.sql.DataSource

●Initialize a connection pool and DataSource for database

driver

●Bind DataSource instance to JNDI

Local J2EE Service Resources

Naming Service:Jboss: org.jnp.server.NamingServer

public static synchronized void initLocalJNDI()throws SystemException

{

if ( !initialized ) { try { NamingServer server = new NamingServer(); NamingContext.setLocal( server ); initialized = true; } catch (NamingException e) { Log.error( logger, "initLocalJNDI(): Exception” ); throw new SystemException ( e ); } }

}

Local J2EE Service Resources

TransactionManager:Jboss: org.jboss.tm.TxManager

transactionManager = TxManager.getInstance();

InitialContext ctx = new InitialContext();

boolean txManagerAlreadyBound = false;

try { Object checkTxManager =

ctx.lookup( "java:/TransactionManager" ); if ( checkTxManager != null ) { txManagerAlreadyBound = true; } } catch (NameNotFoundException e) { //do nothing - TxManager is not bound in JNDI yet }

if ( !txManagerAlreadyBound ) { ctx.bind( "java:/TransactionManager", new Reference( "org.jboss.tm.TxManager", TransactionManagerUtil.class.getName(), null ) );

initialized = true; }

Local J2EE Service Resources

DataSource:Jboss: org.jboss.pool.jdbc.xa.wrapper.XADataSourceImpl

String poolName = dbPoolName; String dataSourceClass = "org.jboss.pool.jdbc.xa.wrapper.XADataSourceImpl"; XADataSourceLoader loader = null; loader = new XADataSourceLoader( poolName, dataSourceClass ); loader.setURL( dbUrl ); loader.setJDBCUser( dbUserName ); loader.setMinSize( 0 ); loader.setMaxSize( maxPoolSize ); loader.setBlockingTimeout( - 1 ); loader.setBlocking(true); loader.setGCInterval( 120000 ); loader.setGCMinIdleTime( 1200000 ); loader.setIdleTimeout( 30 * 60 * 1000 ); // 30 minutes loader.setMaxIdleTimeoutPercent( 1 ); loader.setPSCacheSize( 10 ); loader.startService();

Using the ServiceLocator

The ServiceLocator package can be easily used ( once it is made available on the JavaSig WebSite – www.ociweb.com/javasig ):

● Include the jlocator.jar file in your classpath

● Create a ServiceLocator.xml that maps your business service interface to the type of ServiceLocator that supports the implementation you want to use

● Use the ServiceLocator.getService( Class interfaceClass ) method in your clients

ServiceLocator.xml review

<servicelocator> <locator name="remote-ejb" class="core.locator.RemoteEjbServiceLocator"/>

<locator name="local" class="core.locator.LocalServiceLocator"/>

<locator name="trans-local" class="core.locator.TransactionalLocalServiceLocator"/>

<locator name="local-ejb" class="core.locator.LocalEjbServiceLocator" proxy-class="core.locator.proxy.LocalEjbProxy"/>

<default-locator class="core.locator.BasicLocalServiceLocator" />

<ejb-source name="local-jboss"> <property name="java.naming.factory.initial" value="org.jnp.interfaces.NamingContextFactory"/> <property name="java.naming.provider.url" value="jnp://localhost:1099"/> <property name="java.naming.factory.url.pkgs" value="org.jboss.naming:org.jnp.interfaces"/> </ejb-source>

ServiceLocator.xml review

<service interface-class="core.locator.test.CustomerUpdateService" implementation-class="core.locator.test.CustomerUpdateImpl" locator="trans-local" threadsafe="true"> <container-transaction> <method> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </service>

<service interface-class="inventory.customer.CustomerInfoService" jndi-name="CustomerInfoEJB" ejb-source="local-jboss" locator="remote-ejb" threadsafe="true"/>

<service interface-class="core.locator.test.ACoolTestInterface" implementation-class="core.locator.test.ACoolImpl" locator="local" threadsafe="true"/>

</servicelocator>

Conclusion

Do we have to use EJBs to develop serious enterprise applications? No

Do we have to decide whether an application is EJB-centric from the beginning of the development process?No.

Can write an application and switch whether it uses EJBs, Web Services, or local service implementations based on configuration at deployment time?Yes.

Enjoy the Flexibility!


Recommended