Enterprise Java Development@TOPIC@
Add essential EJB aspects to POJO classes
Deploy to server and test
import javax.ejb.Stateless;
...
@Stateless
public class GreeterEJB implements Greeter {
Other types include @Stateful, @Singleton, and @MessageDriven
import javax.ejb.Remote;
...
@Remote
public interface Greeter {
Defines business remote interface for EJB
Alternately can define @Remote(Interface.class) within EJB bean class
@Stateless
@Remote(Greeter.class)
public class GreeterEJB implements Greeter {
public interface Greeter {
String sayHello(String name) throws BadRequestException;
Greeting sayHello(Name name) throws BadRequestException;
}
import javax.ejb.Remote;
@Remote
public interface GreeterRemote extends Greeter {
}
Useful when EJB implements legacy interface
EJB must then implement Remote or declare @Remote
@Stateless
public class GreeterEJB implements GreeterRemote {
@Stateless
@Remote(GreeterRemote.class)
public class GreeterEJB implements Greeter {
Calling an interface a @Remote can mean a few things -- but it does not have to mean the direct caller is remote. It is recommended that you keep a separate definition of @Remote from @Local interfaces to be able to focus on the semantics of the data passed in and returned when implementing each interface. Remote interfaces should be designed to referenced entities by ID and to pass fully realized entities -- within the scope of the method intent -- by value. Local interfaces can pass lazily-loaded entities by reference. Remote method implementations could leverage sibling Local interface methods for most of their implementation. This allows Local interface EJBs to work fast-and-loose --avoiding excessive lookups -- while providing Remote interfaces at the transaction edge.
Optional callbacks to initialize and destroy resources
@PostConstruct
public void init() {
logger.info("*** GreeterEJB:init({}) ***", super.hashCode());
}
Called after all resources have been injected and before first business method called
No args, void return, no declared checked exceptions
Used to ready bean to satisfy business methods
@PreDestroy
public void destroy() {
logger.info("*** GreeterEJB:destroy({}) ***", super.hashCode());
}
If called, will be after last business method
EJB artifact looks very much like a normal POJO archive
Since our EJB is 100% annotation-based, we have no need for META-INF/ejb-jar.xml descriptor
$ jar tf target/ejb-basic-ejb-4.0.0-SNAPSHOT.jar ... info/ejava/examples/ejb/basic/ejb/BadRequestException.class info/ejava/examples/ejb/basic/ejb/Greeter.class info/ejava/examples/ejb/basic/ejb/GreeterEJB.class info/ejava/examples/ejb/basic/ejb/GreeterRemote.class info/ejava/examples/ejb/basic/dto/Name.class info/ejava/examples/ejb/basic/dto/Greeting.class ...
|-- pom.xml `-- src |-- main | `-- java | `-- info | `-- ejava | `-- examples | `-- ejb | `-- basic | |-- dto | | |-- Greeting.java | | `-- Name.java | `-- ejb | |-- BadRequestException.java | |-- GreeterEJB.java | |-- Greeter.java | `-- GreeterRemote.java `-- test |-- java | `-- info | `-- ejava | `-- examples | `-- ejb | `-- basic | `-- pojo | `-- GreeterTest.java `-- resources `-- log4j.xml
Module contains separate source trees for production and unit test code
EJB module deployed either as naked EJB
17:19:52,979 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for session bean named GreeterEJB in deployment unit deployment "ejb-basic-ejb.jar" are as follows: java:global/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote java:app/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote java:module/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote java:jboss/exported/ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote java:global/ejb-basic-ejb/GreeterEJB java:app/ejb-basic-ejb/GreeterEJB java:module/GreeterEJB 17:19:53,012 INFO [org.jboss.weld.deployer] (MSC service thread 1-1) JBAS016005: Starting Services for CDI deployment: ejb-basic-ejb.jar 17:19:53,029 INFO [org.jboss.weld.deployer] (MSC service thread 1-2) JBAS016008: Starting weld service for deployment ejb-basic-ejb.jar 17:19:53,485 INFO [org.jboss.as.server] (DeploymentScanner-threads - 1) JBAS018559: Deployed "ejb-basic-ejb.jar" (runtime-name : "ejb-basic-ejb.jar")
JBoss server prints out JNDI names to access EJB through...
@Remote
No interface
Globally within the server (java:global)
Within the deployed application (java:app)
Within the EJB module (java:module)
External to server (java:global/exported)
Derive remote client JNDI name from "exported" name
ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote
Local and No-interface interfaces are not exposed to remote clients
packaging=ejb
Required in order for downstream containers to recognize as EJB component
javax.ejb:javax.ejb-api
Supplies EJB API definitions
maven-ejb-plugin
Builds EJB and optional ejbclient artifact
<project>
...
<packaging>ejb</packaging>
<dependencies>
...
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>javax.ejb-api</artifactId>
<scope>provided</scope>
</dependency>
...
</dependencies>
<build>
<plugins>
<!-- tell EJB plugin to create a client-jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<configuration>
<generateClient>true</generateClient>
<clientExcludes>
<clientExclude>**/ejb/*Local.class</clientExclude>
<clientExclude>**/ejb/*EJB.class</clientExclude>
</clientExcludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Initialized EJB bean instances can be cached in a pool
Instances from the pool can be re-used rather than destroy and re-intialize a new instance
JBoss pools singleton and stateful session beans by default
# standalone/configuration/standalone.xml
<session-bean>
<stateful default-access-timeout="5000" cache-ref="simple" passivation-disabled-cache-ref="simple"/>
<singleton default-access-timeout="5000"/>
</session-bean>
JBoss defines a stateless session bean pool for use but left unassigned
# standalone/configuration/standalone.xml
<pools>
<bean-instance-pools>
<strict-max-pool name="slsb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
<strict-max-pool name="mdb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
</bean-instance-pools>
</pools>
No pooling can be costly when initialization costs are high
Stateless and MDB EJB pooling can be enabled globally
# standalone/configuration/standalone.xml
<session-bean>
<stateless>
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
</stateless>
<stateful default-access-timeout="5000" cache-ref="simple" passivation-disabled-cache-ref="simple"/>
<singleton default-access-timeout="5000"/>
</session-bean>
Pooling can be costly when initialization costs are low
Stateless and MDB EJB pooling can be enabled on a per-application(*)/per-EJB(GreeterEJB) basis
# META-INF/jboss-ejb3.xml
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:s="urn:security"
xmlns:c="urn:clustering:1.0"
xmlns:p="urn:ejb-pool:1.0"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1"
impl-version="2.0">
<assembly-descriptor>
<!-- defines EJB pool else not pooled -->
<p:pool>
<ejb-name>*</ejb-name>
<p:bean-instance-pool-ref>slsb-strict-max-pool</p:bean-instance-pool-ref>
</p:pool>
</assembly-descriptor>
</jboss:ejb-jar>
Added necessary aspects to POJO business logic to make it an EJB
Annotated class as @Stateless
Annotated remote interface as @Remote
Deployed to server
Obtained JNDI name of naked EJB-based @Remote
(component-name)/(ejb-name)!(remote-interface-name)
ejb-basic-ejb/GreeterEJB!info.ejava.examples.ejb.basic.ejb.GreeterRemote