Skip navigation

cayhorstmann

June 2006 Previous month Next month

Readers of my blog know about Elvis, the Microsoft persona of the programmer who is neither Einstein nor the point-and-click/drag-and-drop "just give me a wizard" Mort. Elvis wants to use EJB 3 because the annotations make it easy, but he is a bit taken aback by the jargon.

I am working on a glossary that gives both the official definition of various terms (starting with Entity) and explanations that Elvis can understand. Elvis has worked through the basic tutorials, but he needs help deciphering the jargon in the GlassFish forum.

Whenever possible, the official definitions are from the JPA spec (AKA JSR 220: Enterprise JavaBeansTM,Version 3.0 Java Persistence API), a document that ranges from crystal clarity (if you happen to find the right sentence) to terminal obtuseness.

I'll keep updating this glossary over the next few weeks.

What other definitions would you like? Do you spot errors or inaccuracies? Please let me know.

Application Client

???Party line:Frequently mentioned in the spec without definition. Here is an explanation from the GlassFish FAQ: The Java EE platform defines a component that is specially designed to portably access Java EE services from a JVM running outside of the Application Server. It is called a Java EE Application Client and has been part of the platform since its first release (J2EE 1.2). Like all Java EE components it runs within a container provided by the vendor's implementation. The main advantages of the Application Client are that it's portable and that it allows the developer to use the same programming model for defining and accessing resources as is used within web components and EJBs. It follows the overall philosophy of the Java EE platform that as much of the "plumbing" or system-level work as possible should be performed by a container instead of being part of the Application code. That means a guarantee that the no-arg InitialContext constructor will work, that a private component naming context (java:comp/env) is available, and in Java EE 5 that platform annotations and injection are available.

???Elvis: That's the thing that I launch with the appclient tool. See also SE Environment.

Container-Managed Transaction-Scoped Persistence Context

???Party line: (JPA spec 5.6) A container-managed persistence context may be defined to have either a lifetime that is scoped to a single transaction or an extended lifetime that spans multiple transactions, depending on the PersistenceContextType that is specified when its EntityManager is created. This specification refers to such persistence contexts as transaction-scoped persistence contexts and extended persistence contexts respectively.

(JPA spec 5.6.1) The application may obtain a container-managed entity manager with transaction-scoped persistence context bound to the JTA transaction by injection or direct lookup in the JNDI namespace.

???Elvis: The thing that I get when I do

@Stateless
public class MySessionBean implements MySessionBeanInterface {
    @PersistenceContext
    private EntityManager em;
    . . .
}

Detached Entity Instance

???Party line: (JPA spec 3.2) A detached entity instance is an instance with a persistent identity that is not (or no longer) associated with a persistence context.

(JPA spec 3.2.4) A detached entity may result from transaction commit if a transaction-scoped container-managed entity manager is used (see section 3.3); from transaction rollback (see section 3.3.2); from clearing the persistence context; from closing an entity manager; and from serializing an entity or otherwise passing an entity by value—e.g., to a separate application tier, through a remote interface, etc.

???Elvis: Pretty straightforward. But why do they call it a transaction-scoped container-managed entity manager when Section 5.6.1 calls it a Container-Managed Transaction-Scoped Persistence Context? And why do they talk of clearing the persistence context whenclear is an EntityManager method? No wonder I am confused about persistence contexts.

Entity / Entity Class / Entity Instance

???Party line: (JPA spec 2) An entity is a lightweight persistent domain object.

(JPA spec 2.1) The [sic] entity class must be annotated with the Entity annotation or denoted in the XML descriptor as an entity.

Throughout the spec, the term "entity" is used to refer either to an entity class or an instance of such a class, depending on the context.

