Showing posts with label OpenEJB. Show all posts
Showing posts with label OpenEJB. Show all posts

Sunday, April 19, 2009

Testing EJB out of application server - Part 3. Spring managed DAO tier

After a short delay and thinking about giving Spring to manage stateless DAO tier beans, here comes 3-rd part of the article. There were some issues that had to be investigated to make the topic complete. Most of all, there was an open question of controlling Hibernate Session
life cycle, to have OpenSessionInView pattern working.
Let's define what we want to gain with DAO bean management by Spring:
1. Keep DAO beans working with EntityManager to make CRUD operations with entities.
2. Make switch from EJB' @Stateless (and even @Stateful) annotations to Spring' @Service (or have beans configured in XML file).
3. Have bean auto wiring in DAO with Spring IoC, avoiding JNDI lookups and extra configuration in XML file.
4. Ensure transaction and session management is compliant with EJB approach to handle entity beans lazy initialization problem (here I refer to interceptor and callback/template approaches).

Let's examine what changes are to be done to our simple Internet shop application.

DAO tier


Since we do not need to manage DAO beans with EJB but Spring, just change annotations on DAO bean class level:

@Service("purchaseDAO")
public class PurchaseDAOImpl extends AbstractDAO<Purchase> implements PurchaseDAO {
public PurchaseDAOImpl() {
super(Purchase.class);
}
}


Here, we used @Service that is recognized by Spring component scanning functionality we already familiar with. Also, DAO interfaces do not need @Local nor @Remote annotation (so they are removed):

public interface PurchaseDAO extends DAO<Purchase> {
}


There is also a simplification in application context configuration file - DAO beans refering through JNDI to stateful beans managed by EJB have been removed.
Important configuration step is adding <context:annotation-config/> to application context configuration file. This will include support for JPA annotations like @PersistenceContext. To have everything working we need to define bean, with name of persistence unit, which refers to EntityManagerFactory:

<bean id="entityManagerFactory"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject"
ref="entityManagerBean"/>
<property name="targetMethod"
value="getEntityManagerFactory"/>
</bean>

<alias alias="ishopPU"
name="entityManagerFactory"/>


OpenSessionInView pattern


To be correct, in Spring terms pattern is named OpenEntityManagerInView. There is a significant difference between this new approach and old-style one used by applications, built upon Hibernate ORM, to keep Hibernate Session open for a request processing cycle.
Opening Hibernate Session in filter and binding it to SessionFactory these applications relied on the fact entity beans extracted in DAO tier are kept attached to this session until request has been processed and session is closed, so access to lazily initialized references would not cause an exception.
But the approach, in it's original implementation, does not work for EJB - each transactional method is completed within its own EntityManager instance which holds reference to it's distinct Hibernate Session; opening new session does not make any affect.
Instead, using TransactionSynchronizationManager, we bind EntityManager to EntityManagerFactory so each transactional method executed with the same EntityManager, and as result the same Session, instance used:

@Stateful(name = "openSessionInViewCallback")
@LocalBinding(jndiBinding = "openSessionInViewCallbackLocal")
public class OpenSessionInViewCallbackImpl implements OpenSessionInViewCallback {
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;

@PersistenceUnit(unitName = "ishopPU")
private EntityManagerFactory entityManagerFactory;

@Init
public void invoke(Runnable r) {
TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(entityManager));

r.run();
}

@Remove
public void remove() {
TransactionSynchronizationManager.unbindResource(entityManagerFactory);
EntityManagerFactoryUtils.closeEntityManager(entityManager);
}
}


In @Init method we bind entity manager, and unbind it on callback method completion with @Remove cleanup. For more information on Spring implementation of the pattern please refer to org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter and org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor

Source code


Can be downloaded here.

That's it!

Sunday, April 5, 2009

Testing EJB out of application server - Part 2. Integrating Spring

The aim


In Part 1 I mentioned Spring's transaction management API. I believe this is quite a serious reason to think about integrating Spring framework into EJB application. But not the only one. Spring provides easy way to maintain multiple application configurations with IoC making testing configuration of your application independent of production one. Other features, like JNDI support, integration with JMS, JMX, mail and other services make the framework so popular.
Returning to our internet shop application we have to decide what we want to leave managed by EJB and what we want to put under Spring's control, and look at other options we have.

