Skip to main content

The Power and Pain of POJOs

Posted by cayhorstmann on September 8, 2006 at 5:20 PM PDT

In my software engineering course, I assign readings from a href="https://www.safariu.com/online-syllabus.do?syllabusId=7673682235433056305">SafariU
bookshelf. Having read the delightful book href="http://www.insidehighered.com/news/2005/07/13/frosh">My Freshman
Year: What a Professor Learned by Becoming a Student, I realized that
I needed a mechanism for checking that they actually did the reading. At
the beginning of each class meeting, I give a 5 minute quiz, and then we
all look at the answers:

???

I wrote a simple EJB3/JSF app for that purpose. (Why JSF? Maybe it's
not the world's most wonderful framework, but it's part of GlassFish and
supported by NetBeans.) It took about three days of solid work: one
productive day, one totally annoying day chasing down a href="https://glassfish.dev.java.net/issues/show_bug.cgi?id=939">Glassfish
bug, and one frustrating day of chasing spurious problems that could
be avoided if I, or preferably Netbeans, had been smarter.

Is it really possible to write a quick and dirty web application with
EJB3/JSF in a couple of days? What works well and what doesn't? Continuing
the "Elvis meets GlassFish" saga,
I'd like to report on Elvis' experience.

POJOs and EJB3/JPA persistence worked very well. I had a
straightforward OOD model. A quiz has questions. There are two kinds of
questions: fill-in-the-blank questions and multiple-choice questions. A
multiple-choice question has choices.

???

Mapping it to the database was extremely straightforward.

@Entity
public class Quiz implements Serializable {
    @Id @GeneratedValue private Long id;
    private String title;
    @OneToMany private List<Question> questions;
    . . .
}

I set up the data model in Java and let JPA generate the tables. Easy
stuff--Elvis can do this.

Issue # 1: Ordered collections

??? style="float: left; padding-right: 0.5em;" />I add questions to a quiz
(not necessarily by increasing ID). The ordering needs to be preserved,
but it purely depends on the insertion order, not any intrinsic property
of the element entities.

This could be easily accomplished by storing the index in an additional
column in the Quiz_Question join table, like this:

Quiz | Question | Index
-----------------------
1001 | 20552    | 1
1001 | 20207    | 2
. . .

Hibernate can do this, with the @IndexColumn annotation. If
you agree that GlassFish should support a true List collection as
well, please href="https://glassfish.dev.java.net/issues/showvotes.cgi?voteon=1122">vote
for this enhancement request.

Without explicit support, the data model isn't as clean as it should
be. I ended up adding index values to the collected entities, but that's a
hack.

Non-Issue #2: Beans, beans, beans

??? style="float: left; padding-right: 0.5em;" /> I was concerned about this.
Entity beans. Session beans. Managed beans. Would I encounter the evil
distraction of data transfer objects? Was I going to waste a lot of time
chasing through two layers?

But it all worked well. There are no data transfer objects--the
entities are POJOs, and they can simply move between the session beans and
the JSF managed beans. Ideally, it would have been nice to use something
seamless such as Seam,
but the session bean/managed bean split wasn't painful. I put all the
business logic into the session beans and all the display logic (such as
current user, current selection) into the managed beans. Here is an
example. Note the injection of the session bean, and note how the button
action calls the session bean to persist the current submission.

