Skip Headers
Oracle® Containers for J2EE Developer's Guide
10g Release 3 (10.1.3)
Part No. B14433-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

8 J2EE Best Practices

This chapter provides recommended best practices to consider when developing J2EE applications for deployment into OC4J. It includes the following sections:

JavaServer Pages Best Practices

The following sections discuss best practices to consider when developing JSP pages for deployment into OC4J.

Beware of HTTP Sessions

HTTP sessions add performance overhead to your Web applications due to the amount of memory used. Sessions are enabled in JSP by default.

Avoid Using HTTP Sessions

Avoid using HTTP session objects if they are not required. If a JSP page does not require an HTTP session (essentially, does not require storage or retrieval of session attributes), then you can specify that no session is to be used. Specify this with a page directive such as the following:

<%@ page session="false" %>

This will improve the performance of the page by eliminating the overhead of session creation or retrieval.

Note that although servlets by default do not use a session, JSP pages by default do use a session.

Always Invalidate Sessions When No Longer in Use

If your JSPs do use HTTP sessions, ensure that you explicitly cancel each session, using the javax.servlet.http.HttpSession.invalidate() method to release the memory occupied.The default session timeout for OC4J is 30 minutes. You can change this for a specific application by setting the <session-timeout> parameter in the <session-config> element of the application's web.xml file.

Pretranslate JSP Pages Using the ojspc Utility

You might consider using the ojspc utility to pretranslate JSP pages before deployment. This avoids the performance cost of translating pages as they are first accessed by users. See the Oracle Containers for J2EE Support for JavaServer Pages Developer's Guide for additional discussion of the advantages of pretranslation.

Unbuffer JSP Pages

Unbuffer JSP pages. By default, a JSP page uses an area of memory known as a page buffer. This buffer (8 KB by default) is required if the page uses dynamic globalization support content type settings, forwards, or error pages. If it does not use any of these features, you can disable the buffer in a page directive:

<%@ page buffer="none" %>

This will improve the performance of the page by reducing memory usage and saving the output step of copying the buffer.

Forward to JSP Pages Instead of Using Redirects

You can pass control from one JSP page to another using one of two options: Including a <jsp:forward> standard action tag or passing the redirect URL to response.sendRedirect() in a scriptlet.

The <jsp:forward> option is faster and more efficient. When you use this standard action, the forwarded target page is invoked internally by the JSP runtime, which continues to process the request. The browser is totally unaware that the forward has taken place, and the entire process appears to be seamless to the user.

When you use sendRedirect(), the browser actually has to make a new request to the redirected page. The URL shown in the browser is changed to the URL of the redirected page. In addition, all request scope objects are unavailable to the redirected page because redirect involves a new request.

Use a redirect only if you want the URL to reflect the actual page that is being executed in case the user wants to reload the page.

Hide JSP Pages from Direct Invocation to Limit Access

There are situations, particularly in an architecture such as Model-View-Controller (MVC), where you would want to ensure that some JSP pages are accessible only to the application itself and cannot be invoked directly by users.

As an example, assume that the front-end or "view" page is index.jsp. The user starts the application through a URL request that goes directly to that page. Further assume that index.jsp includes a second page, included.jsp, and forwards to a third page, forwarded.jsp, and that you do not want users to be able to invoke these directly through a URL request.

A mechanism for this is to place included.jsp and forwarded.jsp in the application /WEB-INF directory. When located there, the pages cannot be directly invoked through URL request. Any attempt would result in an error report from the browser.

The page index.jsp would have the following statements:

<jsp:include page="WEB-INF/included.jsp"/>
...
<jsp:forward page="WEB-INF/forwarded.jsp"/>

The application structure would be as follows, including the standard classes directory for any servlets, JavaBeans, or other classes, and including the standard lib directory for any JAR files:

index.jsp
WEB-INF/
   web.xml
   included.jsp
   forwarded.jsp
   classes/
   lib/