Data access. We considered JPA as an appropriate way to configure entity and DAO beans that provide basic functionality to access former. DAO stateless beans usually do not depend upon any other application tier, and really seldom use each other; EntityManager gives all an ordinal DAO needs.

Business services. This is really a good place Spring may take care of. Injecting dependencies on other beans (e.g. DAOs), configuration parameters provided within application configuration files, managing transactions is where Spring really strong. Moreover, with Spring testing module we can use these features directly in test classes.

Web services. JSR-181 defines API that is available within stateless beans to define them as providing Web services methods. EJB3 greatly supports this API, and though there is an option to use XFire framework together with Spring, to configure Web services, I see no reason to keep away from standard. Sometimes it is better avoid getting deep into framework hell. Though not always :)

Sample application changes


Domain tier


It is not a surprise that there is nothing to be changed. Entity beans still use JPA for O/R mapping that is controlled by EJB container.

Data access tier


There is one thing that needs to be done to each DAO implementation bean. Since we are going to publish DAO stateless beans in JNDI, so they will be available to Spring, we will use @LocalBinding JBoss annotation to provide bean JNDI name for production environment, as follows:
@Stateless(name = "productDAO")
@LocalBinding(jndiBinding = "productDAOLocal")
public class ProductDAOImpl extends AbstractDAO<Product> implements ProductDAO {
// methods implementations
}

Every DAO bean is configured in application context configuration file like this one:
<jee:jndi-lookup id="productDAO"
proxy-interface="org.ishop.dao.ProductDAO"
jndi-name="productDAOLocal"/>

Unfortunatelly I do not know if there is any way to avoid this, like using automatic scanning.

Service tier


Minor changes should be done to service methods - to switch to Spring transaction API we change @TransactionAttribute to @Transactional and specify other options, if needed. Also dependency injection is now controlled by Spring, based on bean name or type wiring, or on @Autowired annotation:
@Service("productService")
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDAO productDAO;
@Autowired
private PurchaseDAO purchaseDAO;

@Transactional
public void save(Product product) {
productDAO.save(product);
}

@Transactional(readOnly = true)
public Product getProduct(Long id) {
return productDAO.get(id);
}

@Transactional
public Purchase purchase(Long productId, Date paymentDate) {
// method implementation
}
}

Please notice that we used @Service that allows us to skip configuring bean in application context configuration file, relying on auto-detection option:

We need to configure transaction management - Spring will use container JTA transaction manager to which it will redirect all transaction processing requests, based on own transaction annotations support and handling:
<bean class="org.springframework.transaction.aspectj.AnnotationTransactionAspect"
factory-method="aspectOf">
<property name="transactionManager"
ref="platformTransactionManager"/>
</bean>

<bean id="platformTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager"/>

Here we configured aspect to support @Transactional annotations. One principal change is made to the build script - Java classes are processed by AspectJ compiler. Another option is use dynamic proxies that will do the same thing. One significant disadvantage of this approach is that to handle calls between business methods of the same service properly they should be made through the proxy.
One more good thing about Spring transaction management API is that service method may throw any exception (checked or unchecked). Instead of annotating exception class with @ApplicationException as EJB spec requires, in @Transactional annotation parameters we may specify which exception(s) should cause transaction rollback and which will lead to commit. Really nice.

Web services


As well as within business tier, Web service need just minor changes in API - replacement of @EJB with @Autowired and adding SpringContextInterceptor interceptor to the list of method call interceptors:
@Interceptors({SpringContextInterceptor.class, OpenSessionInViewInterceptor.class})
public class ProductWebServiceImpl implements ProductWebService {
@Autowired
private ProductService productService;
// methods skipped
}