???Elvis: An entity class is a class that I annotated with @Entity. (Let's not dwell on the XML alternative.) An entity instance is an object of such a class.

It's a good idea to put "class" or "instance" after any use of "entity".

Entity Manager

???Party line: (JPA spec 3.1) An EntityManager instance is associated with a persistence context. A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed. The EntityManager interface defines the methods that are used to interact with the persistence context. The EntityManager API is used to create and remove persistent entity instances, to find entities by their primary key, and to query over entities.

???Elvis: AnEntityManager is the thing that lets me interact with the database by calling persist, find,createQuery.

Entity Manager Factory

???Party line: (JPA spec 5.4) The EntityManagerFactory interface is used by the application to obtain an application-managed entity manager. When the application has finished using the entity manager factory, and/or at application shutdown, the application should close the entity manager factory. Once an EntityManagerFactory has been closed, all its entity managers are considered to be in the closed state.

???Elvis: I need an EntityManagerFactory when I can't get myEntityManager injected, e.g. in a SE environment or a servlet.

Owning / Inverse Side

???Party line: (JPA spec 2.1.7) A bidirectional relationship has both an owning side and an inverse side. A unidirectional relationship has only an owning side. The owning side of a relationship determines the updates to the relationship in the database, as described in section 3.2.3.

???Elvis: The inverse side is the one with the mappedBy attribute. With a single-valued relationship, only the owning side has a foreign key column.

Persistence Context

???Party line: (JPA spec 5.1) A persistence context is a set of managed entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed by the entity manager.

???Elvis: What's in it for me??? Isn't there exactly one persistence context per entity manager? Why do I need to know about two concepts?

Persistence Unit

???Party line: (JPA spec 6.1) A persistence unit is a logical grouping that includes:

  • An entity manager factory and its entity managers, together with their configuration information.
  • The set of managed classes included in the persistence unit and managed by the entity managers of the entity manager factory.
  • Mapping metadata (in the form of metadata annotations and/or XML metadata) that specifies the mapping of the classes to the database.

???Elvis: That's the thing that I define with the META-INF/persistence.xmlfile. In an SE application, it contains database connection parameters. In an EE application, I need one even if it contains no information--that's the way it is.

SE Environment

???Party line:Frequently mentioned in the spec without definition. An application that runs outside any container.

This is sometimes called a "standalone client" (but not in the spec).

???Elvis: That's when I launch with -javaagent. I also need to add the JPA library (such as toplink-essentials.jar) and the database driver to the class path.

It's summer again, people have more time on their hand, and they think about switching operating systems:

I think that's great. A Windows monoculture is not healthy. Judging from my students' crapware-infected laptops, it is actually extremely unhealty.

In the last five years, I regularly used Windows XP, Mac OS X, and Linux (Fedora and Ubuntu). In my experience, Windows XP is just fine when well-tuned, but not easy to keep that way, and totally wretched when it is not. Mac OS X is pretty if you are into blue and brushed metal and the mouse and Java 1.4.2--as you should be if you are a right-thinking citizen. I much prefer Linux, particularly for Java development. I'd like to convince you to switch to Linux as well. My motivation is simple and selfish. If lots of programmers work on Linux, then Java support will for Linux will get even better.

I often hear the argument "I saw the (caution: oxymoron ahead)coolest dudes at a tech conference running Macs, so I should get a Mac too". There are three simple reasons to switch to Linux instead.

  1. Java. In the reality distortion field, "Apple has optimized Java on Mac OS X to look great and perform superbly, making Mac OS X the ultimate platform for developing and deploying cross-platform Java applications." It just ain't so. Sun releases the latest and greatest versions of Java simultaneously on Linux, Solaris, and Windows. The Mac port is done by Apple. After much delay. With interesting bugs. And, if you use Eclipse plugins, you have SWT bugs too.
  2. Keyboard shortcuts. This is what drove me away from the Mac. On Thunderbird, I check my mail with Alt-F W G (File -> neW -> Get all messages). I start a new message with Alt-M N (Message -> New message), save it with Alt-F L (File -> send Later) and send it with Alt-F D (File -> senD unsent). I really hate to do that with the mouse. M o v e t h e m o u s e t o t h e m e n u b a r t h e n l o c a t e a n d c l i c k t h e m e n u t h e n l o c a t e a n d c l i c k t h e s u b m e n u t h e n l o c a t e a n d c l i c k t h e m e n u i t e m. . . I did give the Mac the good try for a month or two, three years ago, and I just couldn't take the constant mousing.
    Now I know what the Mac zealots will say: 
    • All programs have keyboard shortcuts. Sure, but look at NetBeans. At most half of the menu options have shortcuts, and most of them are impossible to remember (Ctrl + Shift + F8).
    • You can get a third-party program. Ok, but you'd have to laboriously define hundreds of menu accelerators by hand, separately for each program, and they too would be hard to remember.
    • Research has shown that people delude themselves thinking that the keyboard is faster. Studies, in which people from all walks of life have been asked to solve the same task with the mouse or keyboard have conclusively demonstrated that the mouse is faster, even though power users have the illusion that they save time with keystrokes. Well, a programmer isn't a person from all walks of life. We need both of our hands on the keyboard most of the time.
    • You can turn on accessibility features. This one really galls me. Nowhere is the difference between Apple's blowhard marketing and the reality more apparent. Indeed, you can activate an accessibility key to get into the menu. Once you are in the menu, you have to use the arrow keys. What happens if you hit E? Will it open the Edit menu? No, sir. The all-knowing keepers of wisdom in Cupertino know that this would not be good for you. The key is silently and unobtrusively ignored. "Apple’s commitment to accessibility is evident throughout the Mac OS X operating system which is by design, easy to use, but also includes a wide variety of features and technologies specifically designed to provide accessibility to users with disabilities."
  3. Freedom. I run Mac OS X 10.3 on my aging PowerBook, and I like it just fine. Can I try out Java 5.0? Not without paying for and installing Mac OS 10.4.

Ok, you want good Java support, keyboard shortcuts, and freedom. Who doesn't? But isn't Linux hard to use?

For my needs, Gnome seems on par with the Mac OS X and Windows GUIs. In the bad old days, laptop suspend and USB stick mounting worked poorly, but those issues have been fixed. The remaining difficulties are (1) wireless and (2) external displays. Wireless with an external $20 card works beautifully, but some of the built-in adapters can cause grief. Get a $20 card. External display switching is not as easy as it should be. Practice it at home before you give that presentation.

If you are considering switching, do yourself a favor. Stay on your old platform a little longer and switch to cross-platform applications first. For mail, use Thunderbird, not Outlook or Mail.app. You don't want your mail trapped in some proprietary format. Use OpenOffice for word processing and spreadsheeting. You increase your chances of being able to read your files twenty years from now. Use Eclipse or Netbeans or JEdit or Emacs for your Java programming. On Windows, use the bash shell (Cygwin), not the DOS shell. After you are comfortable with the cross-platform applications and tools, make the switch. It'll be easy. And if you don't like your new OS, you can switch again.

???After you switched to Linux, there is one remaining problem--the coolness factor. Nobody can see that you aren't running Windows. The solution is, of course, a Linux sticker. Pick up a garish one at a trade show, or print one from here, here, or here, and slap it on your laptop.

???As any mad scientist would do, just to prove that it can be done, I decided to build Mustang on my shiny new Ubuntu 6.06 "Dapper Drake" system. The build instructions warn you not to try this at home. The official build environment is Redhat Enterprise Advanced Server 2.1 update 2, formerly known as Redhat Advanced Server 2.1 update 2. Ugh.

I was not sure that this was going to work. Ubuntu is Debian based, and it uses GCC 4.0, not GCC 3.2. As it turns out, the X11 headers have recently moved to around, so I had some grief. But it all turned out well, and I lived to tell the tale.

Why would anyone care? If you use Linux as your primary work OS (as I do), and you switched to the increasingly popular Ubuntu, and you want to tinker with the JDK, these instructions will save you time and frustration. (Not that much time, maybe. It took me less than a day to hack my way through.) For everyone else, it may be comforting to know that someone with very limited system programming experience can do the build in an unfamiliar environment. It certainly is a tribute to the Mustang team who put this very complex build together.

Here are the instructions.

  1. In addition to the basic GCC tools, you need the following packages installed:
    • ksh
    • gawk
    • m4
    • libasound-dev
    • libcupsys2-dev
    • libx11-dev
  2. Install the binary Mustang distribution, e.g. in /home/me/jdk1.6.0
  3. You need a binary 1.5 distribution, e.g. in /home/me/jdk1.5.0
  4. Make directories for the source and the output, e.g. /home/me/mustangsrc and /home/me/mustangout
  5. Download and install the JARs
    cd mustangsrc
    java -jar ../downloads/jdk-6-rc-src-b87-jrl-09_jun_2006.jar
    java -jar ../downloads/jdk-6-rc-bin-b87-jrl-09_jun_2006.jar
    java -jar ../downloads/jdk-6-rc-mozilla_headers-b87-unix-09_jun_2006.jar
    
  6. Set environment variables
    unset JAVA_HOME
    export ALT_BOOTDIR=/home/me/jdk1.5.0
    export ALT_DEVTOOLS_PATH=/usr/bin
    export ALT_GCC29_PLUGIN_LIB_PATH=/home/me/jdk1.6.0/jre/plugin/i386/ns7-gcc29
    export ALT_MOZILLA_HEADERS_PATH=/home/me/mustangsrc/share/plugin/
    export ALT_OUTPUTDIR=/home/me/mustangout
    export MILESTONE=rc
    export BUILD_NUMBER=b87
    
  7. In Ubuntu, basename is in /usr/bin, not in/bin. In j2se/make/common/shared/Defs-utils.gmk, change the definition of BASENAME to 
    BASENAME       = $(UTILS_USR_BIN_PATH)basename
    
  8. To avoid the error execvp: cp: Too many levels of symbolic links, make these changes:
    • In hotspot/make/Makefile, change the definition of CPfrom cp to /bin/cp:
      CP=/bin/cp
      
    • The file j2se/make/docs/Makefile has two cp that should be $(CP), in the tagletapidocs:target.
  9. GCC 4.0 is pickier than GCC 3.2. Therefore, we won't fail on warnings. Comment this out in j2se/make/docs/Makefile:
    # WARNINGS_ARE_ERRORS = -Werror
    
  10. GCC 4.0 complains when redefining an extern function as static. Comment out the word static in these function definitions:
    • j2se/src/solaris/hpi/native_threads/src/interrupt_md.c:113: error: static declaration of ‘sigignore’ follows non-static declaration
    • j2se/src/share/native/sun/awt/cmm/spsearch.c:1335: error: static declaration of ‘TestFileCB’ follows non-static declaration
    • j2se/src/share/back/error_messages.c:42: error: static declaration of ‘vprint_message’ follows non-static declaration
    • j2se/src/javaws/share/native/launchFile.c:66: error: static declaration of ‘iksSecureProperty’ follows non-static declaration
    • j2se/src/javaws/share/native/xmlparser.c:53: error: static declaration of ‘RemoveNonAsciiUTF8FromBuffer’ follows non-static declaration
    • j2se/src/solaris/native/sun/awt/awt_dnd.c:172: error: static declaration of ‘xerror_code’ follows non-static declaration
  11. In j2se/src/solaris/native/sun/awt/awt_motif21.c, comment out this declaration:
    /*
    extern XIC XmImGetXIC(
                                    Widget          w,
                            uint32_t    input_policy,
                            ArgList         args,
                            Cardinal        num_args) ;
    */
    
  12. I fixed the error PIC register ‘%ebx’ clobbered in ‘asm’ in j2se/src/solaris/bin/java_md.c by commenting out %ebx in the clobber declaration
     "%eax", /* "%ebx", */ "%ecx", "%edx"
    
    I'd like some expert to tell me if that is ok.
  13. Ubuntu 6.06 has a different location for several X11 files. Make these changes:
    • In motif/lib/Xm/util/Makefile, change IRULESRC to 
      IRULESRC=/etc/X11/config/cf
      
    • In j2se/make/sun/xawt/Makefile, change /usr/X11R6/include/X11/extensions to /usr/include/X11/extensions:
      ifeq ($(PLATFORM), linux)
        CPPFLAGS += -I/usr/include/X11/extensions -I$(MOTIF_DIR)/include -I$(OPENWIN_HOME)/include 
      endif
      
      and in j2se/make/sun/awt/mawt.gmk, change /usr/X11R6/lib to /usr/lib (3x):
      LIBXT = /usr/lib/libXt.a
      LIBSM = /usr/lib/libSM.a
      LIBICE = /usr/lib/libICE.a
      
  14. The build instructionstold me to run make scsl, but then I got an error complaining about a missing jscheme/REPL. Following this advice, I picked up with make j2se, and voila, it worked.

  15. ???It walks, it talks! 
    cd /home/me/mustangout
    bin/java -jar demo/jfc/SwingSet2/SwingSet2.jar
    

???When you start a JPA client application, tutorials such as this one tell you to use this mysterious incantation on the command line:

java -javaagent:toplink-essentials-agent.jar client.Client

What is the reason behind that? Go to the GlassFish forum and search for javaagent. You'll get lots of confused queries. (Ok, most of them are mine.) The gist of the friendly advice that I got seemed to be that the toplink-essentials-agent.jar file was required for lazy loading.

Lazy loading means that you can fetch an object from a database without also fetching all of its dependent objects. Here is the canonical example:

In a multiple-choice testing application, a Questionhas a number of choices:

@Entity
public class Question implements Serializable {
    private int id;
    private Collection<Choice> choices;
    . . .
    @OneToMany
    public Collection<Choice> getChoices() {
        return choices;
    }
}

Now let's say we load a Question object from the database. Should all choices be loaded as well? And what about the objects that are related to Choice objects? That can be dangerous--suppose a Choice remembers all the questions that contain it? Clearly, something has to give, or we pull out a potentially huge transitive closure with every query.

That's where lazy loading comes in. You can mark relationships so that they are fetched lazily. By default, collections are always fetched lazily. For example, consider this code:

Question q = em.find(Question.class, id);
System.out.println(q.getChoices());
System.out.println(q.getChoices().iterator().next());       

It prints

{IndirectList: not instantiated}
elvis.entity.Choice[id=1,text=run anywhere]

Clearly, our collection isn't a humble array list. Indeed, calling q.getChoices().getClass() reveals that it is aoracle.toplink.essentials.indirection.IndirectList.

Ok, so I tried omitting -javaagent. What would happen to my lazy collection? Would it be eagerly fetched? Would my keyboard melt into a puddle of plastic if I accessed it? To my surprise, nothing special happened. The output was exactly the same. What, if anything, did the agent do?

???I finally figured it out, thanks to reading this article very carefully. You need -javaagent for one specific purpose, to lazily load one-to-one and many-to-one relationships. (If you knew this and you read this far, I am sorry to have wasted your time. This blog entry is for Elvis, not Einstein.)

We had a one-to-many relationship, so the agent didn't care. Let's add a lazily fetched one-to-one relationship:

@Entity
public class Question implements Serializable {
    . . .
    private Choice answer;

    @OneToOne(fetch=FetchType.LAZY)
    public Choice getAnswer() {
        return answer;
    }
    . . .
    public String toString() {
        return getClass().getName() + "[id=" + id 
                + ",answer=" + answer"]";
    }
}

Here we need to specify the fetch type since one-to-one relationship are eagerly fetched by default.

Now we'll be able to tell the difference between running with -javaagent and without.

Question q = em.find(Question.class, id);
System.out.println(q.toString());
System.out.println(q.getAnswer());       
         
With -javaagentWithout -javaagent
elvis.entity.Question[id=3,answer=null] elvis.entity.Choice[id=1,text=run anywhere]elvis.entity.Question[id=3,answer=elvis.entity.Choice[id=1,text=run anywhere] elvis.entity.Choice[id=1,text=run anywhere]

Both outputs are interesting.

???With -javaagent, the answer instance field is null, but the getAnswer method doesn't simply return the instance field (even though our code says it does). The agent intercepted the loading of the Questionclass and edited the bytecodes of the getAnswer method so that it fetches the result. (This process of on-the-fly bytecode modification is called "weaving" by the cognoscenti.)

Without -javaagent, lazy fetching has been disabled.

Why was the agent necessary? With theCollection<Choice> field, the persistence provider can simply set an object of a class that implements the Coll ection interface and that does the lazy loading. There is no need to modify the bytecodes of the class. But one can't do that with a single object. The Choice field must be set to aChoice object (or to null), and the getter must be modified. Mystery solved.

Unfortunately, I haven't yet found a way to actually see the modified bytecodes, but if you set a breakpoint in the debugger and inspect the Question object in the debugger, you can see a field that was added to the class:

???

???Here is the executive summary in terms that Elviscan understand:

  • -javaagent:toplink-essentials-agent.jar is only useful when you have one-to-one or many-to-one lazily fetched relationships.
  • If you omit it, your program will still work, but one-to-one and many-to-one relationships will be fetched eagerly.

Here is a simple code example from the JBoss EJB3 tutorial.

@Entity
public class LineItem implements java.io.Serializable
{
   private int id;
   private double subtotal;
   private int quantity;
   private String product;
   private Order order;

   @Id @GeneratedValue(strategy=GenerationType.AUTO)
   public int getId()
   {
      return id;
   }

   public void setId(int id)
   {
      this.id = id;
   }

   public double getSubtotal()
   {
      return subtotal;
   }

   public void setSubtotal(double subtotal)
   {
      this.subtotal = subtotal;
   }

   public int getQuantity()
   {
      return quantity;
   }

   public void setQuantity(int quantity)
   {
      this.quantity = quantity;
   }

   public String getProduct()
   {
      return product;
   }

   public void setProduct(String product)
   {
      this.product = product;
   }

   @ManyToOne
   @JoinColumn(name = "order_id")
   public Order getOrder()
   {
      return order;
   }

   public void setOrder(Order order)
   {
      this.order = order;
   }
}

64 lines.

Here is what you want to read:

@Entity
public class LineItem implements java.io.Serializable
{
   @Id @GeneratedValue(strategy=GenerationType.AUTO) 
   @Property private int id;
   @Property private double subtotal;
   @Property private int quantity;
   @Property private String product;
   @ManyToOne
   @JoinColumn(name = "order_id")
   @Property private Order order;@
}

12 lines.

That's only 20% of the original. The remaining 80% is boilerplate.

???

And that's not even counting the Javadoc!

There has to be a better way. My graduate student Alexandre Alves has provided just that in his M.S. thesis. He implemented

  • an extension to the Mustang javac compiler
  • a Javadoc doclet
  • an optional annotation processor
  • Ant tasks for all these

His tools support the @Property annotation of the preceding example. Simply call his propjavac, and it synthesizes the getters and setters and adds a Propertiessection to the Javadoc.

But wait, there's more. We give you syntax for accessing the properties, almost like in JavaScript or Visual Basic:

LineItem item = new LineItem();
item.@subtotal = 100; 
   // calls item.setSubtotal(100)
em.persist(item);
int id = item.@id; 
   // calls item.getId()

We chose the .@ operator rather than the simple dot in JS/VB so that there is no confusion with the field accessitem.id.

Alexandre worked out the pesky details with visibility, read-only properties, BeanInfo, inheritance, and so on.

But there is still work to be done.

  1. If this is to be a feature of Dolphin, we can't use annotations to generate the getters and setters. Annotations aren't supposed to modify the file in which they are contained. It doesn't seem likely that we can introduce a property keyword either. I suppose we could reuse super . . . NOT. But what about 
    private int @id;
    private Order @order;
    
    That's just a token, which doesn't break backwards compatibility because you couldn't have an annotation at that spot. Too subtle, or just right?
  2. Alexandre's property annotation has attributes for finetuning the code generation, such as making read-only properties, package visible getters and setters, etc. That's harder to do with just syntax. Does one need these features, or should one go by the 80/20 rule? After all, one can always write plain Java for the non-standard cases.
  3. The JavaBeans specification has lots of seldom-used advanced features, such as BeanInfo, indexed properties, property change listeners, and so on. Some of them can be supported easily, others are trickier. I'll gladly elaborate if anyone is interested. Does anyone use these regularly, and if so, do you need boilerplate relief? For example, the JSP/JSF EL doesn't even deal with indexed properties.

I really want native properties to happen in Dolphin. I am sick of the boilerplate. Two years ago, I thought it would be too late, but I have learned from EJB3 that Java can do the right thing...after we've tried everything else.

???

Several years ago, I switched from Emacs to Eclipse. It was a bit painful at first, but Eclipse had two killer features that, once I discovered them, I could not live without.

  • Autocompletion
  • Refactor -> Rename

I have since come to love Eclipse for many other features, small and large, but those were the ones that made me switch. The instant productivity gain was worth the pain of switching.

I now do a lot more server-side programming, with JSF as the presentation technology. But I am not a happy programmer—I feel unproductive because I always get the Stack Trace From Hell.

???

(To see the image in glorious detail, right-click and select "View Image".)

At JavaOne 2006, I saw all the snazzy NetBeans demos, and I did what I did after every JavaOne: Download and install NetBeans (or Forte or Sun Studio or whatever it is called in a given year), try out the demo, wince, and uninstall. But this year, something funny happened on the way to the forum. I loaded a JSF app into NetBeans (not as painful as it used to be), configured GlassFish (ridiculously easy...) and ran the app. I noticed the Run -> Debug Main Project menu. I tried it, fully expecting it to fail. But there I was, with my app stopped at a breakpoint in a managed bean. Hmmm.

netbeans-debugger.png

This time I didn't uninstall NetBeans. A few days later, back in Eclipse and Ant, I got another STFH. An hour of headscratching, inserting logging statements, redeploying, and poring over the logs brought no enlightenment. In desperation, I fired up NetBeans and used the debugger to step through my broken component renderer code. Five minutes later I had the bug fixed.

Then I fixed the next bug in five minutes, using the nifty HTTP monitor:

???

I was actually being productive.

Can I do all that in Eclipse? Well, maybe. I asked on the GlassFish forum, and the only answer I got was a pointer to this article. Hmmm.

There is a JSF plugin that's pre-0.5 and requires WTF 1.5 and a whole army of subsidiary plugins, and an experimental GlassFish plugin that requires WTF 1.0. I tried installing that and hosed my Eclipse installation. Note to self: Try this in a separate Eclipse sandbox next time.

I stuck with NetBeans for the remainder of my project. It isn't as slick as Eclipse and it looks positively hideous--look at the four (!) kinds of tabs in the preceding screenshot.

???

My eyes hurt, but I was more productive. I discovered other nifty features. Autocompletion in JSF pages works pretty well, probably as well as it can ever work, given the dynamic nature of the EL. Running the verifier inside NetBeans is less tedious than from the command line, so I did it more often. All this upfront checking meant fewer occurrences of the STFH.

If you know a working Eclipse plugin or an inexpensive Eclipse derivative that supports JSF 1.2 with autocompletion, debugging, HTTP monitor, etc., I'd love to know. If you don't, give NetBeans 5.5 a try.