Java EE Exercise

Part G: Handling WAR/EJB Interface Issues

This exercise will step through a few issues that can be encountered while developing a more rich Web/EJB interface.

Objectives

Add Web UI Use Case to EJB

We will want to add a use case to the Web UI that allows us to

  • getAllPeople in page-sized quantities
  • get and display a specific person with address information
  • change the address information for that user

The RMI Test has already been testing getPeopleByName() and now we want to add a simple getAllPeople that takes indexing and count properties that could be used in a paging function. We don't need to worry about hydrating at this point because we only want the core person properties.

  1. Add the getAllPeople() method to the @Remote interface.
    $ cat ./javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarRemote.java
    
    @Remote
    public interface RegistrarRemote {
    ...
        Collection<Person> getAllPeople(int index, int count)
            throws RegistrarException;
  2. Add the getAllPeople() method to the @Stateless EJB implementation.
    $ cat javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarEJB.java
    
    ...
        public Collection<Person> getAllPeople(int index, int count)
                throws RegistrarException {
            log.debug(String.format("*** getAllPeople(index=%d, count=%d) ***", index, count));
    
            try {
                return registrar.getAllPeople(index, count);
            }
            catch (Throwable ex) {
                log.error(ex);
                throw new RegistrarException(ex.toString());
            }
        }
  3. Add a test of the getAllPeople to the RMI Test before we attempt to make use of it within the WAR. This test will create PAGES*PAGE_SIZE count of people and then request the EJB get thos people a page at a time. The final count of all people retrieved is evaluated against the expected count.
    $ cat javaeeExTest/src/test/java/myorg/javaeeex/ejbclient/RegistrarIT.java
    
    ...
        @Test
        public void testWebUseCase() throws Exception {
            log.info("*** testWebUserCase ***");
            final int PAGE_SIZE=24;
            final int PAGES=4;
            final int TOTAL = PAGES*PAGE_SIZE;
            for(int i=0; i<TOTAL; i++) {
                Person person = makePerson();
                person.setFirstName("first" + i);
                person.setLastName("last" + i);
                registrar.createPerson(person);
            }
            
            int index = 0;
            int count = 0;
            int pages = 0;
            int loops = 0;
            Collection<Person> people = null;
            do {
                people = registrar.getAllPeople(index, PAGE_SIZE);
                pages = (!people.isEmpty()) ? pages + 1 : pages;
                count += people.size();
                index += people.size();
                loops += 1;
            } while (!people.isEmpty() && loops <= PAGES + 1);
            
            assertEquals("unexpected # of pages", PAGES, pages);
            assertEquals("unexpected number of people", TOTAL, count);
        }
  4. Build, deploy, and test the new method from the RMI Test.
    $ mvn clean install -rf :javaeeExEJB
    ...
    
     -*** testWebUserCase ***
    Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.466 sec
    
    Results :
    
    Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
    
    ...
    [INFO] Java EE Exercise EJB .............................. SUCCESS [5.481s]
    [INFO] Java EE Exercise WAR .............................. SUCCESS [2.841s]
    [INFO] Java EE Exercise EAR .............................. SUCCESS [1.530s]
    [INFO] Java EE Exercise Remote Test ...................... SUCCESS [25.191s]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
  5. Now add some extra getters of the person information within the existing test method.
    $cat javaeeExTest/src/test/java/myorg/javaeeex/ejbclient/RegistrarIT.java
    
    
                loops += 1;
                for (Person person : people) {
                    log.debug(String.format("person: (%d) %s %s", 
                        person.getId(), person.getFirstName(), person.getLastName()));
                }
            } while (!people.isEmpty() && loops <= PAGES + 1);
  6. Build and run the new form of the test.
    $ mvn clean install -rf :javaeeExEJB
    
    ...
    
     -testUtil.resetAll() complete
     -*** testWebUserCase ***
     -person: (1) first0 last0
     -person: (2) first1 last1
     -person: (3) first2 last2
    
    ...
    
     -person: (93) first92 last92
     -person: (94) first93 last93
     -person: (95) first94 last94
     -person: (96) first95 last95
    Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.421 sec
  7. Attempt a deeper processing of the people returned.
    $ cat javaeeExTest/src/test/java/myorg/javaeeex/ejbclient/RegistrarIT.java
    
    ...
                log.debug("people=" + people);
            } while (!people.isEmpty() && loops <= PAGES + 1);   
  8. Re-run the deployment
     -person: (24) first23 last23
     Tests run: 6, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.359 sec <<< FAILURE!
    
     Results :
    
     Tests in error: 
       testWebUseCase(myorg.javaeeex.ejbclient.RegistrarIT): failed to lazily initialize a collection of role: myorg.javaeeex.bo.Person.addresses, no
       session or session was closed
    
       Tests run: 6, Failures: 0, Errors: 1, Skipped: 0
    
       ...
       [INFO] Java EE Exercise EJB .............................. SUCCESS [6.483s]
       [INFO] Java EE Exercise WAR .............................. SUCCESS [2.300s]
       [INFO] Java EE Exercise EAR .............................. SUCCESS [1.392s]
       [INFO] Java EE Exercise Remote Test ...................... FAILURE [12.783s]
       [INFO] ------------------------------------------------------------------------
       [INFO] BUILD FAILURE
  1. Take a look at the fasilsafe results.
    ./javaeeExTest/target/failsafe-reports/myorg.javaeeex.ejbclient.RegistrarIT.txt
     -------------------------------------------------------------------------------
    Test set: myorg.javaeeex.ejbclient.RegistrarIT
     -------------------------------------------------------------------------------
    Tests run: 6, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.359 sec <<< FAILURE!
    testWebUseCase(myorg.javaeeex.ejbclient.RegistrarIT)  Time elapsed: 0.98 sec  <<< ERROR!
    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: myorg.javaeeex.bo.Person.addresses, no session or session 
    was closed
            at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:393)
            ...
            at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:266)
            at myorg.javaeeex.bo.Person.toString(Person.java:55)
            at java.lang.String.valueOf(String.java:2838)
            ...
            at java.lang.StringBuilder.append(StringBuilder.java:132)
            at myorg.javaeeex.ejbclient.RegistrarIT.testWebUseCase(RegistrarIT.java:215)
    1. Add a hydrated form of the getAllPeople. Leverage the hydratePerson(Person) helper method that was implemented in a previous part of this overall exercise.
      $ cat ./javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarRemote.java
      
      ...
      @Remote
      public interface RegistrarRemote {
         ...
      
          Collection<Person> getAllPeople(int index, int count)
              throws RegistrarException;
          Collection<Person> getAllPeopleHydrated(int index, int count)
              throws RegistrarException;
      $ cat javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarEJB.java
      
      ...
      @Stateless
      public class RegistrarEJB implements RegistrarLocal, RegistrarRemote {
         ...
      
          public Collection<Person> getAllPeopleHydrated(int index, int count)
              throws RegistrarException {
              log.debug(String.format("*** getAllPeopleHydrated(index=%d, count=%d) ***", index, count));
      
              try {
                  Collection<Person> people = registrar.getAllPeople(index, count);
                  for (Person person : people) {
                      hydratePerson(person);
                  }
                  return people;
              }
              catch (Throwable ex) {
                  log.error(ex);
                  throw new RegistrarException(ex.toString());
              }
          }
    2. Update the RMI Test to use the hydrated form.
      $ cat javaeeExTest/src/test/java/myorg/javaeeex/ejbclient/RegistrarIT.java
      
      ...
              do {
                  people = registrar.getAllPeopleHydrated(index, PAGE_SIZE);
    3. Rebuild/test the new version of the application. Notice how the objects returned from the EJB tier are now fully hydrated and do not cause a lazy load exception.
      $ mvn clean install -rf :javaeeExEJB
      
      
      ...
       -person: (96) first95 last95
       -people=[id=73:first72 last72 123, addresses={{street1 city1, state1 zip1},}, id=74:first73 last73 123, addresses={{str
      ...
      
      1, state1 zip1},}, id=94:first93 last93 123, addresses={{street1 city1, state1 zip1},}, id=95:first94 last94 123, addres
      ses={{street1 city1, state1 zip1},}, id=96:first95 last95 123, addresses={{street1 city1, state1 zip1},}]
       -people=[]
       -person: (1) first0 last0
       -address: (1) street1 city1, state1 zip1
       -person: (1) first0 last0
       -address: (97) street12 city12, state12 zip12
      Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.504 sec
      
      Results :
      
      Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
      
      ...
      [INFO] Java EE Exercise EJB .............................. SUCCESS [5.259s]
      [INFO] Java EE Exercise WAR .............................. SUCCESS [2.834s]
      [INFO] Java EE Exercise EAR .............................. SUCCESS [1.138s]
      [INFO] Java EE Exercise Remote Test ...................... SUCCESS [24.765s]
      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD SUCCESS
    4. Add the getPersonById() for the next phase of the Web UI use case. Do this after the paging tests.
      $ cat javaeeExTest/src/test/java/myorg/javaeeex/ejbclient/RegistrarIT.java
      
              ...
              assertEquals("unexpected number of people", TOTAL, count);
              
              //view a specific user
              long id = registrar.getAllPeople(0, PAGE_SIZE).iterator().next().getId();
              Person person = registrar.getPersonById(id);
              log.debug(String.format("person: (%d) %s %s", 
                  person.getId(), person.getFirstName(), person.getLastName()));
              Address address = person.getAddresses().iterator().next();
              log.debug(String.format("address: (%d) %s %s, %s %s", 
                  address.getId(), 
                  address.getStreet(),
                  address.getCity(), address.getState(), address.getZip()));
    5. Build and run the new getPersonById() functionality within the overall Web UI usecase. Expect a lazy load exception to occur for accessing the address returned.
      $ mvn clean install -rf :javaeeExTest
      
      ...
      Tests run: 6, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 1.437 sec FAILURE!
      
      Results :
      
      Tests in error:
        testWebUseCase(myorg.javaeeex.ejbclient.RegistrarIT): failed to lazily initialize a collection of role: myorg.javaeeex.bo.Person.addresses, no session or session was closed
      
      
      Tests run: 6, Failures: 0, Errors: 1, Skipped: 0
      
      [INFO] ------------------------------------------------------------------------
      [ERROR] BUILD FAILURE
      [INFO] ------------------------------------------------------------------------
      [INFO] There are test failures.
      ./javaeeExTest/target/failsafe-reports/myorg.javaeeex.ejbclient.RegistrarIT.txt
      ::::::::::::::
       -------------------------------------------------------------------------------
      Test set: myorg.javaeeex.ejbclient.RegistrarIT
       -------------------------------------------------------------------------------
      Tests run: 6, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.356 sec <<< FAILURE!
      testWebUseCase(myorg.javaeeex.ejbclient.RegistrarIT)  Time elapsed: 1.111 sec  <<< ERROR!
      org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: myorg.javaeeex.bo.Person.addresses, no session or session 
      was closed
              at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:393)
          ...
              at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:266)
              at myorg.javaeeex.ejbclient.RegistrarIT.testWebUseCase(RegistrarIT.java:226)
      
    6. Add a new form of the getPersonById() that hydrates the Person before returning them. Leave the former one in-tact so we can use it later to demonstrate the differences between the two forms.
      $ cat javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarRemote.java
      
      @Remote
      public interface RegistrarRemote {
          ...
      
          Person getPersonById(long id)
              throws RegistrarException;
          Person getPersonByIdHydrated(long id)
              throws RegistrarException;
      $cat ./javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarEJB.java
      
      ...
          public Person getPersonByIdHydrated(long id) throws RegistrarException {
              log.debug("*** getPersonByIdHydrated() ***");
      
              try {
                  Person person = registrar.getPersonById(id);
                  hydratePerson(person);
                  return person;
              }
              catch (Throwable ex) {
                  log.error(ex);
                  throw new RegistrarException(ex.toString());
              }
          }
    7. Update the RMI Test RMI Test to use the new hydrated form.
      $ cat javaeeExTest/src/test/java/myorg/javaeeex/ejbclient/RegistrarIT.java
      
      ...
              //view a specific user
              long id = registrar.getAllPeople(0, PAGE_SIZE).iterator().next().getId();
              Person person = registrar.getPersonByIdHydrated(id);
    8. Build and run the new implementation.
      $ mvn clean install -rf :javaeeExEJB
      
      ...
      
       -person: (1) first0 last0
       -address: (1) street1 city1, state1 zip1
      Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.501 sec
      
      Results :
      
      Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
      
      ...
      [INFO] Java EE Exercise EJB .............................. SUCCESS [4.890s]
      [INFO] Java EE Exercise WAR .............................. SUCCESS [2.655s]
      [INFO] Java EE Exercise EAR .............................. SUCCESS [1.114s]
      [INFO] Java EE Exercise Remote Test ...................... SUCCESS [24.296s]
      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD SUCCESS
    9. Add a method to changeAddress for a person.
      $ cat javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarRemote.java
      
      ...
      import myorg.javaeeex.bo.Address;
      
      @Remote
      public interface RegistrarRemote {
          ...
      
          Person getPersonById(long id)
              throws RegistrarException;
          Person getPersonByIdHydrated(long id)
              throws RegistrarException;
          
          Person changeAddress(Person person, Address address)
              throws RegistrarException;
      $ cat javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarEJB.java
      
      ...
      @Stateless
      public class RegistrarEJB implements RegistrarLocal, RegistrarRemote {
      ...
          public Person changeAddress(Person person, Address address)
                  throws RegistrarException {
              log.debug("*** changeAddress() ***");
      
              try {
                  return registrar.changeAddress(person, address);
              }
              catch (Throwable ex) {
                  log.error(ex);
                  throw new RegistrarException(ex.toString());
              }       
          }
    10. Update the testWebUseCase() RMI Test method to change the address of the person.
      $ cat javaeeExTest/src/test/java/myorg/javaeeex/ejbclient/RegistrarIT.java
      
      ...
              //update the address of a specific user
              Address address2 = new Address();
              address2.setStreet(address.getStreet() + 2);
              address2.setCity(address.getCity() + 2);
              address2.setState(address.getState() + 2);
              address2.setZip(address.getZip() + 2);
              Person p3 = registrar.changeAddress(person, address2);
              Address a3 = p3.getAddresses().iterator().next();
              assertEquals("unexpected street" , address2.getStreet(), a3.getStreet());
              assertEquals("unexpected city" , address2.getCity(), a3.getCity());
              assertEquals("unexpected state" , address2.getState(), a3.getState());
              assertEquals("unexpected zip" , address2.getZip(), a3.getZip());
    11. Update the RMI Test method to report the changes of the person.
      $ cat javaeeExTest/src/test/java/myorg/javaeeex/ejbclient/RegistrarIT.java
      
      ...
              //view user again
              person = registrar.getPersonByIdHydrated(id);
              log.debug(String.format("person: (%d) %s %s", 
                  person.getId(), person.getFirstName(), person.getLastName()));
              address = person.getAddresses().iterator().next();
              log.debug(String.format("address: (%d) %s %s, %s %s", 
                  address.getId(), 
                  address.getStreet(),
                  address.getCity(), address.getState(), address.getZip()));
    12. Build and deploy the new implementation.
       -person: (1) first0 last0
       -address: (1) street1 city1, state1 zip1
       -person: (1) first0 last0
       -address: (97) street12 city12, state12 zip12
      Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.313 sec
      
      Results :
      
      Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
      
      ...
      [INFO] Java EE Exercise EJB .............................. SUCCESS [5.834s]
      [INFO] Java EE Exercise WAR .............................. SUCCESS [2.136s]
      [INFO] Java EE Exercise EAR .............................. SUCCESS [1.946s]
      [INFO] Java EE Exercise Remote Test ...................... SUCCESS [13.410s]
      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD SUCCESS

      We now are at a point where we now have new methods needed by the web-tier that have been tested by the RMI integration tests.

Add the Web UI Use Case to the WAR using Remote interface

We will start the transition into the WAR using the remote interface of the EJB. This will allow us to implement most of the initial implementation within the client-side Jetty framework.

  1. Add an action/handler class to provide the getAllPeople() functionality. Since the RMI methods are defined to throw Exception we need to also update the doHandle() method to allow Exception to also be thrown from derived classes.
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
        private abstract class Handler {
            ...
            protected static final String INDEX_PARAM = "index";
            protected static final String COUNT_PARAM = "count";
            ...
            public abstract void doHandle(HttpServletRequest request, 
                    HttpServletResponse response) 
                    throws ServletException, IOException, Exception;
            ...
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
    import java.util.Collection;
    ...
    import myorg.javaeeex.bo.Person;
    ...
        private class GetAllPeople extends Handler {
            public void doHandle(HttpServletRequest request, 
                    HttpServletResponse response) 
                    throws Exception {
                action = "EJB.getAllPeople"; //describe action in case of exception
                
                String indexStr = (String)request.getParameter(INDEX_PARAM);
                String countStr = (String)request.getParameter(COUNT_PARAM);
                int index = Integer.parseInt(indexStr);
                int count = Integer.parseInt(countStr);
                
                Collection<Person> people = registrar.getAllPeopleHydrated(index, count);
    
                    
                request.setAttribute(RESULT_PARAM, people);                
                RequestDispatcher rd = 
                  getServletContext().getRequestDispatcher(DISPLAY_RESULT_URL);
                rd.forward(request, response);                
            }
        }
  2. Add this option to the ADMIN handlers.
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
    public class RegistrarHandlerServlet extends HttpServlet {
    
    ...
        public static final String GET_ALL_PEOPLE_COMMAND = "Get All People";
    ...
    
        public void init() throws ServletException {
    ...
                if (ADMIN_TYPE.equals(handlerType)) {               
                    handlers.put(GET_ALL_PEOPLE_COMMAND, new GetAllPeople());
                } 
    ...
  3. Add a way to invoke the command from the initial menu. Please this request in an admin-specific sub-menu.
    $ mkdir -p javaeeExWAR/src/main/webapp/admin
    $ cat javaeeExWAR/src/main/webapp/admin/admin_menu.jsp
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
                "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
        <title>JavaEE Exercise Admin Menu</title>
    </head>
    <body>
        <h2>Hello JavaEE Admin User</h2>
    
        <ul>
            <li><a href="../model/admin/handler?command=Get+All+People&index=0&count=10">Get All People</a></li>
        </ul>
    </body>
    </html>
  4. Add navigation to the admin_menu from the root menu.
    Note:
    admin_menu.jsp is mis-typed as admin-menu.jsp on purpose. It is being done to show you one of the errors you might encounter when building your web tier.
    $ cat javaeeExWAR/src/main/webapp/index.jsp
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
                "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
        <title>JavaEE Exercise Main Page</title>
    </head>
    <body>
        <h2>Hello JavaEE Controller</h2>
    
        <ul>
            <li><a href="model/admin/handler">invoke servlet</a></li>
            <li><a href="model/admin/handler?command=Ping">invoke admin EJB.ping()</a></li>
            <li><a href="model/handler?command=Ping">invoke anonymous EJB.ping()</a></li>
            <li><a href="admin/admin-menu.jsp">Admin</a></li>
        </ul>
    </body>
    </html>
  5. Test your local implementation using the Jetty container. Make sure the EAR has been deployed to the server and the integration tests have run at least once to provide data.
    • http://localhost:9080
    • click on /javaeeExWAR
    • click on Admin
    • click Get All People
      $ mvn clean integration-test -rf :javaeeExTest)
      $ (cd javaeeExWAR; mvn clean jetty:run)
      HTTP ERROR: 404
      
      NOT_FOUND
      
      RequestURI=/javaeeExWAR/admin/admin-menu.jsp
      
      Powered by Jetty://
  6. Correct the error with the URI in the main menu. This will fail for a separate reason.
    • Hit save for the main menu in the editor.
    • Refresh the main menu in the browser
    • Re-click again on Admin
      <li><a href="admin/admin_menu.jsp">Admin</a></li>
      Hello JavaEE Admin User
      
          * Get All People
       -error in EJB.getAllPeople
      java.lang.reflect.UndeclaredThrowableException
              at $Proxy16.getAllPeopleHydrated(Unknown Source)
              at myorg.javaeeex.web.RegistrarHandlerServlet$GetAllPeople.doHandle(RegistrarHandlerServlet.java:186)
              at myorg.javaeeex.web.RegistrarHandlerServlet$Handler.handle(RegistrarHandlerServlet.java:145)
              at myorg.javaeeex.web.RegistrarHandlerServlet.doGet(RegistrarHandlerServlet.java:98)
      ...
              at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
      Caused by: java.lang.ClassNotFoundException: org.hibernate.collection.internal.PersistentBag
  7. The same error occured in an earlier part of the RMI Test exercise. The problem is the List returned comes straight from Hibernate and Hibernate is not brought in as a dependency in the ejava.common jboss-rmi-client. We could have fixed the problem for all clients if we took the time to create a vanilla POJO DTO -- but instead we chose to take the shortcut and add hibernate to the client's classpath. We will take that same lazy workaround here as well. Add the following dependency to the WAR's jetty plugin dependency definition. It could also go in the parent jetty pluginManagement if we wanted to take this same work-around for all WARs we create.
    $ cat javaeeExWAR/pom.xml
    
            <plugins>
                <plugin>
                    <groupId>org.mortbay.jetty</groupId>
                    <artifactId>maven-jetty-plugin</artifactId>
                    <dependencies>
                        ...
                        <dependency>
                            <groupId>org.hibernate</groupId>
                            <artifactId>hibernate-core</artifactId>
                            <version>${hibernate-entitymanager.version}</version>
                        </dependency>
  8. Re-test your local implementation using Jetty. Kill and restart the Maven process since we have changed the plugin dependency definition. The call should work this time.
    • http://localhost:9080
    • click on /javaeeExWAR
    • click on Admin
    • click Get All People
      $ (cd javaeeExWAR; mvn clean jetty:run)
      Result: [id=1:first0 last0 123, addresses={{street12 city12, state12 zip12},}, id=2:first1 last1 123, addresses={{street1 city1, state1 zip1},}, id=3:first2 last2 123, addresses={{street1 city1, state1 zip1},}, id=4:first3 last3 123, addresses={{street1 city1, state1 zip1},}, id=5:first4 last4 123, addresses={{street1 city1, state1 zip1},}, id=6:first5 last5 123, addresses={{street1 city1, state1 zip1},}, id=7:first6 last6 123, addresses={{street1 city1, state1 zip1},}, id=8:first7 last7 123, addresses={{street1 city1, state1 zip1},}, id=9:first8 last8 123, addresses={{street1 city1, state1 zip1},}, id=10:first9 last9 123, addresses={{street1 city1, state1 zip1},}]
      
      Go to Main Page 
  9. Create a JSP specifically to handle the display of people in page increments.
    $ cat javaeeExWAR/src/main/webapp/WEB-INF/content/DisplayPeople.jsp
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
                "http://www.w3.org/TR/html4/strict.dtd">
    
    <jsp:directive.page errorPage="/WEB-INF/content/ErrorPage.jsp"/>
    <jsp:directive.page import="java.util.*"/>
    <jsp:directive.page import="myorg.javaeeex.bo.*"/>
    <html>
        <title>People Display</title>
        <body>
            <h2>People Display</h2>
    
            <jsp:scriptlet>
                List people = (List)request.getAttribute("result");
                int index = ((Integer)request.getAttribute("index")).intValue();
                int count = ((Integer)request.getAttribute("count")).intValue();
                int nextIndex = ((Integer)request.getAttribute("nextIndex")).intValue();
                String handler = request.getContextPath() + "/model/admin/handler";
            </jsp:scriptlet>
    
            <ul>
                <jsp:scriptlet>
                    for(Object o: people) {
                        Person p = (Person)o;
                        String firstName = p.getFirstName();
                        String lastName = p.getLastName();
                </jsp:scriptlet>
                    <li><%= firstName %> <%= lastName %></li>
                <jsp:scriptlet>
                    }
                </jsp:scriptlet>
            </ul>
    
            <form method="GET"
                action="<%=request.getContextPath()%>/model/admin/handler">
                Index: <%= index %><p/>
                Count: <%= count %><p/>
                <input type="hidden" name="index" value="<%= nextIndex %>"/>
                <input type="hidden" name="count" value="<%= count %>"/>
                <input type="submit" name="command" value="Get All People"/>
            </form>
    
            <p/><a href="<%=request.getContextPath()%>/index.jsp">Go to Main Page</a>
        </body>
    </html>
  10. Update the Handler in the servlet to re-direct the output to the new JSP.
        private abstract class Handler {
    ...
            protected static final String NEXT_INDEX_PARAM = "nextIndex";
            protected static final String DISPLAY_PEOPLE_URL = 
                "/WEB-INF/content/DisplayPeople.jsp";
    ...
        private class GetAllPeople extends Handler {
            public void doHandle(HttpServletRequest request, 
                    HttpServletResponse response) 
                    throws Exception {
    ...
    
                Collection<Person> people = registrar.getAllPeopleHydrated(index, count);
                int nextIndex = (people.size()==0) ?
                    index : index + people.size();
    
                //request.setAttribute(RESULT_PARAM, people);
                request.setAttribute(RESULT_PARAM, people);
                request.setAttribute(INDEX_PARAM, index);
                request.setAttribute(COUNT_PARAM, count);
                request.setAttribute(NEXT_INDEX_PARAM, nextIndex);
    
                //RequestDispatcher rd =
                //  getServletContext().getRequestDispatcher(DISPLAY_RESULT_URL);
                RequestDispatcher rd =
                  getServletContext().getRequestDispatcher(DISPLAY_PEOPLE_URL);
                rd.forward(request, response);
  11. Re-request "Get All People" from the Admin menu. The following, more domain-specific display should be displaying all returned names for each page.
    People Display
    
        * first0 last0
        * first1 last1
        * first2 last2
        * first3 last3
        * first4 last4
        * first5 last5
        * first6 last6
        * first7 last7
        * first8 last8
        * first9 last9
    
    Index: 0
    
    Count: 10
        [Get All People]
    
    Go to Main Page 
    Note:
    I ran into a repeatable error if I used the browser refresh instead of a new menu navigation to start the next wave of tests. My first call would work, but each browser refresh after that resulted in a faulty EJB stub. The fix was to navigate to the menu and restart the navigation. I do not currently know what the issue is. I could not re-create the issue using the JBoss-deployed WAR.
  12. Continue to press "Get All People" until last page is reached.
    People Display
    
    Index: 96
    
    Count: 10
    
        [Get All People]
    
    Go to Main Page 
  13. Update the DisplayPeople JSP to allow you to request the detailed display of a specific person in the list.
    $ cat javaeeExWAR/src/main/webapp/WEB-INF/content/DisplayPeople.jsp
    
    ...
                <jsp:scriptlet>
                    for(Object o: people) {
                        Person p = (Person)o;
                        String firstName = p.getFirstName();
                        String lastName = p.getLastName();
                        String url = "?id=" + p.getId() + "&amp;command=Get+Person";
                </jsp:scriptlet>
                    <li><a href="<%= url %>"><%= firstName %> <%= lastName %> </a></li>
  14. Hit refresh and you should see the new HREFS, but clicking on then result in the following error. This is because our servlet has not yet been updated to process the issued "Get Person" command.
    Command Error
    A request was made, but the command was not recognized. command=Get Person
    
    Go to Main Page 
  15. Add a handler for "Get Person".
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
        private abstract class Handler {
            protected static final String ID_PARAM = "id";
    ...
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
        private class GetPerson extends Handler {
            public void doHandle(HttpServletRequest request, 
                    HttpServletResponse response) 
                    throws Exception {
                action = "EJB.getPerson"; //describe action in case of exception
                
                String idStr = (String)request.getParameter(ID_PARAM);
                long id = Long.parseLong(idStr);
    
                Person person = registrar.getPersonByIdHydrated(id);
    
                request.setAttribute(RESULT_PARAM, person);
    
                RequestDispatcher rd =
                 getServletContext().getRequestDispatcher(DISPLAY_RESULT_URL);
                rd.forward(request, response);
            }
        }
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
    public class RegistrarHandlerServlet extends HttpServlet {
    
    ...
        public static final String GET_PERSON_COMMAND = "Get Person";
    ...
    
        public void init() throws ServletException {
    ...
                if (ADMIN_TYPE.equals(handlerType)) {               
                    handlers.put(GET_ALL_PEOPLE_COMMAND, new GetAllPeople());
                    handlers.put(GET_PERSON_COMMAND, new GetPerson());
                } 
  16. Wait for the Servlet class to be automatically redeployed within Jetty This will happen when you cause the class to be recompiled to the target/classes directory either through a maven build or saving within Eclipse. Hit refresh on the browser to see the new functionality.
    • http://localhost:9080/javaeeExWAR/model/admin/handler?id=91&command=Get+Person
    [INFO] Restart completed at Sun Apr 05 18:31:51 EDT 2009
    Result: id=91:first90 last90 123, addresses={{street1 city1, state1 zip1},}
    
    Go to Main Page 
  17. Add a DisplayPerson JSP to specifically display a person. Incorporate the ability to display and request a change of address.
    $ cat javaeeExWAR/src/main/webapp/WEB-INF/content/DisplayPerson.jsp
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
                "http://www.w3.org/TR/html4/strict.dtd">
    
    <jsp:directive.page errorPage="/WEB-INF/content/ErrorPage.jsp"/>
    <jsp:directive.page import="myorg.javaeeex.bo.*"/>
    <html>
        <title>Display Person</title>
        <body>
    
            <jsp:scriptlet>
                Person person = (Person)request.getAttribute("result");
                Address address = (person.getAddresses() != null) ?
                    (Address)person.getAddresses().iterator().next() :
                    new Address();
            </jsp:scriptlet>
    
            Id: <%= person.getId() %><p/>
            First Name: <%=person.getFirstName()%><p/>
            Last Name: <%=person.getLastName()%><p/>
            SSN: <%=person.getSsn()%><p/>
    
            <form method="GET"
                action="<%=request.getContextPath()%>/model/admin/handler">
                Street: <input type="text" name="street" size="25" value="<%= address.getStreet()%>"/><p/>
                City: <input type="text" name="city" size="25" value="<%= address.getCity()%>"/><p/>
                State: <input type="text" name="state" size="2" value="<%= address.getState()%>"/><p/>
                Zip: <input type="text" name="zip" size="5" value="<%= address.getZip()%>"/><p/>
                <input type="hidden" name="id" value="<%= person.getId() %>"/>
                <input type="submit" name="command" value="Change Address"/>
            </form>
    
    
            <p/><a href="<%=request.getContextPath()%>/index.jsp">Go to Main Page</a>
        </body>
    </html>
  18. Update the handler to use the new JSP
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
        private abstract class Handler {
    ...
            protected static final String DISPLAY_PERSON_URL = 
                "/WEB-INF/content/DisplayPerson.jsp";
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
        private class GetPerson extends Handler {
            public void doHandle(HttpServletRequest request, 
                    HttpServletResponse response) 
                    throws Exception {
    ...
                RequestDispatcher rd =
                 //getServletContext().getRequestDispatcher(DISPLAY_RESULT_URL);
                 getServletContext().getRequestDispatcher(DISPLAY_PERSON_URL);
                rd.forward(request, response);
    
            }
        }
  19. Test the new display capability.
    • http://localhost:9080/javaeeExWAR/model/admin/handler?id=91&command=Get+Person
    Id: 91
    First Name: first14
    
    Last Name: last14
    
    SSN: 123
    
    Street: street1
    City: city1
    
    State: state1
    
    Zip: zip1
    
     [Change Address]
    
    Go to Main Page
  20. Issue a "Change Address" Request. Note the error for now.
    • http://localhost:9080/javaeeExWAR/model/admin/handler?street=street1&city=city1&state=state1&zip=zip1&id=91&command=Change+Address
      Command Error
      A request was made, but the command was not recognized. command=Change Address
      
      Go to Main Page 
  21. Create the handler to implement the change of address.
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
        private abstract class Handler {
    ...
            protected static final String STREET_PARAM = "street";
            protected static final String CITY_PARAM = "city";
            protected static final String STATE_PARAM = "state";
            protected static final String ZIP_PARAM = "zip";
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
    import myorg.javaeeex.bo.Address;
    ...
    
        private class ChangeAddress extends Handler {
            public void doHandle(HttpServletRequest request, 
                    HttpServletResponse response) 
                    throws Exception {
                action = "EJB.changeAddress"; //describe action in case of exception
                
                log.debug("Change Address: id=" + request.getParameter(ID_PARAM) +
                        ", uri=" + request.getRequestURI());
                String idStr = (String)request.getParameter(ID_PARAM);
                long id = Long.parseLong(idStr);
            
                String street = (String)request.getParameter(STREET_PARAM);
                String city = (String)request.getParameter(CITY_PARAM);
                String state = (String)request.getParameter(STATE_PARAM);
                String zip = (String)request.getParameter(ZIP_PARAM);
            
                Address address = new Address();
                address.setStreet(street);
                address.setCity(city);
                address.setState(state);
                address.setZip(zip);
            
                Person person = registrar.getPersonByIdHydrated(id);                
                person = registrar.changeAddress(person, address);
                
                request.setAttribute(RESULT_PARAM, person);
            
                RequestDispatcher rd =
                 getServletContext().getRequestDispatcher(DISPLAY_PERSON_URL);
                rd.forward(request, response);
            }
        }
  22. Add the new ChangeAddress handler to the Controller Servlet
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
    public class RegistrarHandlerServlet extends HttpServlet {
    ...
        public static final String CHANGE_ADDRESS_COMMAND = "Change Address";
    ...
        public void init() throws ServletException {
    ...
                if (ADMIN_TYPE.equals(handlerType)) {               
                    handlers.put(GET_ALL_PEOPLE_COMMAND, new GetAllPeople());
                    handlers.put(GET_PERSON_COMMAND, new GetPerson());
                    handlers.put(CHANGE_ADDRESS_COMMAND, new ChangeAddress());
                } 
  23. Once the servlet is re-compiled and the Jetty engine recognizes the change, hit refresh on the browser and attempt a few address changes.
    [INFO] Restart completed at Sun Apr 05 20:37:16 EDT 2009
     -init() called
     -initRegistrar(), registrar=null
     -jndiProperties={java.naming.provider.url=jnp://localhost:1099, java.naming.factory.initial=org.jnp.interfaces.NamingCo
    ntextFactory, java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces}
     -registrar initialized:jboss.j2ee:ear=javaeeEx.ear,jar=javaeeExEJB-1.0-SNAPSHOT.jar,name=RegistrarEJB,service=EJB3
     -configured handler type:admin with {Change Address=myorg.webtier.web.RegistrarHandlerServlet$ChangeAddress@1396ef7, Ge
    t All People=myorg.webtier.web.RegistrarHandlerServlet$GetAllPeople@1a422f6, Get Person=myorg.webtier.web.RegistrarHandl
    erServlet$GetPerson@f36e59}
     -doGet() called
     -command=Change Address
     -Change Address: id=91, uri=/javaeeExWAR/model/admin/handler
     -doGet() called
     -command=Change Address
     -Change Address: id=91, uri=/javaeeExWAR/model/admin/handler
  24. Deploy your implementation to the application server to verify all works in the target environment.
    • http://localhost:8080/javaeeEx/
    • select Admin
    • select "Get All People"
    • select one of the elements to display person
    • change one of the address fields and press "Change Address"
    • press "Go to Main Page"
    • verify the change for the modified Person
      $ mvn clean install -rf :javaeeExEJB
      
      ...
      [INFO] Java EE Exercise EJB .............................. SUCCESS [5.753s]
      [INFO] Java EE Exercise WAR .............................. SUCCESS [3.233s]
      [INFO] Java EE Exercise EAR .............................. SUCCESS [1.359s]
      [INFO] Java EE Exercise Remote Test ...................... SUCCESS [26.487s]
      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD SUCCESS

Migrate WAR to EJB Local Interface

All the functionality we put in place so far leverages the Remote interface of the EJB. The remote aspect of the EJB was quite helpful, in that it could be developed and tested with the RMI Test framework and WAR development could be done without server-side deployment delays by using Jetty. In this section, we will put a little work into trying to leverage the Local interface of the EJB, while still retaining some development functionality if the remote interface.

  1. Expand the Local interface of the EJB to include methods used by the Web UI.
    $ cat javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarLocal.java
    
    package myorg.javaeeex.ejb;
    
    import java.util.Collection;
    
    import javax.ejb.Local;
    
    import myorg.javaeeex.bl.RegistrarException;
    import myorg.javaeeex.bo.Address;
    import myorg.javaeeex.bo.Person;
    
    @Local
    public interface RegistrarLocal {
        void ping();
    
        Person getPersonByIdHydrated(long id)
            throws RegistrarException;
    
        Person changeAddress(Person person, Address address)
            throws RegistrarException;
    
        Collection<Person> getAllPeopleHydrated(int index, int count)
                    throws RegistrarException;
    }

    We could refactor the common methods of the Remote and Local interfaces at this point to establish a reusable base interface. However, we will keep them separate at this point of development to highlight that Local and Remote interfaces should be thought of as independent.

  2. Generalize the type of the variable used to hold the Registrar reference in the Web UI. Change the @Inject back to an @EJB specification to take easy advantage of the beanInterface property that can be used to define the type of specification.
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
    import javax.ejb.EJB;
    import myorg.javaeeex.ejb.RegistrarLocal;
    ...
    
    public class RegistrarHandlerServlet extends HttpServlet {
       ...
        @EJB(beanInterface=RegistrarLocal.class)
        private Object registrar;
  3. Update the calls to the EJB to determine the type of interface working with.
                if (registrar instanceof RegistrarRemote) {
                   ((RegistrarRemote)registrar).ping(); 
                } else {
                   ((RegistrarLocal)registrar).ping();
                }
                Collection<Person> people = (registrar instanceof RegistrarRemote) ?
                    ((RegistrarRemote)registrar).getAllPeopleHydrated(index, count) :
                    ((RegistrarLocal)registrar).getAllPeopleHydrated(index, count);
    
                Person person = (registrar instanceof RegistrarRemote) ?
                    ((RegistrarRemote)registrar).getPersonByIdHydrated(id) :
                    ((RegistrarLocal)registrar).getPersonByIdHydrated(id);
                Person person = (registrar instanceof RegistrarRemote) ?
                    ((RegistrarRemote)registrar).getPersonByIdHydrated(id) :
                    ((RegistrarLocal)registrar).getPersonByIdHydrated(id);
                person = (registrar instanceof RegistrarRemote) ?
                    ((RegistrarRemote)registrar).changeAddress(person, address) :
                    ((RegistrarLocal)registrar).changeAddress(person, address);
  4. Test within the Jetty container. Note the use of the Remote interface outside of the application server.
    $ mvn clean install -DskipTests
    $ mvn pre-integration-test -rf javaeeExTest; (cd javaeeExWAR; mvn clean jetty:run)
    
    ...
    
    [INFO] Starting scanner at interval of 10 seconds.
    
     -initRegistrar(), registrar=null
     -java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
     -registrar initialized:Proxy for remote EJB StatelessEJBLocator{appName='javaeeExEAR', moduleName='javaeeExEJB', distinctName='', beanName='RegistrarEJB', view='interface myorg.javaeeex.ejb.RegistrarRemote'}
  5. Build, deploy, and test within the application server. Note the use of the Local interface within the application server.
    //SERVER LOG
    
    22:51:03,551 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet] (http--127.0.0.1-8080-2) initRegistrar(), registrar=Proxy for view class: myorg.javaeeex.ejb.RegistrarLocal of EJB: RegistrarEJB
  6. Now lets try to take advantage of a Local interface by relaxing the hydration on a method.
    $ cat javaeeExEJB/src/main/java/myorg/javaeeex/ejb/RegistrarLocal.java
    
    ...
    @Local
    public interface RegistrarLocal { 
        void ping();
    
        Person getPersonById(long id)
            throws RegistrarException;
        Collection<Person> getAllPeople(int index, int count)
                    throws RegistrarException;
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
        private class GetPerson extends Handler {
            public void doHandle(HttpServletRequest request, 
                    HttpServletResponse response) 
                    throws Exception {
    ...
                Person person = (registrar instanceof RegistrarRemote) ?
                    ((RegistrarRemote)registrar).getPersonByIdHydrated(id) :
                    //((RegistrarLocal)registrar).getPersonByIdHydrated(id);
                    ((RegistrarLocal)registrar).getPersonById(id);
  7. Rebuild, deploy, and request a person. Notice that we still do not have the ability to access the Lazy-Loaded objects with the Local interface. The problem is that even though we are using the local interface, the transaction and session with the DB has closed. We either need to hydrate the objects before returning (we already did that) or we need to get the transaction to last longer.
    • http://localhost:8080/javaeeEx/model/admin/handler?id=1&command=Get+Person
      $ mvn clean integration-test -rf :javaeeExEJB
      http://localhost:8080/javaeeEx/model/admin/handler?id=1&command=Get+Person
      
      General Exception Page
      
      An error was reported by the application. More detailed information may follow.
      .
      
      org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: myorg.javaeeex.bo.Person.addresses, no session or session was closed at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358) at 
      ...
      myorg.webtier.web.RegistrarHandlerServlet$GetPerson.doHandle(RegistrarHandlerServlet.java:250) at myorg.webtier.web.RegistrarHandlerServlet$Handler.handle(RegistrarHandlerServlet.java:169) at 

    At this point we are going to try to initiate the transaction from the Web tier to have it active throughout the handler code. We will implement this logic with a Servlet Filter.

  8. Add a the shell of a Servlet Filter class.
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/JPAFilter.java
    
    package myorg.javaeeex.web;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class JPAFilter implements Filter {
        private static final Log log = LogFactory.getLog(JPAFilter.class);
    
        public void init(FilterConfig config) throws ServletException {
            log.debug("*** JPAFilter.init() ***");
        }
    
        public void doFilter(ServletRequest request,
                ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
    
            log.debug("*** JPAFilter.doFilter() ENTER ***");
            chain.doFilter(request, response);
            log.debug("*** JPAFilter.doFilter() EXIT ***");
        }
    
        public void destroy() {}//required by interface
    }
  9. Register the filter to fire for the Controller Servlet(s) using the web.xml in the src/main tree.
    $ cat javaeeExWAR/src/main/webapp/WEB-INF/web.xml
    
    ...
    
        <filter>
            <filter-name>JPAFilter</filter-name>
            <filter-class>myorg.javaeeex.web.JPAFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>JPAFilter</filter-name>
            <url-pattern>/model/admin/handler</url-pattern>
            <url-pattern>/model/handler</url-pattern>
        </filter-mapping>
    </web-app>
  10. Verify the debug appears using Jetty
    2009-04-05 23:00:46.573::INFO:  No Transaction manager found - if your webapp requires one, please configure one.
     -*** JPAFilter.init() ***
    [INFO] Restart completed at Sun Apr 05 23:00:47 EDT 2009
    
    ...
     -*** JPAFilter.doFilter() ENTER ***
     -doGet() called
     -command=Get Person
     -*** JPAFilter.doFilter() EXIT ***
    
  11. Inject an instance of a UserTransaction into the Filter. This will only work within the application server the way it is designed here.
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/JPAFilter.java
    
    ...
    import javax.transaction.UserTransaction;
    ...
    public class JPAFilter implements Filter {
    ...
        @Inject
        private static UserTransaction tx;
    ...
  12. Add the transaction management with the UserTransaction.
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/JPAFilter.java
    
    ...
    import javax.transaction.Status;
    
    ...
            public void doFilter(ServletRequest request, ServletResponse response,
                            FilterChain chain) throws IOException, ServletException {
                log.debug("*** JPAFilter.doFilter() ENTER ***");
    
                boolean ownTx = false;
    
                if (tx != null) {
                    try {
                        if (tx.getStatus() != Status.STATUS_ACTIVE) {
                            tx.begin();
                            ownTx = true;
                        }
                    } catch (Exception ex) {
                        log.error("error starting transaction:", ex);
                        throw new ServletException("error starting transaction", ex);
                    }
                } else {
                        log.debug("no UserTransaction injected -- moving on without one");
                }
    
                chain.doFilter(request, response);
    
                if (tx != null) {
                    try {
                        if (ownTx) {
                            if (tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) {
                                tx.rollback();
                            } else if (tx.getStatus() == Status.STATUS_ACTIVE) {
                                tx.commit();
                            }
                        }
                    } catch (Exception ex) {
                        log.error("error ending transaction:", ex);
                        throw new ServletException("error ending transaction", ex);
                    }
                }
                
                log.debug("*** JPAFilter.doFilter() EXIT ***");
            }
    
  13. Verify the Jetty configuration still works. Note that we have no transaction active in the Web UI hosted by Jetty and must use the hydrated form of the calls.
     -*** JPAFilter.doFilter() ENTER ***
     -no UserTransaction injected -- moving on without one
    
    ...
    
     -doGet() called
     -command=Get All People
     -*** JPAFilter.doFilter() EXIT ***
  14. Test the configuration within the application server. We should now successfully be able to work with the non-hydrated form of the methods.
    Note:
    If you are not seeing your changes on the server, make sure to install the suspect artifact into the localRepository using "mvn clean install".
    22:21:36,782 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet] init() called 
    22:21:36,783 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet] jndiProperties={java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory, java.naming.provider.url=jnp://127.0.0.4:1099, java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces}
    22:21:36,794 DEBUG [myorg.javaeeex.ejb.RegistrarEJB] **** init ****
    22:21:36,795 DEBUG [myorg.javaeeex.ejb.RegistrarEJB] em=org.jboss.jpa.tx.TransactionScopedEntityManager@2399a1
    22:21:36,795 DEBUG [myorg.javaeeex.ejb.RegistrarEJB] init complete, registrar=myorg.javaeeex.blimpl.RegistrarImpl@1d6fc09
    22:21:36,795 DEBUG [myorg.javaeeex.ejb.RegistrarEJB] ping called
    22:21:36,795 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet] registrar initialized:Proxy to jboss.j2ee:ear=javaeeEx.ear,jar=javaeeExEJB-1.0-SNAPSHOT.jar,name=RegistrarEJB,service=EJB3 implementing [interface myorg.javaeeex.ejb.RegistrarLocal]
    22:21:36,795 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet] jndiName used:/myorg/javaeeEx/RegistrarEJB/local
    22:21:36,796 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet] configured handler type:admin with {Get Person=myorg.javaeeex.web.RegistrarHandlerServlet$GetPerson@13cdfec, Get All People=myorg.javaeeex.web.RegistrarHandlerServlet$GetAllPeople@19131da, Change Address=myorg.javaeeex.web.RegistrarHandlerServlet$ChangeAddress@15ff8a2}
    22:21:36,796 DEBUG [myorg.javaeeex.web.JPAFilter] *** JPAFilter.doFilter() ENTER ***
    22:21:36,801 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet] doGet() called
    22:21:36,801 DEBUG [myorg.javaeeex.web.RegistrarHandlerServlet] command=Get Person
    22:21:36,802 DEBUG [myorg.javaeeex.ejb.RegistrarEJB] *** getPersonById(89) ***
    22:21:36,969 DEBUG [myorg.javaeeex.web.JPAFilter] *** JPAFilter.doFilter() EXIT ***
  15. Update the remaining Local calls to use the non-hydrated form.
    $ cat javaeeExWAR/src/main/java/myorg/javaeeex/web/RegistrarHandlerServlet.java
    
    ...
        private class GetAllPeople extends Handler {
    ...
                Collection<Person> people = (registrar instanceof RegistrarRemote) ?
                    ((RegistrarRemote)registrar).getAllPeopleHydrated(index, count) :
                    //((RegistrarLocal)registrar).getAllPeopleHydrated(index, count);
                    ((RegistrarLocal)registrar).getAllPeople(index, count);
        private class ChangeAddress extends Handler {
    ... 
                Person person = (registrar instanceof RegistrarRemote) ?
                    ((RegistrarRemote)registrar).getPersonByIdHydrated(id) :
                    //((RegistrarLocal)registrar).getPersonByIdHydrated(id);
                    ((RegistrarLocal)registrar).getPersonById(id);
  16. Redeploy and retest functionality within the application server.

Summary

In this exercise we added a good deal more beef to our Web UI; specifically...

  • Use case functionality that we tested through the RMI Test first.
  • More JSP views that are invoked by the controller.
  • Local and Remote interface functionality.
  • Extending the transaction into the Web UI so that lazy loading techniques can be leveraged in the EJB's local interface.