In 1998 Sun introduced their distributed server-side component architecture under the name of Enterprise JavaBeans (EJB). Since then, the EJB technology has seen a widespread acceptance throughout the industry. The "write once, run anywhere" philosophy embraced by the EJB specification is undoubtedly a major factor in its success. An EJB component can be built once and then deployed on different platforms without recompiling or altering the source code.
The concept behind the EJB API is simple: let the application programmer concentrate on writing business logic and shield him or her from complex system-level services. To achieve this the EJB components must operate within a controlled runtime environment called the container. This container is responsible for all the system-level services, such as instance pooling, transaction handling, security, exception handling, and data caching. To provide the EJB component with these low-level services, it's mandatory that the EJB interact only with the container and the resources provided by it.
Not having to write system-level services comes at a cost. To ensure portability across containers from multiple vendors, the EJB developer is expected to operate within the framework provided by the EJB specification. This means there are restrictions on certain features that are normally available to the Java developer. The specifications of the runtime environment (EJB 1.1 ch18, EJB 2.0 ch24) list these restrictions.
Some restricted features such as changing the socket factory of ServerSocket are rarely used, and this article won't elaborate on these types of restrictions due to the fact that few developers will be exposed to this level of programming. However, there are restrictions that can have a significant impact on your usual coding techniques.
In this article I'll state some of the programming restrictions as defined in the EJB specifications 2.0 (almost the same as the 1.1 spec) and try to explain the reason for the restriction. Where possible I'll also include an alternative approach to reaching your goal without violating the restriction.
Before diving into the details of the restrictions, it's important to clear up one of the greatest misconceptions in EJB programming. Many believe that these restrictions apply only to the code they write within the EJB's classes and not the helper classes used by the EJB component. This is wrong. The EJB components run within the container, hence all classes that run within the container are subject to the same restrictions.
An enterprise bean must not use read/write static fields. Using read-only static fields is allowed. Therefore, it is recommended that all static fields in the enterprise bean class be declared as final.
This restriction is simple but it has the greatest impact on a programmer's normal coding routines. When using a static field (class variable) you expect all your instances to access the same field. If you're operating within one JVM, this is a correct assumption; however, the EJB server can run multiple JVMs for performance or load-balancing reasons. Now each JVM contains one or more instances of the EJB and these instances no longer reference the same field. In addition, there's the problem of concurrent access to the static field, because in the EJB specification the use of the threading API is also restricted, so no synchronization mechanisms are available (more on this later).
To use a static field you'll have to declare it to be read-only. A final static field cannot be changed after it has been initialized, and instances across all JVMs will now be consistent.
One problem facing many programmers is how to implement the Singleton pattern (one instance of a particular class in the whole application) in an EJB when the use of static fields is restricted. Some argue that using a JNDI to store a reference to your instance can solve this problem, but I haven't seen a foolproof implementation that works. You might be dealing with the same instance but you still have the concurrency issue to contend with. Furthermore, a Singleton EJB would by definition run in a single container, and that means introducing a single point of failure. Since the EJB architecture is all about load balancing and fault tolerance, I believe the Singleton pattern should not to be used in distributed EJB environments.
If you want to use a Singleton for a nonblocking and read-only service and it's not a problem having one instance in each JVM or for each deployed ejb-jar's classloader, then the use is warranted. An example of these services is the caching of data retrieved through expensive JNDI calls. You just have to realize that it's not a Singleton in the classic form and use it accordingly.
An enterprise bean must not use thread synchronization primitives to synchronize execution of multiple instances.
As mentioned before, the same EJB can be run in different JVMs. It's up to the container to take care of the object's life cycle and any concurrency issues. Therefore, declaring an EJB component's method to be synchronized is a violation of the specification. The utility classes used within the EJB, such as the Collection classes, are allowed to use the synchronized keyword on its methods or blocks of code because these are all dealing with single instances.
An enterprise bean must not use the AWT functionality to attempt to output information to a display, or to input information from a keyboard.
This one is obvious. The whole concept of separating the GUI layer from the business logic layer is compromised if direct interaction is permitted. Most EJB servers will allow you to print to the screen from within the container, which can be useful at times during the development process (debugging, etc.); however, it should not be required in a production system.
An enterprise bean must not use the java.io package to attempt to access files and directories in the file system.
According to the specifications the file system is not an appropriate mechanism to access data. Since file systems are not transactional by nature, it's a valid argument. Another problem is that there's no resource manager involved in java.io operations so the container has no control. Now if an EJB uses files on a system with heavy client loads, there's the chance of running out of file descriptors and bringing down the system.
Java developers are accustomed to using property files in their applications because they're helpful in specifying configuration settings. A simple mechanism within EJBs called environment entries provides these parameters. These environment entries are placed in the deployment descriptor and consist of a name, type, value, and description. The type can be any of the seven primitive wrapper classes (Integer, Boolean, etc.) or a String object. The entries are retrieved through the bean's environment, naming context. The following code provides the deployment descriptor of an EJB:
To retrieve the entry in your code use:
<description>the example environment entry
Context ctx = new InitialContext();
A legal method to read information from files within an EJB component is to use the classloader. The container has granted the classloader permission to use the java.io package, therefore it's possible to use the getResource() or getResourceAsStream() method defined in java.lang.Class to load in a file.
Context env = (Context)ctx.look-up("java:comp/env");
String myValueString = (String)env.lookup("entryname");
When using a file system it's best to access it through a resource manager the same way you use the javax.sql.DataSource connection factory to obtain a connection. To gain access to a resource (any entity that can be accessed through a URL string) use a JNDI lookup to get a handle to the java.net.URL object and use it to obtain a URLConnection. By using this mechanism to access a file, the resource is decoupled from the application code using the resource. For the application that's using the resource, there's no difference between the local file system and one located on the opposite side of the world.
To achieve the flexibility and scalability of using a resource manager for URL entities, a resource manager must be set up in our application server. The method to declare a resource manager is vendor-dependent, so consult the container's documentation. Once this is done, all that remains is adding an entry in the deployment descriptor of the bean and the application can use the resource. The following code provides the deployment descriptor of an EJB using a URL resource:
To retrieve the resource in your code use:
<description>the URL of the resource
/ res-ref-name >
<res-type> java.net.URL</ res-type >
<env-auth>Container</ env-auth >
</ resource-ref >
Context ctx = new InitialContext();
An enterprise bean must not attempt to listen on a socket, accept connections on a socket, or use a socket for multicast.
java.net.URL url = (java.net.
java.net.URLConnection conn = url.openConnection;
//Now create an input stream for reading
java.io.InputStream is = Conn.getInputStream();
// or an output stream for writing to the resource
java.io.OutputStream is = Conn.getOutputStream();
In other words, an EJB is not allowed to act as a network server. This makes sense since clients are supposed to connect to the EJB using a specified remote protocol that's in-line with the implemented security permissions. When a client uses a socket to connect to the EJB, it's a potential security hole. This does not limit the use of a network socket client from within your EJB. An example of such a socket client is a stateful session bean that queries an existing inventory system that can be accessed only through a TCP connection.
The enterprise bean must not attempt to query a class to obtain information about the declared members that are not otherwise accessible to the enterprise bean because of the security rules of the Java language. The enterprise bean must not attempt to use the Reflection API to access information that the security rules of the Java programming language make unavailable.
One feature provided by the EJB architecture is a security model. It's possible to declare programmatic or declarative security roles on methods that can be called by clients. These clients must have a required role to be granted permission to invoke the method. By using the reflection API, it's possible to bypass these security restrictions and invoke methods that you should not have access to. Any other use of the Reflection API is unrestricted.
The enterprise bean must not attempt to create a classloader; obtain the current classloader; set the context classloader; set security manager; create a new security manager; stop the JVM; or change the input, output, and error streams.
This basically states that you are not allowed to alter the runtime environment of the container. In enterprise applications, security is one of the most important issues, and the security managers and classloaders are at the root of protecting applications from unauthorized access of the runtime entities. Again, the EJB architecture has dealt with security by having the container be responsible for managing all access to the EJBs. By intercepting all method calls on the entities, the container can use the Java 2 platform security policy mechanism to prohibit certain functionality. To be able to perform this the container must have a stable execution environment that it creates by using a set of classloaders and security policies. If we were to make runtime changes to them, the container would lose control over the execution environment and security could not be guaranteed.
The enterprise bean must not attempt to manage threads. The enterprise bean must not attempt to start, stop, suspend, or resume a thread; or to change a thread's priority or name. The enterprise bean must not attempt to manage thread groups.
The reason for not being able to suspend, resume, start, stop, or create new threads again has to do with the container's ability to control the runtime environment. When dealing with a high-transaction environment, performance is an important issue. If there's no strict management of resources, like the use of threads, there's a good chance that the application will use too many resources and slow the system down to unacceptable levels. Or worse, the server is no longer able to serve client requests.
Thread-specific storage is another problem with using the threading API. For an EJB container to do its work properly, it needs to track information for the current request. In a system with thousands of simultaneous requests being processed, it's important to have an efficient and manageable way of getting at the required information.
Since the Java 2 platform, there's a class called java.lang.ThreadLocal for storing information associated with a specific thread. Most container vendors use a single thread dedicated to servicing a single request, and hereby they can use the ThreadLocal class to store the current user, transaction, security context, or any other required parameter from the request. If an EJB were to create new threads, the required information might not be propagated correctly to the new thread, potentially causing serious errors.
Not having to manage threads is a relief for most developers, but not being able to use custom threads has its drawbacks. For example, if you have a bean that needs to query different multiple external systems (ERP, legacy, etc.), it will have to be done in a series. Since these systems will take time to process your request, the time spent waiting can add up.
If you could use threads, you would be able call these requests in parallel, significantly reducing the overall time needed to process your request. There's talk of introducing a limited threading model into the EJB architecture, and it will most likely be in the form of an EJB retrieving a thread from a thread pool managed by the container. No threading mechanism has been mentioned in the EJB 2.0 specifications, so it could still be a long way off.
The enterprise bean must not attempt to pass this as an argument or method result. The enterprise bean must pass the result of SessionContext.getEJBObject(), SessionContext.getEJBLocalObject(), EntityContext.getEJBObject(), or Entity-
The life cycle of all bean instances is managed by the container. That's why we always access the bean through the EJBObject, never directly. The container can activate or passivate a bean at any time, and the client is never aware that this is taking place. While a client has a reference to the EJBObject, the container can passivate the actual bean instance. When the client invokes a method through the EJBObject, the container intercepts the call and performs tasks such as activating the bean and reassociating it with the EJBObject.
This programming restriction is questionable when you're dealing with helper classes. Since the helper classes execute only while the bean is active, theoretically, the use of the "this" reference should not cause any problems when used in this manner, but Sun Microsystems might want to clarify the issue.
Most programming restrictions are clear and concise, but a few restrictions are open to interpretation. This is probably why not all containers enforce these restrictions rigorously. The container from the J2EE reference implementation throws SecurityExceptions when the restrictions are violated. Many commercial EJB servers, such as BEA's WebLogic and IBM's WebSphere, don't seem to enforce a lot of these restrictions. These servers are still valid implementations because according to the EJB specifications, the container may allow more or fewer permissions to the enterprise bean instance, which can prevent portability across different containers.
When developing EJB components it's best to test them against the reference implementation to verify that they conform to the specifications. Unfortunately, most development projects are governed by strict deadlines and portability is not always at the top of the list. However, some developers forget that you should stick to the specification not just because of the portability issue, but because reliability can also be affected. Unfortunately, reliability problems always become apparent near the end of the development phase when there's little time left. If it turns out that some components need to be reengineered because they violate the programming restrictions, it will be a lot more time-consuming to fix them than to verify them against the reference implementation from the start.
One more reason to adhere to the programming restriction of the EJB specification is reuse. When you're in the business of building solutions for your clients, you'll undoubtedly deal with EJB servers from multiple vendors. The ability to reuse generic components that have been tested and proven to work across multiple EJB servers can save you significant amounts of time.
The EJB technology has put the building of scalable and robust enterprise applications within reach of a great number of Java programmers. Unfortunately, a number of EJB developers have never read the specifications and are unaware of all the programming restrictions. Hopefully, this article has cleared up some issues that Java developers face during their road to proficient EJB development. Just remember, EJBs are only portable if you write them that way.
Leander van Rooijen is a senior consultant for Cap Gemini Ernst & Young in the Netherlands. A Sun Certified Java developer, he's a specialist in server-side
technology. Leander holds a degree in mechanical engineering.