Introduction
This quick EJB lesson doesn't explain why you should or shouldn't use EJBs, nor does it talk about when to use them and when not to, nor does it even explain any of the concepts behind the different EJB components. This guide is simply to help someone see how to put together a working EJB 2.1 application using JBoss as the Application server.

To be consistent with many of the other examples, the front end is built using the Employee Struts application that is used elsewhere on this site. (The Struts app is 1.x not 2.x). The front end of choice doesn't matter at all so if you haven't even seen a Struts application it won't matter. Where you see the "EmployeeAction" class in the source code that calls our EJB layer, just think of a Servlet (which is all a Struts Action actually is).

This example is using Local EJB calls. Everything, including the war is placed in an ear and deployed on one instance of JBoss. If you want an example of making remote EJB calls then see the EJB 2.1 - Remote

Without further ado, lets get started...
The Setup
Download the source There is too much in this application to do a complete walk through of every single component. Best thing to do is download the source above and unzip it into your project directory of choice. For eclipse users it comes with an eclipse project so you can just import that project file and be good to go. To build using ant, type 'ant' and the project will build and deploy to the default server on JBoss.

Java5 The Java5 JDK needs to be set as your environment. JAVA_HOME env variable needs to be defined pointing to yth the JDK home directory.

JBoss 4.x - Download JBoss 4.x here http://labs.jboss.com/portal/jbossas/download. Since you'll be probably be coding with the EJB 3.0 spec soon enough, set up JBoss4.x using the installer as described on the download page. After JBoss is installed declare a JBOSS_HOME environment variable pointing to the JBoss root directory.

Using the build.xml files To run the builds you will need ant installed. Running "ant all" from the root of the project directory will build the entire project. Use can just run "ant deploy" which will do all plus deploy the ear to jboss (in default). Each of the three modules (common, ejb, and webapp, have their own build files even though some of them are dependent on each other.)

Running the example You can drop the above ear file into your jboss/default/deploy directory or build from the source code with ant and use 'ant deploy.' Once the ear is deployed, start jboss from the jboss/bin directory with 'run.bat' or 'run.sh' depending on your OS. Once jboss has started, go the following url: http://localhost:8080/rr-employee-allinone/

A Note About XDoclet
XDoclet reduces the amount of annoying code you have to write when dealing with EJB 2.1 by probably 90%. I didn't use XDoclet here since I wanted to show all the steps you'd have go through to work with EJBs when not using XDoclet. Using EJB3 things become even more simplified with the use of annotations and you don't even need to use XDoclet.
The Persistence Layer
Our EJBs will simply call a service class that in turn calls our dao to handle our CRUD (create, retrieve, update, delete) operations. I could have just left the service class out and had the EJBs call the daos directly, but my personal preference is to have one extra layer of abstraction there just in case I need to do any non-dao related stuff that still belongs in a common area. The CRUD Employee dao in this example does nothing more than work with an in-memory list of items.

Our EmployeeService class looks like:

public class EmployeeService {
    private static EmployeeDao dao = new EmployeeSimpleDao();

    public static List<Employee> getAllEmployees() {
        return dao.getAllEmployees();
    }

    public static void updateEmployee(Employee emp) {
        dao.update(emp);
    }

    public static void deleteEmployee(Integer id) {
        dao.delete(id);
    }

    public static Employee getEmployee(Integer id) {
        return dao.getEmployee(id);
    }

    public static void insertEmployee(Employee emp) {
        dao.insert(emp);
    }
}
EmployeeFacade
This is our EJB that has the actual business method implementations (our calls to our service/persistence layer). I followed the Sun convention and the one used in the "JBoss At Work" book and called all our object a Facade since our EJB acts as a Stateless Session Facade object http://java.sun.com/blueprints/corej2eepatterns/Patterns/SessionFacade.html - simply the object that exposes our business methods for our client to use.


public class EmployeeFacadeBean implements SessionBean {

    private SessionContext sessionCtx;

    public void setSessionContext(SessionContext sessionCtx) throws EJBException {
        this.sessionCtx = sessionCtx;
    }

    public void ejbCreate() throws CreateException {}
    public void ejbRemove() throws EJBException {}
    public void ejbActivate() throws EJBException {}
    public void ejbPassivate() throws EJBException {}

    public List<Employee> getAllEmployees() throws EJBException {
        return EmployeeService.getAllEmployees();
    }

    public void updateEmployee(Employee emp)throws EJBException  {
        EmployeeService.updateEmployee(emp);
    }

    public void deleteEmployee(Integer id) throws EJBException  {
        EmployeeService.deleteEmployee(id);
    }

    public  Employee getEmployee(Integer id) throws EJBException  {
        return EmployeeService.getEmployee(id);
    }

    public void insertEmployee(Employee emp) throws EJBException  {
        EmployeeService.insertEmployee(emp);
    }
}

TODO: document the code snippets that follow...
EmployeeFacadeLocal
import java.util.List;
import javax.ejb.EJBException;
import javax.ejb.EJBLocalObject;

import net.reumann.common.vo.Employee;

public interface EmployeeFacadeLocal extends EJBLocalObject {
    public List<Employee> getAllEmployees() throws EJBException;

    public void updateEmployee(Employee emp) throws EJBException;

    public void deleteEmployee(Integer id) throws EJBException;

    public Employee getEmployee(Integer id) throws EJBException;

    public void insertEmployee(Employee emp) throws EJBException;
}
EmployeeFacadeLocalHome

import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;

public interface EmployeeFacadeLocalHome extends EJBLocalHome {
    public static final String COMP_NAME = "java:comp/env/ejb/EmployeeFacadeLocal";
    public static final String JNDI_NAME = "EmployeeFacadeLocal";