public class QuizMB { // a JSF managed bean
    @EJB private EditSB editSB; // a session bean
    . . .
    public String nextQuestion() { // a button action
        if (pastEndDate()) return "past_end_date";
        // calling the session bean      
        currentSubmission = quizSB.saveSubmission(currentSubmission);
        if (currentQuestionIndex == getHighestQuestionIndex())
            return "all_questions_taken";
        else {
            currentQuestionIndex++;       
            return currentQuestionType();
        }
    }

Non-Issue #3. Lazy Fetching

??? style="float: left; padding-right: 0.5em;" />I was concerned about lazily
fetched collections. When an entity is exported from a session bean to a
managed bean, it is detached from the container. If it has a lazily
fetched property (by default, any collection-valued property is lazy),
then you can't access it in the managed bean.

Of course, you can't just make all collections eager. The initial page
shows a selection box filled with the titles of all quizzes that have
elapsed. I simply get a List from the session bean.
If a quiz loads all questions, and all questions load all choices, then I
pull out a lot more data than I need. So, that collection needs to be
lazy.

On the other hand, when I have a multiple choice question, I really
want all the choices with it, and there are only a few for each question.
I used eager fetching here.

It was easy enough to override the fetch type on a case-by-case basis.
Make a query that fetches the parts that you need.

public Quiz getQuiz(Long id) {
    Query q = em.createQuery("SELECT DISTINCT x FROM Quiz x LEFT JOIN FETCH x.questions WHERE x.id = :id")
        .setParameter("id", id);
       
    return (Quiz) q.getSingleResult();
}

Note the LEFT JOIN FETCH. I first had a JOIN FETCH,
and then getSingleResult threw an exception when a quiz had no
questions (which only happens when you save an empty quiz, log out, log in
again, and then retrieve it, as I found out the hard way when I demoed the
app to a colleague.)

Full disclosure: I had a problem with a href="http://forums.java.net/jive/thread.jspa?threadID=17821&tstart=75">complex
join fetch, but I think that's a bug. I worked around it by making two
separate queries.

Overall, the lazy fetch default is good. Override it with eager
fetching for small collections, and use LEFT JOIN FETCH
otherwise.

Non-Issue #4. JSF

I thought it would be a giant pain to use JSF without a GUI builder. (I
didn't want to figure out how to build the GUI in Creator and write the
remaining code in Netbeans.) But it wasn't too bad. The key is to use CSS
as much as possible. Make styles for all functional elements. For example,
I tagged prompts (such as User name and Password) as
prompt:

<h:outputText styleClass="prompt" value="#{msgs.login_username}"/>

I added a style

.prompt:after {
    content: ":";
}
.prompt {
     font-weight: bold;
}

With a bit of practice, I was able to put most of the styling into CSS,
so that the JSF pages only contained the components and the EL wiring.

Ideally, of course, JSF would have many more prefabricated components,
but that's a topic for another blog...

Issue # 5. The Stack Trace from Hell

??? style="float: left; padding-right: 0.5em;" />I was concerned about
debugging. After all, when something goes wrong with the app server, you
get the Stack Trace from Hell instead of a gentle message "error x
in file y line z".

[#|2006-08-25T08:45:59.290-0800|SEVERE|sun-appserver-ee9.1|javax.enterprise.resource.webcontainer.jsf.application|_ThreadID=21;_ThreadName=httpWorkerThread-8080-1;_RequestID=55780119-4727-462d-b53f-196c600080ba;|javax.ejb.EJBException: nested exception is: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
java.rmi.RemoteException
javax.faces.el.EvaluationException: javax.ejb.EJBException: nested exception is: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.RemoteException
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:91)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:96)
at javax.faces.component.UICommand.broadcast(UICommand.java:383)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:471)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:783)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:97)
at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:244)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:113)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:397)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:278)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:556)
at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:246)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:185)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:73)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:182)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at com.sun.enterprise.web.VirtualServerPipeline.invoke(VirtualServerPipeline.java:120)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:939)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:137)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:556)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:939)
at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:619)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.processNonBlocked(DefaultProcessorTask.java:550)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:780)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:326)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:251)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:205)
at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:252)
at com.sun.enterprise.web.connector.grizzly.WorkerThreadImpl.run(WorkerThreadImpl.java:103)
Caused by: javax.ejb.EJBException: nested exception is: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.RemoteException
at com.horstmann.qq.session._QuizSB_Wrapper.saveSubmission(com.horstmann.qq.session._QuizSB_Wrapper.java)
at com.horstmann.qq.web.QuizMB.saveSubmission(QuizMB.java:114)
at com.horstmann.qq.web.QuizMB.nextQuestion(QuizMB.java:119)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.el.parser.AstValue.invoke(AstValue.java:157)
at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:283)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:71)
... 33 more
|#]