SpringContextInterceptor is responsible for application context startup when application Web service accessed the first time.
public class SpringContextInterceptor extends SpringBeanAutowiringInterceptor {
private BeanFactory beanFactory;

protected BeanFactory getBeanFactory(Object o) {
if (beanFactory == null) {
beanFactory = new ClassPathXmlApplicationContext("/applicationContext.xml").getAutowireCapableBeanFactory();
}
return beanFactory;
}

@PreDestroy
@PrePassivate
public void releaseBean(InvocationContext invocationContext) {
super.releaseBean(invocationContext);
}

@PostConstruct
@PostActivate
public void autowireBean(InvocationContext invocationContext) {
super.autowireBean(invocationContext);
}
}


Changes to unit tests


Spring provides a module to support bean wiring into test classes for both jUnit and TestNG. What we need to change in AbstractPersistenceTest is set up application context and remove lookup() method since we do not rely on bean wiring with EJB:
@ContextConfiguration(locations = {"classpath:/test-applicationContext.xml"})
public abstract class AbstractPersistenceTest extends AbstractTestNGSpringContextTests {
@BeforeClass(groups = "database")
public final void setUpDatabase() throws NamingException, IOException {
Properties props = new Properties();
props.load(AbstractPersistenceTest.class.getResourceAsStream("/persistence.properties"));
new InitialContext(props);
}

@BeforeClass(dependsOnMethods = "setUpDatabase")
protected void springTestContextPrepareTestInstance() throws Exception {
super.springTestContextPrepareTestInstance();
}
}

Here method springTestContextPrepareTestInstance has been overriden to be called when EJB container has been set up by setUpDatabase().
Testing code does not need serious changes as well as production code does not - use dependency injection with @Autowired instead of looking up for required resource in JNDI.
I strongly believe Spring offers a lot of features which when used in enterprise level applications may reduce coupling of beans and tiers, making daily development and testing easier.

Source code


ishop.ejb-testng-spring.zip

Thanks you!

Saturday, March 21, 2009

Testing EJB out of application server - Part 1

Introduction



Earlier I had a couple of projects that used Hibernate as a persistence framework of choice. Hibernate provides rich API, that is actually a superset of what EJB3 provides, and at the same time it gives an opportunity to test core application modules and services without application server running with the application deployed on it. But using Hibernate for persistence we at the same time stay far from technologies that are standards de jure nowadays. The question is whether it is possible to follow industrial standards and still have things implemented as simple as possible. In this post I'll try to show on example the way how EJB can be used as the primary technology for persistence, bean management and Web services implementation. The most important thing it covers is testing EJBs without deploying developing application onto application server.

EJB spec offers standard way to develop enterprise level application that can be deployed on any application server, that follows the standard, but does not say how we can easily test EJBs. And usually the first what developers decide is to test them remotely on application server. The approach perhaps is the most significant time consumer and requires great experience in EJB development - each redeployment may cost too much in comparison to how much time spent on writing production code that is tested.

Fortunately there is a solution (of course there is always something we can do) - "OpenEJB offers an "embeddable and lightweight EJB 3.0 implementation that can be used as a standalone server or embedded into Tomcat, JUnit, TestNG, Eclipse, IntelliJ, Maven, Ant, and any IDE or application. OpenEJB is included in Apache Geronimo, IBM WebSphere Application Server CE, and Apple's WebObjects." - the title page states.
So, in this post we will focus on using EJB - entity and session beans, and Web services - and testing the application without application server. Since JBoss is a widely used application server, it is evident why it was chosen for development, also evident that there is Hibernate behind the scene. Next time I'm going to focus on integration with Spring framework, so stay tuned..

The price