    public EmployeeFacadeLocal create() throws CreateException;
}
ejb/META-INF/ejb-jar.xml

<?xml version="1.0" encoding="UTF-8"?>

<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
	version="2.1">

	<description>ejb-jar.xml for ejb 2.1 Local tutorial</description>
	<display-name>ejb-jar.xml</display-name>

	<enterprise-beans>
		<!-- Session Beans -->
		<session>
			<description>EmployeeFacade Session Bean</description>
			<display-name>EmployeeFacade Session Bean</display-name>
			<ejb-name>EmployeeFacade</ejb-name>
			<home>net.reumann.ejb.EmployeeFacadeRemoteHome</home>
			<remote>net.reumann.ejb.EmployeeFacadeRemote</remote>
			<local-home>
				net.reumann.ejb.EmployeeFacadeLocalHome
			</local-home>
			<local>net.reumann.ejb.EmployeeFacadeLocal</local>
			<ejb-class>net.reumann.ejb.EmployeeFacadeBean</ejb-class>
			<session-type>Stateless</session-type>
			<transaction-type>Container</transaction-type>
		</session>
	</enterprise-beans>

	<assembly-descriptor>
		<container-transaction>
			<method>
				<ejb-name>EmployeeFacade</ejb-name>
				<method-intf>Local</method-intf>
				<method-name>getAllEmployees</method-name>
				<method-params></method-params>
			</method>
			<trans-attribute>Required</trans-attribute>
		</container-transaction>
		<container-transaction>
			<method>
				<ejb-name>EmployeeFacade</ejb-name>
				<method-intf>Local</method-intf>
				<method-name>updateEmployee</method-name>
				<method-params>
					<method-param>net.reumann.common.vo.Employee</method-param>
				</method-params>
			</method>
			<trans-attribute>Required</trans-attribute>
		</container-transaction>
		<container-transaction>
			<method>
				<ejb-name>EmployeeFacade</ejb-name>
				<method-intf>Local</method-intf>
				<method-name>deleteEmployee</method-name>
				<method-params>
					<method-param>java.lang.Integer</method-param>
				</method-params>
			</method>
			<trans-attribute>Required</trans-attribute>
		</container-transaction>
	....	
	</assembly-descriptor>

</ejb-jar>
ejb/META-INF/jboss.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd">
<jboss>
	<enterprise-beans>
		<session>
			<ejb-name>EmployeeFacade</ejb-name>
			<jndi-name>EmployeeFacadeRemote</jndi-name>
			<local-jndi-name>EmployeeFacadeLocal</local-jndi-name>
			<method-attributes></method-attributes>
		</session>
	</enterprise-beans>
	<assembly-descriptor></assembly-descriptor>
	<resource-managers></resource-managers>
</jboss>
common package ServiceLocator

public class ServiceLocator {
  ...
  public static EJBLocalHome getEjbLocalHome(String localHomeJndiName) throws ServiceLocatorException {
        EJBLocalHome localHome = null;
        try {
            Context ctx = new InitialContext();
            localHome = (EJBLocalHome) ctx.lookup(localHomeJndiName);
        } catch (ClassCastException cce) {
            throw new ServiceLocatorException(cce);
        } catch (NamingException ne) {
            throw new ServiceLocatorException(ne);
        }
        return localHome;
    }
   ...
}
webapp pacakge - example call to an EJB in a Servlet
*Note: For a real-life application is probably best to put these ejb calls in some kind of other layer outside your serlvet. This will make it easier to change around whether you need to make local EJB calls or remote ones.

EmployeeFacadeLocalHome employeeHome = 
    (EmployeeFacadeLocalHome) ServiceLocator.getEjbLocalHome(EmployeeFacadeLocalHome.COMP_NAME);
EmployeeFacadeLocal employeeEJB = employeeHome.create();
List<Employee> employees = employeeEJB.getAllEmployees();
webapp package - WEB-INF/web.xml
Relevant portion to EJBs in web.xml, these EJB refs come at the end of the file after any context-param definitions.

<ejb-local-ref>
	<ejb-ref-name>ejb/EmployeeFacadeLocal</ejb-ref-name>
	<ejb-ref-type>Session</ejb-ref-type>
	<local-home>EmployeeFacadeLocalHome</local-home>
	<local>EmployeeFacadeLocal</local>
</ejb-local-ref>
webapp package - WEB-INF/jboss-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.4//EN"
 "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd">

<jboss-web>
	<ejb-local-ref>
		<ejb-ref-name>ejb/EmployeeFacadeLocal</ejb-ref-name>
		<local-jndi-name>EmployeeFacadeLocal</local-jndi-name>
	</ejb-local-ref>
	<ejb-local-ref>
		<ejb-ref-name>ejb/DepartmentFacadeLocal</ejb-ref-name>
		<local-jndi-name>DepartmentFacadeLocal</local-jndi-name>
	</ejb-local-ref>
</jboss-web>
</code></pre></div>
ear application.xml file

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/j2ee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                                 http://java.sun.com/xml/ns/j2ee/application_1_4.xsd"
             version="1.4">
    <display-name>rr-employee-allinone EAR</display-name>
    <module>
        <web>
            <web-uri>rr_employee-webapp.war</web-uri>
            <context-root>rr-employee-allinone</context-root>
        </web>
    </module>
    <module>
        conf.jar
    </module>    
    <module>
        <java>common.jar</java>
    </module>
    <module>
        <java>persistence.jar</java>
    </module>
    <module>
        <ejb>employee-ejb.jar</ejb>
    </module>
</application>
</code></pre></div>
Code and Lesson - Rick Reumann