A large number of texts touch on the basics of J2EE code; they refer to 'well written Java' or 'using patterns'. Most enterprise projects will also define guidance for developers in this area; the ubiquitous 'coding standards' and 'implementation framework'. If you are the kind of developer/designer who can't cite a standards compliant, well written peice of Java source code and explain the patterns used to make that code effective in its framework then the rest of this document is for you. If you can point at such a peice of code, but you don't know what J2EE is then reaffirm your confidence by reading on.
J2EE code (the business glue) could loosely be classified as a subset of the Java language - mainly because it is resident in the J2EE environment, where a large number of common programming problems are solved through the use of the various APIs. Unfortunately, owing to the way J2EE has evolved, there is no obvious dichotomy between what is usually provided by the API environment and what should be provided by the business orientated glue code. However this has been improved by J2EE 1.4.
What follows is a mixture of 'steers' for the developer, the project manager, the tester and the configuration manager. The aim is to provide a well rounded expression of what it takes to get a J2EE codebase started and organised.
Mantra - 'abstraction, abstraction, abstraction, ...'
This directory is a good place to start. Pick one and stick to it. If you don't know which one to pick then pick Scott Ambler's because it is easy to read. Some tools work best with the Sun standards.
There are other definitions of this acronym, but this one best summarizes the point. The point is that 99% of good J2EE code is boringly simple. The majority of methods have one or two lines, synchronization is almost never used and there are typically only a couple of member variables. The reason behind this is that J2EE provides a number of runtime environments for your code (e.g. servlet container), and the more benign and environment neutral you code is the better.
Another acronym that helps is the DOT - 'Do One Thing' class description. And similarly the IDT - 'I Do That' class description. 'Ignorance is Bliss', if a class knows nothing of its environment then it is less likely that changes to that environment will hurt it.
Javadoc is a good indicator of simplicity; if you cannot describe entirely the class or method in one or two sentences then it is probably too complicated. Also you can look at the import list; if you deal with more than one J2EE API or reference multiple business tier components in a class then it is probably too complex.
With this style of coding there are two main issues:
The latest batch of IDEs and build tools make the whole process of writing J2EE code much easier to deal with. Even the free tools provide most of the features you could want. Eclipse provides a good editor (generate getter/setter, formatting, templates, auto completion, quick fix), powerful refactoring (extract interface, extract method, externalize strings) and a variety of J2EE plug-ins. Apache ANT does the building; and with things like xdoclet source generators and JMX plug-ins it can be a very powerful tool.
See this directory for more details, as th e list of useful tools and features just keeps on growing.
Third party libraries (TPLs) are nearly always required by a project. In the same way as your project needs a coding standard, you should have a way of using TPLs.
Every time you reference a TPL directly you tie that code to the TPL, and so every time you upgrade the TPL or change supplier (e.g. switch from log4j to java.logging) then you are required to rework those classes. If you hide the TPL behind a generic interface then you gain the ability to switch and upgrade your service provider without necessarily impacting your applications. Conversely you can define a contract for each TPL service that you can test against on each upgrade. If the interface to the TPL changes vastly so that your interface has to change then it is probably not a good TPL to be using.
A note about 'util' packages: Almost every project starts in its early days a 'util' package. Most 'util' packages contain implementations of the same classes and paradigms. You should seriously consider using a TPL 'util' package like those of Apache Jakarta or 'util.concurrent' and not much else. The reason is that most project 'util' classes are implemented with that project in mind, and so they act as more of a drag on the reuse of project code than a help because they are tuned for a specific environment. So when providing code to another project environment then the TPLs used by the imported code only need to be linked into the TPLs of the new project.
If you are getting bored already and you don't want to read much more then just read this section and leave it there.
In the J2EE world your code will have to run not only in the context of other code you have written, but also in the context of other people's code which may potentially do some very different things.
There are two ways you can improve your chances of surviving an alien code context.
In any case you should make sure the documentation reflects what the class is capable of handling.
Java provides interface definitions for a reason, the reason cited for normal Java is so that you can implement multiple inheritance, but the primary reason for J2EE is so that you can abstract your implementing classes.
If every reference to your component in a system used its interface definition and not its actual class then you can modify your internal component implementation without fear of 'breaking' your client components. In doing so you can make your class definitions package private and almost guarantee that no-one misuses your component.
If you use interfaces by default in J2EE code, then later when you rework your components you will already have the interface definitions ready to use. Also you can regression test components by their interface.
But what about constructors? I hear you cry. Quite right - you cannot use interfaces to define constructors. The solution is to try and only use the default constructor in your implementation and then define a factory or registry to be able to get access to implementation objects. This may appear to be a headache, but not having a business level constructor can be a blessing in disguise as many J2EE constructs like EJB and object pools demand default construction.
Putting your interfaces and implementations in separate jar files also means that upgrading your component implementation is as simple as dropping in a new jar file. If this approach would suit your project then you should consider how the definition of the factories or registry would fit into your project environment.
As a rule of thumb, all your non-private (non constructor) method parameters, return types and member variables should be either java.lang classes (rather than primitives) or interface definitions.
A classic mistake by budding OO developers in Java is to overuse extend. F or example a novice may choose to extend EventListenerList to get its functionality in their class 'for free', even though no access to the protected interface is required. This works fine, but you risk exposing the other methods of the inherited class like removeAll() and you close off an avenue for abstraction - the parent class. The parent class in this case is a private abstraction of common implementation behaviour rather than common public behaviour.
You only have to extend a Java 1.4 Exception class to realise why parameterised constructors can make things complicated. I am not suggesting that such powerful language constructs as exceptions should be made interfaces (and so allowing wider use), as that would encourage mis-use of exceptions in a program's flow of control.
J2EE has many environments where parameterised constructors are not very helpful. The obvious one is EJB, but you can imagine any pool or reflective factory being made more complicated by parameterised (non-default) constructors.
So what is the solution? A common approach is to define a lifecycle for objects. This usually involves defining an interface with methods like initialize(), clean(), recycle() and destroy(). You can imagine that pooled resources that implement such an interface can now be enhanced to recycle objects safely. Please notice the presence of clean(), this is important as the object may hold hard references to other objects and so prior to cleaning will prevent those objects from being garbage collected.
The drawback of using initializers rather than constructors is that the code is less able to be checked by the compiler, so in response you need more defensive programming.
When your component interacts with its surroundings you have the choice of either making method calls on other objects or firing events (there are other ways but less common).
The key difference between making the call or firing the event is one of multiplicity; if you call a method you do so on one object, whereas if you fire an event you can interact with none, one or more objects. So when you take your class from one project and plug it into another, if you have used events then there is little or no integration to do.
However, you should be aware that events do not support the request/reply interaction pattern. Sometimes an event producer can hold on to object references in its listener list that should have long since been garbage collected, for this reason I would suggest perhaps holding Reference objects rather than the original link in the listener list.
There are a few advantages to using events heavily, firstly you can add monitors as additional listeners to objects, secondly you can implement event queues at thread interaction points to avoid some of the common synchronization problems and thirdly events permit the relocation of resources to remote VMs (for example in software agents) more easily than RMI.
I have already mentioned some techniques which could be
classified as
These are classes whose only role (DOT) is to pull together 2 or more business objects of your system. They represent the binding relationship between the composites, but do not go as far as offering the behaviour of their composites. They remove the need for the peer classes of the relationship to know about the relationship. The implementation would typically delegate to a java Collection.
These are three forms of basically the same concept, interface limitation. Their role is to limit the interaction between the caller and the callee classes.
Delegates are used where the nature of the interaction is a subset of the caller's behaviour but the majority of the callee's (IDT). Proxies are used where the nature of the interaction is a subset of the callee's behaviour. Facades are used to rewire interactions to fit the environment (usually that of a legacy system).
If you are dealing with an interaction that is a subset of both the caller and the callee's behaviour then you should consider refactoring your callee or using an event interaction. There is no harm in defining multiple interface definitions for you components.
These are classes that do the hard work of firing events to listeners. They abstract the functionality involved with event delivery from the business entity.
They need to have a strategy for dealing with errors thrown by event handlers and a strategy for the flow of control of the delivery. You can use different kinds of dispatchers as you require, some could work in a dispatch thread doing background delivery, others could delegate (or gossip) to middleware, and others can just call the handlers directly.
These classes are used to 'bind' an implementation class to an interface. The factories themselves can come from a factory if necessary. As for how the factory gets hold of the implementation is very specific to how you want to structure your project.
There are two main ways to 'bind', the most common is to use Class.forName and then call the default constructor, the other way is to use serialized objects to create the implementation specific part of the factory. Some factory implementations have replaceable products and fire events to indicate changes in product (for example, you log provider goes to System.out until your network component can start syslog sending). Others use blocking queues to ensure factory product dependencies. The project naming conventions can be used to create an automatic mapping from interface to implementer and/or factory. Some systems with live software update requirements will have search paths for factories and/or their products.
These classes are like factories except that they always give you the same object in a given scope. Singletons are normally associated with a single object per VM but used to create single objects per application, which is not a good concept in J2EE as the application and the VM are different levels of scope. I prefer to refer to these as Accessors, where you can define the scope of each accessible object.
If you make the scope local to that of each access then you have a factory; if you make the scope each VM then you have a singleton pattern instance. Like factories you can scope and bind each accessor request in any manner you wish. A combined accessor/factory framework for your J2EE application is certainly a useful abstraction.
These classes remove the responsibility for some aspect of a primary class role. Helpers do the job of preserving the ignorance of the primary class. The helper is there to help the primary class by encapsulating a certain piece of knowledge or functionality. In a way they declutter the code of the primary class.
Helpers do not normally have any state associated with them, and their methods are often best modelled as static. The class java.lang.Math is a good example of a helper class. I often find that extracting an interface to a helper class from a complex piece of new code provokes thought on the more unimaginable error conditions, which can lead to a more robust implementation.
Refactoring is like tidying your desk or sorting your email - it is just something you have to do. As my wife tells me 'it should be something you do as you go along'. We all know that there are times when things just get messy.
Anyone who has worked on a large project has probably come across the scenario where their one line change (boss agreed half day code/test/deliver) just tipped the balance on a design issue that led to a week long refactoring exercise.
This section is about building refactoring into the process and not being scared to change things.
Most developers get attached to the code they write, it is their work of art. Some people stylise their javadoc, others use cunning variable names but the majority exalt their unbeatable implementation. For example, one of the most common objections from developers to using automated code formatting tools is that they can no longer style the source the way they want to.
Refactoring other people's code, for whatever reason, is likely to cause friction and resentment. The biggest factor in avoiding friction when refactoring is the project lead's interpersonal skills. But to give them a head start you should try and de-personalize all of the project artefacts; this can be done by not using @author tags and forcing all source to be uniformly formatted. If you must use author tags then give the author of the designer not the implementer.
The objective is to make it very hard for the bug finder or refactoring developer to directly blame someone. Once I found a bug in a particularly shoddy piece of code, 'who wrote this pile of s***' I nearly said out loud, and it was only when I checked in the new improved version that I realized that I had written the original 9 months earlier.
A good way to explain why someone's code needs to be refactored (rewritten) is to refer to the code structure; the most commonly refactored areas are those that are either critical to the software architecture or those that are influential in critical system requirements. In either case the original developer should be proud that they 'blazed the trail' and did it first. That tends to leave them in a more positive frame of mind than the usual 'RTFM' or 'how many years did you say you have been doing this?'.
If a good developer has difficulty trying to stick to the guidelines here, then there is normally a problem with the design and/or architecture. In cases where the code cannot be KISS or DOT then the developer should get in the habit of informing the designer/architect. It may be that there is no other way, but most of time it is an early indication of deeper architectural issues and limitations.
The project should use source code metrics analysis, either as part of the code review process or as an occasional exercise, in order to identify potential refactorings and issues. There are a number of Java tools becoming available that allow you to search for code duplication; these are also useful forms of feedback.
You should extends the refactoring process to include the package classification of your classes. While I am not going to join the argument about how to package Java code, I will say that the greater the number of packages the more abstract the code can be. And with modern tools searching for the right class is not too much of a burden.
Regularly reviewing the contents of a package to check the cohesion and completeness of its contents is a valuable activity. So if you keep your class names accurate then this should be an easy task.
Refactoring can cause havoc with your
One solution to this problem, and one that encourages a lot of the points made in this text, is to use zip files as the source unit to control. To explain further:
You have two source code zip files for each component, one for the interfaces and the other for the implementations (alternative: zip for implementations, java source files for interfaces). The zip files are the elements that are source controlled, and can be stored without compression if you like. When a developer accesses the source code for a component they unzip the controlled unit and then have a local writable form in a source tree of their choice. This makes it easy to limit the bounds of the refactoring steps and to free the developer from any constraints regarding CM structure. You can set up the build process to automatically update the zip file when some source code has been updated. Most developers will only need to interact with the interface zip files. A build process that takes a zip file expands it and then builds its associated jar/war/ear file could easily be established. Dependencies between zip files could be more readily modelled in the CM environment making incremental releases more manageable.
Many developers in a hurry forget that testing is a valid step in any development, even if it is only a mental walk through of the code. I am not going to go into all the ways of testing J2EE code and how to use them, but I do want to talk about the testing artefacts.
With a J2EE style component you have already abstracted the implementation from the interface, and defined a factory capable of delivering implementations as configured. The first product of a component's implementation after the interfaces are completed should be the stub (or mock ) implementation. The reason for this is that other developers will then be able to not only code against your components interfaces, but also test against them, before version 1.0 of the real implementation hits the streets. This is a simple form of early integration.
Furthermore, stub implementations can be written in such a way that they can delegate to the real code inside the test environment to exercise the real interface implementation when available.
With all these interface classes, stubbed implementations and their respective deployment environments to is worth considering generating the fundamental parts of you code framework. This technique would mean that a developer would create or update the core implementation class, and (with the help of some hints) then generate the interfaces, factories, parsers and test classes required.
There is a danger here of the tail wagging the dog, in other words, that the generator limitations shape the code in some way. But provided the role of the generator in the build process is well scoped then there should not too much difficulty.
It has always been good practice to create configuration files, but
the traditional Java properties files are limited. XML builds on
this fine tradition allowing both a tighter integration with the
environment (like
But configuration is only one use of XML. Because XML can enforce a structure (DTD) then it can be used as a suitable format to interacting over weakly typed interfaces. In the same way that your Java classes provide a toString() method, then you should consider making all your project classes Markupable and thus implement a toXML() method and perhaps even go as far as having class name based namespaces and having static getDTD() methods on the classes.
Why? Apart from the obvious uses with persistence, debugging and tracing,an implementation such as this could form the basis of an agent based architecture(assuming a corresponding initialiseWithXML method),similarly the code could exist in a data centricrather than process centric environment. A relational database is an example of a data centric environment.
The J2EE world has a number of XML helpers available to aid such solutions.
Are you still chanting the mantra? - 'abstraction, abstraction, abstraction...'