[#|2006-08-25T08:45:59.294-0800|WARNING|sun-appserver-ee9.1|javax.enterprise.resource.webcontainer.jsf.lifecycle|_ThreadID=21;_ThreadName=httpWorkerThread-8080-1;_RequestID=55780119-4727-462d-b53f-196c600080ba;|#{quizzes.nextQuestion}: javax.ejb.EJBException: nested exception is: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.RemoteException
javax.faces.FacesException: #{quizzes.nextQuestion}: javax.ejb.EJBException: nested exception is: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.RemoteException
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:111)
at javax.faces.component.UICommand.broadcast(UICommand.java:383)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:471)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:783)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:97)
at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:244)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:113)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:397)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:278)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:556)
at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:246)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:185)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:73)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:182)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at com.sun.enterprise.web.VirtualServerPipeline.invoke(VirtualServerPipeline.java:120)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:939)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:137)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:556)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:939)
at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:619)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.processNonBlocked(DefaultProcessorTask.java:550)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:780)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:326)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:251)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:205)
at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:252)
at com.sun.enterprise.web.connector.grizzly.WorkerThreadImpl.run(WorkerThreadImpl.java:103)
Caused by: javax.faces.el.EvaluationException: javax.ejb.EJBException: nested exception is: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.RemoteException
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:91)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:96)
... 32 more
Caused by: javax.ejb.EJBException: nested exception is: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.RemoteException
at com.horstmann.qq.session._QuizSB_Wrapper.saveSubmission(com.horstmann.qq.session._QuizSB_Wrapper.java)
at com.horstmann.qq.web.QuizMB.saveSubmission(QuizMB.java:114)
at com.horstmann.qq.web.QuizMB.nextQuestion(QuizMB.java:119)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.el.parser.AstValue.invoke(AstValue.java:157)
at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:283)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:71)
... 33 more
|#]
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:210)
at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:119)
at com.sun.corba.ee.impl.presentation.rmi.bcel.BCELStubBase.invoke(BCELStubBase.java:197)
at com.horstmann.qq.session.__QuizSB_Remote_DynamicStub.saveSubmission(__QuizSB_Remote_DynamicStub.java)
at com.horstmann.qq.session._QuizSB_Wrapper.saveSubmission(com.horstmann.qq.session._QuizSB_Wrapper.java)
at com.horstmann.qq.web.QuizMB.saveSubmission(QuizMB.java:114)
at com.horstmann.qq.web.QuizMB.nextQuestion(QuizMB.java:119)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.el.parser.AstValue.invoke(AstValue.java:157)
at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:283)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:71)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:96)
at javax.faces.component.UICommand.broadcast(UICommand.java:383)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:471)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:783)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:97)
at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:244)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:113)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:397)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:278)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:556)
at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:246)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:185)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:73)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:182)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at com.sun.enterprise.web.VirtualServerPipeline.invoke(VirtualServerPipeline.java:120)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:939)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:137)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:556)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:939)
at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:619)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.processNonBlocked(DefaultProcessorTask.java:550)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:780)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:326)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:251)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:205)
at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:252)
at com.sun.enterprise.web.connector.grizzly.WorkerThreadImpl.run(WorkerThreadImpl.java:103)
Caused by: java.rmi.RemoteException
javax.ejb.EJBException: nested exception is: java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.RemoteException
at com.horstmann.qq.session._QuizSB_Wrapper.saveSubmission(com.horstmann.qq.session._QuizSB_Wrapper.java)
at com.horstmann.qq.web.QuizMB.saveSubmission(QuizMB.java:114)
at com.horstmann.qq.web.QuizMB.nextQuestion(QuizMB.java:119)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.el.parser.AstValue.invoke(AstValue.java:157)
at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:283)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:71)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:96)
at javax.faces.component.UICommand.broadcast(UICommand.java:383)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:471)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:783)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:97)
at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:244)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:113)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:397)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:278)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:556)
at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:246)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:185)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:73)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:182)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at com.sun.enterprise.web.VirtualServerPipeline.invoke(VirtualServerPipeline.java:120)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:939)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:137)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:586)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:556)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:939)
at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:619)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.processNonBlocked(DefaultProcessorTask.java:550)
at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:780)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:326)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:251)
at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:205)
at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:252)
at com.sun.enterprise.web.connector.grizzly.WorkerThreadImpl.run(WorkerThreadImpl.java:103)
|#]

This was a serious issue. I learned quickly to recode all my session
beans to

public Output mySessionBean(Input input) {
   try {
      . . .
   } catch (RuntimeException ex) {
      logger.log(Level.SEVERE, "", ex);
      throw ex;
   }
}

Note to self: Try an interceptor next time.

The NetBeans debugger helped immensely. Without the ability to set
breakpoints in the managed bean and session bean code, I would have had a
truly miserable time.

Something needs to be done. Glassfish has the wrong view of the world.
It thinks that it is running a perfectly debugged application, and if
something goes wrong, then it should dump it's life story to the log and
sulk. Just imagine if the Java compiler took that approach to error
handling. You'd compile, find no class file, and search for elusive clues
in the log.

$ javac MyFile.java
$ ls MyFile.class
ls: MyFile.class: No such file or directory
$ vi /usr/local/jdk1.6.0/logs/compiler.log

Fortunately, the compiler doesn't say "don't blame me--it was your
fault for feeding me a buggy program". Neither should GlassFish.

Conclusion

In the past, EJB was far too complicated for rapid development. It only
made sense for "big iron" apps. But EJB3 is different. A competent
developer (Elvis, not Einstein) can use it to put together a simple app
quickly. The tools could be better (error handling, visual editing, JSF
components), but Netbeans is reasonable and gives you one-stop
shopping.

Related Topics >>