Use JSP-Timeout for Efficient Memory Utilization

Set the jsp-timeout attribute of the <orion-web-app> element to an integer value, in seconds, after which any JSP page will be removed from memory if it has not been requested. This frees up resources in situations where some pages are called infrequently. The default value is 0, indicating no timeout. The <orion-web-app> element is found in the OC4J global-web-application.xml and orion-web.xml files. Modify the global-web-application.xml file to apply the timeout to all applications in an OC4J instance. To set configuration values to a specific application, set the file in the application-specific orion-web.xml file.

Package JSP Files In EAR File for Deployment

OC4J supports deployment of JSP pages by copying the files directly to the appropriate location. This is very useful when developing and testing the pages.

However, this practice is not recommended for releasing your JSP-based application for production. Always package JSP files in an Enterprise Archive (EAR) file to allow deployment in a standard manner and to allow deployment across multiple application servers.

Classloading Best Practices

See "Class Loading Best Practices" for J2EE classloading best practices.

Sessions Best Practices

The following sections discuss best practices to consider with regard to sessions.

Persist Session State if Appropriate

HTTP Sessions are used to preserve the conversation state with a Web browser. As such, they hold information, which if lost, could result in a client having to start the conversation over.

Hence, it is always safe to save the session state in database. However, this imposes a performance penalty. If this overhead is acceptable, then persisting sessions is indeed the best approach.There are trade-offs when implementing state safety that affect performance, scalability, and availability. If you do not implement state-safe applications, then:

  • A single JVM process failure will result in many user session failures. For example, work done shopping online, filling in a multiple page form, or editing a shared document will be lost, and the user will have to start over.

  • Not having to load and store session data from a database will reduce CPU overhead, thus increasing performance.

  • Having session data clogging the JVM heap when the user is inactive reduces the number of concurrent sessions a JVM can support, and thus decreases scalability.

In contrast, a state safe application can be written so that session state exists in the JVM heap for active requests only, which is typically 100 times fewer than the number of active sessions.

To improve performance of state safe applications:

  • Minimize session state. For example, a security role might map to detailed permissions on thousands of objects. Rather than store all security permissions as session state, just store the role id. Maintain a cache, shared across many sessions, mapping role id to individual permissions.

  • Identify key session variables that change often, and store these attributes in a cookie to avoid database updates on most requests.

  • Identify key session variables that are read often, and use HttpSession as a cache for that session data in order to avoid having to read it from the database on every request. You must manually synchronize the cache, which requires care to handle planned and unplanned transaction rollback.

Do Not Store Shared Resources in Sessions

Objects that are stored in the session objects will not be released until the session times out (or is invalidated). If you hold any shared resources that have to be explicitly released to the pool before they can be reused (such as a JDBC connection), then these resources may never be returned to the pool properly and can never be reused.

Set Session Timeout Appropriately

Set session timeout appropriately (setMaxInactiveInterval()) so that neither sessions timeout frequently nor does it live for ever this consuming memory.

Monitor Session Memory Usage

Monitor the memory usage for the data you want to store in session objects. Make sure there is sufficient memory for the number of sessions created before the sessions time out.

Use a Mix of Cookie and Sessions

Typically, a cookie is set on the Web browser (automatically by the container), to track a user session. In some cases, this cookie may last a much longer duration than a single user session. (For example, one time settings, such as to determine the end-user geographic location).Thus, a cookie that persists on the client disk could be used to save information valid for the long-term, while a server side session will typically include information valid for the short-term.In this situation, the long-term cookie should be parsed on only the first request to the server, when a new session established. The session object created on the server should contain all the relevant information, so as not to require re-parsing the cookie on each request.A new client side cookie should then be set that contains only an ID to identify the server side session object. This is automatically done for any JSP page that uses sessions.This gives performance benefit since the session object contents do not have to be re-created from the long-term cookie. The other option is to save the user settings in a database on the server, and have the user login. The unique userid can then be used to retrieve the contents from the database and store the information in a session.

