340 likes | 509 Views
Session Beans EJB3.0. February 2010 – August 2010 eric.gerlofsma@hu.nl www.ericgerlofsma.nl. Basic Architecture of EJB3.0. Implementation Server and Client Stateless versus Stateful Remote versus Local The JNDI binding Life Cycle Interceptors Context The Annotations Deployment.
E N D
Session Beans EJB3.0 February 2010 – August 2010 eric.gerlofsma@hu.nl www.ericgerlofsma.nl
Basic Architecture of EJB3.0 • Implementation Server and Client • Stateless versus Stateful • Remote versus Local • The JNDI binding • Life Cycle • Interceptors • Context • The Annotations • Deployment
A Remote Stateless SessionBean and his interface package efg.ejb; import javax.ejb.Remote; @Remote public interface HelloWorld { String sayHello(); } This is the Remote Interface implements package efg.ejb; import javax.ejb.Stateless; @Stateless public class HelloWorldBean implements HelloWorld { public String sayHello() { return "HelloWorld!"; } } This is the Remote EJB
Directory Structure HelloWorld ├─jar │└─efg │└─ejb │├─HelloWorld.class │└─HelloWorldBean.class ├─src │├─HelloWorld.java │└─HelloWorldBean.java └─build.xml
Deployment build.xml <project name="efg_ejb3_stateless_helloworld" basedir="." default="jar" > <property environment="env"/> <property name="jboss.home" value="${env.JBOSS_HOME}"/> <property name="jboss.server" value="${jboss.home}/server/default"/> <property name="jboss.deploy" value="${jboss.server}/deploy"/> <property name="app" value="${ant.project.name}.jar"/> <path id="j2ee"> <fileset dir="${jboss.home}/client"> <include name="**/*.jar"/> </fileset> </path> <target name="compile"> <javac srcdir="src" destdir="jar" deprecation="on"> <include name="**/*.java" /> <classpath refid="j2ee"/> </javac> </target> <target name="jar" depends="compile"> <jar destfile="${app}" basedir="jar"/> <move file="${app}" todir="${jboss.deploy}"/> </target> </project>
JNDI binding • startup browser • http://Localhost:8080 • JMX Console • jboss service=JNDIView • invoke list() and see: • Global JNDI Namespace with: • +- HelloWorldBean (class: org.jnp.interfaces.NamingContext) • +- remote (class: Proxy for: efg.ejb.HelloWorld) • +- remote-efg.ejb.HelloWorld (class: Proxy for: efg.ejb.HelloWorld) • java:comp namespace of the component • jboss.j2ee:jar=efg_ejb3_stateless_helloworld.jar • ,name=HelloWorldBean • ,service=EJB3 : • +- EJBContext (class: javax.ejb.EJBContext) • +- TransactionSynchronizationRegistry[link -> java:TransactionSynchronizationRegistry] • (class: javax.naming.LinkRef) • +- UserTransaction (class: org.jboss.ejb3.tx.UserTransactionImpl) • +- env (class: org.jnp.interfaces.NamingContext) • +- ORB[link -> java:/JBossCorbaORB] (class: javax.naming.LinkRef)
Client, lookup, cast, call package efg.ejb; import javax.naming.InitialContext; public class Main { public static void main(String[] args) throws Exception { InitialContext ctx = new InitialContext(); HelloWorld helloWorld = (HelloWorld)ctx.lookup("HelloWorldBean/remote"); System.out.println(helloWorld.sayHello()); } } jndi.properties java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.provider.url=jnp://localhost:1099
Client Directory Structure client ├─efg │└─efg │└─ejb │├─HelloWorld.class │└─Main.class ├─src │├─HelloWorld.java │└─Main.java ├─log4j.xml ├─jndi.properties └─build.xml
Deployment build.xml(only for TextPad not for Eclips) <project name="HelloWorldClient" basedir="." default="exec" > <property environment="env"/> <property name="jboss.home" value="${env.JBOSS_HOME}"/> <property name="jboss.server" value="${jboss.home}/server/default"/> <path id="j2ee"> <pathelement path="."/> <fileset dir="${jboss.home}/client"> <include name="**/*.jar"/> </fileset> <fileset dir="${jboss.home}/lib"> <include name="**/*.jar"/> </fileset> </path> <target name="compile"> <javac srcdir="src" destdir="."> <include name="**/*.java" /> <classpath refid="j2ee"/> </javac> </target> <target name="exec" depends="compile"> <java classname="efg.ejb.Main" fork="false"> <classpath refid="j2ee"/> </java> </target> </project>
Client, lookup, no interface package efg.ejb; import java.lang.reflect.Method; import javax.naming.InitialContext; public class Main { public static void main(String[] args) throws Exception { String sayHello = "sayHello"; InitialContext ctx = new InitialContext(); Object helloWorld = ctx.lookup("HelloWorldBean/remote"); Class[] param = {}; Method method = helloWorld.getClass().getMethod(sayHello, param); Object[] arg = {}; System.out.println(method.invoke(helloWorld, arg)); } } jndi.properties java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.provider.url=jnp://localhost:1099
Changing the JNDI-Name @Stateless(mappedName="ejb/HelloWorld/mappedName", name="ejb/HelloWorld/name") +- ejb (class: org.jnp.interfaces.NamingContext) | +- HelloWorld (class: org.jnp.interfaces.NamingContext) | | +- name (class: org.jnp.interfaces.NamingContext) | | | +- remote-efg.ejb.HelloWorld (class: Proxy for: efg.ejb.HelloWorld) | | +- mappedName (class: Proxy for: efg.ejb.HelloWorld)
Life cycle Stateless SessionBean? Does Not Exist Class.newinstance() injections @PostConstruct @PreDestroy Method Ready Pool business methods
Showing the Life cycle /*****************************************************************\ Life Cycle Methods \*****************************************************************/ @PostConstruct public void postConstruct() { System.out.println("("+id+")HelloWorldBean.postConstruct()"); } @PreDestroy public void preDestroy() { System.out.println("("+id+")HelloWorldBean.preDestroy()"); }
Interceptors for special jobs(1) package efg.ejb; import javax.ejb.Stateless; import javax.interceptor.Interceptors; @Stateless @Interceptors(MyInterceptor.class) public class HelloWorldBean implements HelloWorld { . . . } Class-declared-Interceptors are invoked, instead of calling a business method. An Interceptor gets the responsibility of calling the next entry in the interceptor chain. Finally the business method is called. Before and after the call special action, like logging, checking authentication and timing can be done.
Interceptors for special jobs(2) package efg.ejb; import javax.interceptor.InvocationContext; import javax.interceptor.AroundInvoke; public class MyInterceptor { @AroundInvoke public Object test(InvocationContext invocationContext) throws Exception { . . . Object ret = invocationContext.proceed(); . . . return ret; } }
Interceptors for special jobs(3) javax.interceptor.InvocationContext Map<String, Object> getContextData() Method getMethod() Object[] getParameters() Object getTarget() Object proceed() void setParameters(Object[] params)
Interceptors for special jobs(2) 15:46:14,775 INFO [STDOUT] (0)HelloWorldBean() 15:46:14,775 INFO [STDOUT] MyInterceptor() 15:46:14,775 INFO [STDOUT] MyInterceptor.test([ target=efg.ejb.HelloWorldBean@1bce9a , method=public java.lang.String efg.ejb.HelloWorldBean.sayHello() , parameters=[] , contextData={} ]) 15:46:14,775 INFO [STDOUT] Method=public java.lang.String efg.ejb.HelloWorldBean.sayHello() 15:46:14,775 INFO [STDOUT] (0)HelloWorldBean.sayHello() 15:46:14,775 INFO [STDOUT] return=HelloWorld!
Injected SessionContext @Resource private SessionContext sessionContext; EJBContext Principal getCallerPrincipal() booleanisCallerInRole(String rolename) Object lookup(String name) TimerServicegetTimerService() booleangetRollbackOnly() void setRollbackOnly() SessionContext
No Annotations ejb-jar.xml(1) <?xml version="1.0" encoding="UTF-8"?> <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" version="3.0" > <enterprise-beans> <session> <ejb-name>HelloWorldSessionBean</ejb-name> <mapped-name>HelloWorldMappedName</mapped-name> <business-remote>efg.ejb.HelloWorld</business-remote> <ejb-class>efg.ejb.HelloWorldBean</ejb-class> <session-type>Stateless</session-type> <remove-method> <bean-method> <method-name>sayHello</method-name> </bean-method> </remove-method> <post-construct> <lifecycle-callback-method>postConstruct</lifecycle-callback-method> </post-construct> <pre-destroy> <lifecycle-callback-method>preDestroy</lifecycle-callback-method> </pre-destroy> </session> </enterprise-beans>
No Annotations ejb-jar.xml(2) <interceptors> <interceptor> <interceptor-class>efg.ejb.MyInterceptor</interceptor-class> <around-invoke> <method-name>test</method-name> </around-invoke> </interceptor> </interceptors> <assembly-descriptor> <security-role> <role-name>beheerder</role-name> </security-role> <security-role> <role-name>klant</role-name> </security-role> <method> <ejb-name>HelloWorldSessionBean</ejb-name> <method-name>sayHello</method-name> </method> </method-permission> <interceptor-binding> <ejb-name>HelloWorldSessionBean</ejb-name> <interceptor-class>efg.ejb.MyInterceptor</interceptor-class> <method> <method-name>sayHello</method-name> </method> </interceptor-binding> </assembly-descriptor> </ejb-jar>
Stateless or Stateful SessionBean? A SLSB has no state: they don’t have properties(global variables). A SLSB is thread safe: multiple clients can share the same SSB. The container instantiates a SLSB only once. The container can hold 100 SLSB’s. see server/default/conf/standardjboss.xml A SFSB has state. A SFSB is connected to one client only. A SFSB must be destroyed if the client timed out. A SFSB needs a complex management model. Don’t use a SFSB if it is not necessary.
Life cycle Stateful SessionBean? Does Not Exist timeout Class.newinstance() injections @PostConstruct Exception @PreDestroy Method Ready Pool business methods @PrePassivate @PostActivate Passive
Annotations • @PostConstruct • @PreDestroy • @PrePassivate • @PostActivate • @EJB • @Resource • @Stateless • @Stateful • @Remote • @Local • @RemoteBinding • @SecurityDomain • @DeclareRoles • @RolesAllowed
Stateless / Stateful package javax.ejb; @Target(TYPE) @Retention(RUNTIME) public @interface Stateless { String description() default ""; String mappedName() default ""; String name() default <ejb-name>; } package javax.ejb; @Target(TYPE) @Retention(RUNTIME) public @interface Stateful { String description() default ""; String mappedName() default ""; String name() default <ejb-name>; }
Remote / Local package javax.ejb; @Target(TYPE) @Retention(RUNTIME) public @interface Remote { Class[] value default {}; } package javax.ejb; @Target(TYPE) @Retention(RUNTIME) public @interface Local { Class[] value default {}; } If the SessionBean has no Remote or Local annotations and the SessionBean implements a business interface, that interface is by default a Local business interface. A default global JNDI binding is created. <ejb-name>/remote <ejb-name>/local
Life Cycle package javax.annotation; @Documented @Target(METHOD) @Retention(RUNTIME) public @interface PostConstruct package javax.annotation; @Documented @Target(METHOD) @Retention(RUNTIME) public @interface PreDestroy package javax.ejb; @Target(METHOD) @Retention(RUNTIME) public @interface PostActivate package javax.ejb; @Target(METHOD) @Retention(RUNTIME) public @interface PrePassivate
Security package javax.annotation.security; @Documented @Target(TYPE) @Retention(RUNTIME) public @interface DeclareRoles package javax.annotation.security; @Documented @Target(TYPE, METHOD) @Retention(RUNTIME) public @interface RolesAllowed
EJB reference package javax.ejb; @Target(TYPE, METHOD, FIELD) @Retention(RUNTIME) public @interface EJB { String description default ””; Class beanInterface default Object.class; String beanName default ””; String mappedName default ””; String name default ””; } If mappedName is used: it is the global JNDI name of the EJB you are referencing, and all other attributes are ignored. If beanName is used: the container searches for the beanName as <ejb-link> , and all other attributes are ignored. If no attributes are used: the container searches for the declarated bean interface, and throws an Exception if duplicates are found.
Resource reference package javax.annotation; @Target(TYPE, METHOD, FIELD) @Retention(RUNTIME) public @interface Resource { String description default ””; Class type default Object.class; String name default ””; String mappedName default ””; boolean sharable default true; Resource.AuthenticationType authentication default CONTAINER; }
Questions • The interface HelloWorld in the client is a copy of the HelloWorld interface in the Server. Is it still the same interface if the packages are different? Is it still the same interface if the classnames are different? • A session bean has a local interface. Why is this session bean unreachable from the outside?