First of all, you should consider whether functionality Hibernate provides, that is not accessible in EJB, is critical or not for your application. For example, Hibernate Criteria API, perhaps the most significant feature, can be helpful in organizing quick and efficient search on multiple criterions. Without this feature StringBuilder with multiple if/else constructs become our best friends and the worst enemies. Though EntityManager can be cast to HibernateEntityManager to access Hibernate internals, the code will not look so perfect and clear anymore as it was when the Earth had been created. Also EJB needs another approaches to solve familiar problems, like re-implementing OpenSessionInView (Hibernate solution can be found here.

Example application



Domain tier


Let's focus on a simple Internet shop application that sells products and tracks each product purchase in database. For mapping entities to database tables JPA annotations are used. Sure, Hibernate specific annotations, like @Index, still can be used, especially if you need to generate database schema script. Let's just keep things simpler.

@Entity
@Table(name = "product")
@SequenceGenerator(name = "product_generator", sequenceName = "product_seq")
public class Product {
@Id
@Column(name = "id")
@GeneratedValue(generator = "product_generator")
private Long id;

@Column(name = "code", nullable = false)
private String code;

@OneToMany(mappedBy = "product", fetch = FetchType.LAZY)
private Set<Purchase> purchases = new HashSet<Purchase>();

// ...
}

@Entity
@Table(name = "purchase")
@SequenceGenerator(name = "purchase_generator", sequenceName = "purchase_seq")
public class Purchase {
@Id
@Column(name = "id")
@GeneratedValue(generator = "purchase_generator")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id", nullable = false)
private Product product;

@Column(name = "payment_date", nullable = false)
private Date paymentDate;

// ...
}


No magic here - things look really familiar.

Data access tier



DAO beans are simple as well, and rely on EJB3 interfaces:

public interface DAO<T> {
void save(T obj);

T get(Long id);
}

@Local
public interface ProductDAO extends DAO<Product> {
}

public abstract class AbstractDAO<T> implements DAO<T> {
@PersistenceContext(unitName = "ishopPU")
private EntityManager entityManager;

public void save(T obj) {
entityManager.persist(obj);
}

// other methods
}

@Stateless(name = "productDAO")
public class ProductDAOImpl extends AbstractDAO<Product> implements ProductDAO {
// ProductDAO interface methods
}


@Local marks business interface for DAO and @Stateless controls DAO implementation beans management by EJB container. Also AbstractDAO takes reference to the entity manager for the ishopPU persistence unit, configured in persistence.xml configuration file. Persistence unit name is required when we have more than one unit listed in this file.

Service tier



Stateless session beans that provide business methods to clients, like Web services and UI, may look as follows:

@Local
public interface ProductService {
// business methods
}

@TransactionManagement
@Stateless(name = "productService", mappedName = "productService")
public class ProductServiceImpl implements ProductService {
@EJB
private ProductDAO productDAO;

@TransactionAttribute
public void save(Product product) {
productDAO.save(product);
}

@TransactionAttribute
public Product getProduct(Long id) {
return productDAO.get(id);
}

@TransactionAttribute
public Purchase purchase(Long productId, Date paymentDate) throws BusinessException {
// implementation
}
}


Business method purchase() throws BusinessException that is annotated with @ApplicationException on class level. I have to say that Spring transaction management API is much cleaner and allows to control more aspects, like isolation level, read-only option and commit/rollback behavior depending on thrown exception type, on method and not on exception class level. That's one more reason you may consider it's use in the project.

Web service



Web service looks the same way as business service except implementation class is annotated with JBoss specific annotation to specify concrete URL and JNDI context name we want to bind the service to:

@WebService(name = "ishop",
targetNamespace = "http://ishop.org",
serviceName = "productWebService")
@WebContext(contextRoot = "/ishop-ws", urlPattern = "/productWebService")
@Remote(ProductWebService.class)
@RemoteBinding(jndiBinding = "productWebServiceRemote")
@Stateless
public class ProductWebServiceImpl implements ProductWebService {
@WebMethod
public ProductBean getProduct(@WebParam(name = "id") long id) {
// implementation
}
}


Production code configuration



Persistence unit configuration is also plain, but you should notice that ishopPU unit is configured to be used with Hibernate as persistence provider (org.hibernate.ejb.HibernatePersistence) and properties listed in properties XML element go directly to the provider. The most important is that we specify dialect to use - Oracle (org.hibernate.dialect.OracleDialect) and other options like log SQL statements executed (set to true) and automatic database schema generation (set to none).

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">

<persistence-unit name="ishopPU" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>

<!-- Data source name -->
<jta-data-source>java:/ishopDS</jta-data-source>

<!-- Persistent classes -->
<class>org.ishop.domain.Product</class>
<class>org.ishop.domain.Purchase</class>

<properties>
<!-- Hibernate specific properties -->
<property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="none"/>
</properties>
</persistence-unit>
</persistence>


Properties specified for Hibernate will be used in production. For testing we will use HSQLDB as we do not need to access any pre-existing data, and will generate it for each test. Below is discussed how to change these properties (at least database dialect), for testing environment.

Testing code configuration



Let's consider what we do to test all the stuff out of application server. Assume we want to test DAO, business logic and call Web service remotely to check that application was deployed successfully in production environment, and works well.

  1. At first, to keep session beans JNDI names consistent, we configure OpenEJB descriptor file openejb-jar.xml:

    <openejb-jar xmlns="http://www.openejb.org/openejb-jar/1.1">
    <properties>
    openejb.deploymentId.format = {ejbName}
    openejb.jndiname.format = {deploymentId}{interfaceType.annotationName}
    </properties>
    </openejb-jar>


    so each session bean JNDI name consists of two components - bean name and interface type name (either Local or Remote).

  2. JNDI Configuration file for testing environment - jndi.properties - initializes JNDI context factory for OpenEJB:

    java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory


  3. The last what we do is >re-configure persistence context, in persistence.xml file to switch to testing data source:

    java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory

    # Data source configuration
    ishopPU=new://Resource?type=DataSource
    ishopPU.JdbcDriver=org.hsqldb.jdbcDriver
    ishopPU.JdbcUrl=jdbc:hsqldb:localdb/localdb

    # These properties override specified in persistence.xml
    ishopPU.hibernate.dialect=org.hibernate.dialect.HSQLDialect
    ishopPU.hibernate.hbm2ddl.auto=create-drop


    Now in unit tests OpenEJB uses HSQLDB for persistence, with correct dialect and other Hibernate properties.
    The syntax for property is following:
    <PERSISTENCE UNIT NAME>.<HIBERNATE PROPERTY NAME>=<PROPERTY VALUE>



Writing testing code



I won't be explaining here why I chose TestNG - you may easily switch to jUnit if you want to. Let's create abstract base class for tests that need access to database, and add setUpDatabase() method that initializes local database before class test methods run, loading configuration from persistence.properties file. Since all session beans are published by the container in JNDI, to access them we create an utility method lookup() that returns any bean from JNDI contaxt, by bean JNDI name:

public abstract class AbstractPersistenceTest {
private Context context;

@BeforeClass(groups = "database")
public final void setUpDatabase() throws NamingException, IOException {
Properties props = new Properties();
props.load(AbstractPersistenceTest.class.getResourceAsStream("/persistence.properties"));
context = new InitialContext(props);
}

@SuppressWarnings("unchecked")
protected final <T> T lookup(String name) throws NamingException {
return (T) context.lookup(name);
}
}


Below is a simple business service test class:

public class ProductServiceTest extends AbstractPersistenceTest {
private ProductService productService;

private Product product;

@BeforeClass(groups = "database")
public void setUpService() throws NamingException {
productService = lookup("productServiceLocal");
}

@BeforeMethod(groups = "database")
public void createProduct() {
product = new Product();
product.setCode("ABCDEF");
}

@Test(groups = "database")
public void testSave() {
productService.save(product);

Assert.assertNotNull(product.getId());
}

// other tests
}


Before class test methods run, setUpService() and createProduct() methods are invoked to get reference to ProductService bean and create test Product.

Lazy initialization problem


The problem is caused by dynamic proxies Hibernate generates instead of loading business entities or collections from database. Usually that is FetchType.LAZY specified for fetch attribute in @OneToMany and similar mapping configuration annotations. Solution existing for enterprise applications using Hibernate for persistence is opening Hibernate Session for HTTP request and keeping it open till request processing is complete. Entities loaded in any transaction stay attached to HTTP session so Web page may access lazily loaded collections and entities without LazyInitializationException being thrown.
Assume that in EJB we have no access to Hibernate internals, but there are choices how we may solve the problem.

  • Access lazily loaded objects in transaction.

    For this purpose we access all our business methods in a single transaction. For example, annotate Web service bean with @TransactionManagement (that is actually done by EJB container by default) configured to use container transaction management (TransactionManagementType.CONTAINER):

    @TransactionManagement
    // other annotations suppresses
    public class ProductWebServiceImpl implements ProductWebService {
    // implementation
    }

    This will grant that Web service and business methods are executed in the same transaction. Not the best solution, expecially because it damages business methods transaction boundaries, causing unexpected errors and probably missing results. It may not work for business methods with propagation level REQUIRES_NEW, and certainly will cause problems with NEVER.
  • Template method.

    The idea is to wrap multiple business methods and execute them in a single transaction boundary. Seems there is no difference with the previous approach, but we can use EXTENDED persistence context mode when persistent entities kept attached to stateful bean till the moment the bean is removed from context:

    @Local
    interface OpenSessionInViewCallback {
    void invoke(Runnable r);

    void remove();
    }

    @Stateful(name = "openSessionInViewCallback")
    public class OpenSessionInViewCallbackImpl implements OpenSessionInViewCallback {
    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager entityManager;

    @Init
    public void invoke(Runnable r) {
    r.run();
    }

    @Remove
    public void remove() {
    }
    }


    Template class gets reference to the stateful bean from JNDI context and removes bean from context when callback method is executed:

    public final class OpenSessionInViewTemplate {
    public static void invoke(Runnable r) {
    OpenSessionInViewCallback callback = null;
    try {
    // make lookup for callback bean
    Context context = new InitialContext();
    callback = (OpenSessionInViewCallback) context.lookup("openSessionInViewCallbackLocal");

    // invoke runnable
    callback.invoke(r);
    } catch (NamingException e) {
    throw new RuntimeException(e);
    } finally {
    if (callback != null) {
    // remove bean
    callback.remove();
    }
    }
    }
    }


    Any business methods calls can be done in run() method of Runnable without
    LazyInitializationException thrown and affecting business methods transactions boundaries. Unit tests may need to use template method approach.
  • Interceptor.

    The solution is similar to the previous one but the calling code will look much simple now:

    @Remote(ProductWebService.class)
    @Stateless(name = "productWebService")
    @TransactionManagement(TransactionManagementType.BEAN)
    @Interceptors(OpenSessionInViewInterceptor.class)
    public class ProductWebServiceImpl implements ProductWebService {
    @EJB
    private ProductService productService;

    @WebMethod
    public ProductBean getProduct(@WebParam(name = "id") long id) {
    ProductBean bean = null;

    Product product = productService.getProduct(id);
    if (product != null) {
    bean = new ProductBean();
    bean.setId(product.getId());
    bean.setCode(product.getCode());
    Date lastPaid = null;
    for (Purchase purchase : product.getPurchases()) {
    if (lastPaid == null || lastPaid.after(purchase.getPaymentDate())) {
    lastPaid = purchase.getPaymentDate();
    }
    }
    bean.setLastPaid(lastPaid);
    }
    return bean;
    }
    }


    In this example, thankfully to OpenSessionInViewInterceptor interceptor, invocation of getPurchases() will not cause error, at the same time any transaction boundary is not affected as well.


Deploying application and running tests



After application has been built with Maven:
mvn package


and everything was fine (pray first), you need to copy ishop-ear/src/main/resources/ishop-ds.xml to JBoss server/<CONFIGURATION>/deploy directory and configure according to your environment. Ensure that database driver JAR file is in JBoss application server class path. Execute schema.sql and sample.sql scripts to populate database with required tables and add sample data (databases other than Oracle may need to have them tuned). Copy ishop-ear/target/ishop-ear-1.0.ear to the same application server deployment directory. Start JBoss for that configuration:
$JBOSS_HOME/bin/run.sh -c <CONFIGURATION>


To check Web services change to ishop-wsclient/target and launch application like follows:
java -jar ishop-wsclient-1.0.jar 1

where 1 is the first product ID in production database. To have Web service client working ensure that application server has been started and available on port 8080 on local host (default settings).

Conclusion


Though following enterprise standards sometimes is a difficult decision and has its consequences, in many cases switching to EJB may increase application portability making, in the future, migration to another platform more transparent, reducing costs spent on application development and further support. At the same time, it is not so hard, as we may imagine at first, to develop and especially test enterprise application, based on EJB, out of application server. And OpenEJB helps with this.

Source code


ishop.ejb-testng.zip

Thank you.