Use Coarse Objects Inside HTTP Sessions

Oracle Application Server automatically replicates sessions when session object is updated. If a session object contains granular objects, (for example, a person's name), it results in too many update events to all the servers in the island. Hence, it is recommended to use coarse objects, (for example the person object, as opposed to the name attribute), inside the session.

Use Transient Data in Sessions Whenever Appropriate

Oracle Application Server does not replicate transient data in a session across servers in the island. This reduces the replication overhead (and also the memory requirements). Hence, use the transient type liberally.

Invalidate Sessions

The number of active users is generally quite small compared to the number of users logged in on the system. For example, of the 100 users on a Web site, only 10 may actually be doing something at any given time.

A session is typically established for each user on the system. Each session, of course, uses memory.

Simple things, like a logout button, provide an opportunity for quick session invalidation and removal. This avoids memory usage growth since the sessions on the system will be closer to the number of active users, as opposed to all those that have not timed out yet.

Miscellaneous Guidelines

  • Use sessions as light-weight mechanism by verifying session creation state.

  • Use cookies for long-standing sessions.

  • Put recoverable data into sessions so that they can be recovered if the session is lost. Store non-recoverable data persistently (in file system or in database using JDBC). However, storing every data persistently is an expensive thing. Instead, one can save data in sessions and use HttpSessionBindingListener or other events to flush data into persistent storage during session close.

  • Sticky versus Distributable Sessions

    • Distributable session data must be serialized and useful for failover. However it is expensive, as the data has to be serialized and replicated among peer processes.

    • Sticky sessions affect load-balancing across multiple JVMs, but are less expensive as there is no state replication.

Enterprise Java Bean Best Practices

This section describes Enterprise Java Beans (EJB) best practices. It includes the following topics:

Using Local, Remote, and Message Driven EJBs When Appropriate

EJBs can be local or remote. If you envision calls to an EJB to originate from the same container as the one running the EJB, local EJBs are better since they do not entail the marshalling, unmarshalling, and network communication overhead. The local beans also allow you to pass an object-by-reference, thus, improving performance further.

Remote EJBs allow clients to be on different machines and/or different application server instances to talk to them. In this case, it is important to use the value object pattern to improve performance by reducing network traffic.

If you choose to write an EJB, write a local EJB over a remote EJB object. Since the only difference is in the exception on the EJB object, almost all of the implementation bean code remains unchanged.

Additionally, if you do not have a need for making synchronous calls, message driven beans are more appropriate.

Use EJB Judiciously

An EJB is a reusable component backed by component architecture with several useful services: persistence, transactions security, naming, etc. However, these additions make it "heavy."

If you just require abstraction of some functionality and are not leveraging the EJB container services, you should consider using a simple JavaBean, or implement the required functionality using JSPs or servlets.

Use Service Locator Pattern

Most J2EE services and/or resources require "acquiring" a handle to them via an initial Java Naming and Directory Interface (JNDI) call. These resources could be an EJB Home object, or, a JMS topic. This results in expensive calls to the server machine to resolve the JNDI reference, even though the same client may have gone to the JNDI service for a different thread of execution to fetch the same data.

Hence, it is recommended to have a Service Locator, which in some sense is a local proxy for the JNDI service, so that the client programs talk to the local service locator, which in turn talks to the real JNDI service, and that only if required.

The Java Object Cache bundled with the product may be used to implement this pattern.

This practice improves availability since the service locator can hide failures of the backend server or JNDI tree by having cached the lookup. Although this is only temporary since the results still have to be fetched.

Performance is also improved since trips to the back-end application server are reduced.

Cluster Your EJBs

Cluster your EJBs only when you require:

  • Load Balancing: The EJB client(s) are load balanced across the servers in the EJB cluster.

  • Fault Tolerance: The state (in case of stateful session beans) is replicated across the OC4J processes in the EJB cluster. If the proxy classes on the client cannot connect to an EJB server, they will attempt to connect to the next server in the cluster. The client does not see the failure.

  • Scalability: Since multiple EJB servers behaving as one can service many more requests than a single EJB server, a clustered EJB system is more scalable. The alternative is to have stand-alone EJB systems, with manual partitioning of clients across servers. This is difficult to configure and does not have fault tolerance advantages.

In order to fully leverage EJB clustering you will need to use remote EJBs. Remote EJBs have some performance implications over local EJBs. If you use local EJBs and save a reference to them in a servlet (or JSP) session, when the session is replicated the saved reference becomes invalid. Therefore, use EJB clustering only when you need the listed features.

Index Secondary Finder Methods

When finder methods, other than findByPrimaryKey() and findAll(), are created they may be extremely inefficient if appropriate indexes are not created that help to optimize execution of the SQL code generated by the container.

Understand EJB Lifecycle

As a developer, it is imperative that you understand the EJB lifecycle. Many problems can be avoided by following the lifecycle and the expected actions during call backs more closely.

This is especially true with entity beans and stateful session beans. For example, in a small test environment, a bean may never get passivated, and thus a mis-implementation (or non-implementation) of ejbPassivate() and ejbActivate() may not show up until later. Moreover, since these are not used for stateless beans, they may confuse new developers.

Use Deferred Database Constraints

For those constraints that may be invalid for a short time during a transaction but will be valid at transaction boundaries, use deferred database constraints. For example, if a column is not populated during an ejbCreate(), but will be set prior to the completion of the transaction, then you may want to set the not null constraint for that column to be deferred. This also applies to foreign key constraints that are mirrored by EJB relationships with EJB 2.0.

Create a Cache with Read Only EJBs

For those cases where data changes very slowly or not at all, and the changes are not made by your EJB application, read-only beans may make a very good cache. A good example of this is a country EJB. It is unlikely that it will change very often and it is likely that some degree of stale data is acceptable.

To do this:

  1. Create read-only entity beans.

  2. Set exclusive-write-access="true".

  3. Set the validity timeout to the maximum acceptable staleness of the data.

Pick an Appropriate Locking Strategy

It is critical that an appropriate locking strategy be combined with an appropriate database isolation mode for properly performing and highly reliable EJB applications.

Use optimistic locking where the likelihood of conflict in updates is low. If a lost update is acceptable or cannot occur because of application design, use an isolation mode of read-committed. If the lost updates are problematic, use an isolation mode of serializable.

Use pessimistic locking where there is a higher probability of update conflicts. Use an isolation mode of read-committed for maximum performance in this case. Use read-only locking when the data will not be modified by the EJB application.

Understand and Leverage Patterns

With the wider industry adoption, there are several common (and generally) acceptable ways of solving problems with EJBs. These have been widely published in either books or discussion forums, etc. In some sense, these patterns are best practices for a particular problem. These should be researched and followed.

Here are some examples:

  • Session Façade: Combines multiple entity bean calls into a single call on a session bean, thus reducing the network traffic.

  • Message Façade: Use MDBs if you do not need a return status from your method invocation.

  • Value Object Pattern: A value object pattern reduces the network traffic by combining multiple data values that are usually required to be together, into a single value object.

A full discussion on the large number of patterns available is outside the scope of this document, but the references section contains some useful books and/or Web sites on this subject.

When Using Entity Beans, Use Container Managed Aged Persistence Whenever Possible

Although there are some limitations to container-managed persistence (CMP), CMP has a number of benefits. One benefit is portability. With CMP, decisions like persistence mapping and locking model selection become a deployment activity rather than a coding activity. This allows deployment of the same application in multiple containers with no change in code. This is commonly not true for Bean Managed Persistence (BMP) since SQL statements and concurrency control must be written into the entity bean and are therefore specific to the container and/or the data store.

Another benefit is that, in general, J2EE container vendors provide quality of service (QoS) features such as locking model variations, lazy loading, and performance and scalability enhancements, which may be controlled via deployment configuration rather than by writing code. Oracle Application Server includes features such as read-only entity beans, minimal writing of changes, and lazy loading of relations, which would have to be built into code for BMP.

A third benefit of CMP is container-managed relationships. Through declarations, not unlike CMP field mapping, a CMP entity bean can have relationships between two entity beans managed by the container with no implementation code required from application developers.

Last but least, tools are available to aid in the creation of CMP entity beans so that minimal work is required from developers for persistence. This allows developers to focus on business logic, which allows them to be more efficient. JDeveloper9i is a perfect example where, through modeling tools and wizards, very little work is required to create CMP entity beans including creation of both the generic EJB descriptors and the Oracle Application Server specific descriptors.

Overall, there are cases where CMP does not meet the requirements of an application, but the development effort saved, and the optimizations that J2EE containers like OC4J provide make CMP much more attractive than BMP.

Entity Beans using Local interfaces Only

It is a good practice to expose your entity beans using only local interfaces because container managed relationship can only be used with local interfaces. Also local interfaces avoid expensive serialization and remote network calls.

Use a Session Bean Facade for Entity Beans

Avoid using entity beans directly from Web modules and client applications and use a session bean façade layer instead. Use of entity beans from client applications hardcodes the domain model in the client. It also introduces difficulty when managing both remote and local interfaces for entity beans.

Create a session bean façade layer by grouping together all natural use cases. This exposes operations to one or more entity beans. It provides finer grained access to the entity beans and reduces database interactions by acting as a transaction boundary. This also enables the entity beans to be accessed by Web services by exposing the stateless session bean as a Web service endpoint.

Enforce Primary Key Constraints at the Database Level

Enforce primary key constraint for the underlying table for your CMP entity beans instead of having the container execute an extra SQL statement to check for a duplicate primary key. You can switch this check by setting the do-select-before-insert=ÓfalseÓ for your entity bean in the orion-ejb-jar.xml file.

Use Foreign Key for 1-1 and 1-M Relationships

Use a foreign key when completing the O-R mapping for 1-1 and 1-Many relationships between entity beans instead of using an association table. This allows you to avoid maintaining an extra table and an extra SQL statement generated by container to maintain the relationships.

Avoid findAll() Method on Entities Based on Large Tables

When you use the findAll() method, the container tries to retrieve all rows of the table. You should try to avoid this type of operation on entities based on tables that have a large number of records. It will slow down the operations of your database.

Set prefetch-size to Reduce Round Trips to Database

Oracle JDBC drivers have extensions that allow setting the number of rows to prefetch into the client while a result set is being populated during a query. This reduces the number of round trips to the server. This can drastically improve performance of finder methods that return a large number of rows. You can specify the prefetch-size attribute for your finder method in the orion-ejb-jar.xml file.

Use lazy-loading with Caution

If you turn on lazy loading, which is off by default, then only the primary keys of the objects retrieved within the finder are returned. Thus, when you access the object within your implementation, the OC4J container uploads the actual object based on the primary key.

You may want to turn on the lazy loading feature if the number of objects that you are retrieving is so large that loading them all into your local cache would decrease performance. If you retrieve multiple objects, but you only use a few of them, then you should turn on lazy loading. In addition, if you only use objects through the getPrimaryKey method, then you should also turn on lazy loading.

Avoid Performing O-R Mapping Manually

O-R mapping for CMP entity beans in the orion-ejb-jar.xml file is very complex and error prone. Any error in the mapping can cause deployment errors and generation of wrong SQL code for EJB-SQL statements. The following two approaches are recommended:

  • Use JDeveloper 9.0.5.1 to perform the O-R mapping and to generate the mapping information in the orion-ejb-jar.xml file.

  • Deploy the application in OC4J to generate the mappings and then modify the orion-ejb-jar.xml file to include the correct table-name and persistence-names.