Skip Headers
Oracle® Application Server Best Practices Guide
10g Release 2 (10.1.2)
B28654-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
 

4 Oracle Application Server Containers for J2EE (OC4J) Applications and Developer Tools

This chapter describes the best practices for J2EE applications. It includes the following topics:

4.1 Java Server Pages Best Practices

This section describes Java Server Pages (JSP) best practices. It includes the following topics:

4.1.1 Pre-Translate JSPs Before Deployment to Prevent Translation Overhead

You can use the Oracle ojspc tool to pre-translate the JSPs and avoid the translation overhead that has to be incurred when the JSPs are executed the first time. You can pre-translate the JSPs on the production system or before you deploy them. Also, pre-translating the JSPs allows you the option to deploy only the translated and compiled class files, if you choose not to expose and compromise the JSP source files.

4.1.2 Separate Presentation Markup from Java to Improve Application Performance

Separating presentation markup such as HTML from Java code is a good practice to get better performance from your application. The following are a few tips:

  • Use JavaBeans for the business logic and JSPs only for the view. Thus, JSPs should primarily contain logic for HTML (or other presentation markup) generation only.

  • Use stylesheets when appropriate to provide even more separation of the aspects of HTML that a user can control better.

  • JSPs containing a large amount of static content, including large amounts of HTML code that does not change at runtime, which may result in slow translation and execution. Use dynamic includes, or better, enable the external resource configuration parameter to put the static HTML into a Java resource file.

4.1.3 Use JSP Template Mechanism to Reserve Resources

Using the JSP code out.print("<html>") requires more resources than including static template text. For performance reasons, it is best to reserve the use of out.print() for dynamic text.

4.1.4 Set sessions to false If Not Using Sessions to Prevent Overhead of Creating Sessions

The default for JSPs is session="true". If your JSPs do not use any sessions, you should set session="false" to eliminate the overhead of creating and releasing these internal sessions created by the JSP runtime.

Implementation Details

To disable sessions, set the directive as follows:

<%@page session="false" %>

4.1.5 Always Invalidate Sessions When No Longer Used to Prevent Overhead of Applications

Sessions add performance overhead to your Web applications. Each session is an instance of the javax.servlet.http.HttpSession class. The amount of memory used for each session depends on the size of the session objects created.

If you use sessions, ensure that you explicitly cancel each session using the invalidate() method to release the memory occupied by each session when you no longer need it.

The default session timeout for OC4J is 30 minutes.

Implementation Details

To change this timeout for a specific application, set the <session-timeout> parameter in the <session-config> element of the web.xml file.

4.1.6 Set main_mode Parameter to justrun to Prevent Recompilation of JSPs

The Oracle JSP configuration parameter main_mode determines whether classes are automatically reloaded or JSPs are automatically recompiled. In a deployment environment set main_mode to a value of justrun. The runtime dispatcher does not perform any timestamp checking, so there is no recompilation of JSPs or reloading of Java classes. This mode is the most efficient mode for a deployment environment where code is not expected to change.

Implementation Details

If comparing timestamps is unnecessary, as is the case in a production deployment environment where source code does not change, you can avoid all timestamp comparisons and any possible re-translations and reloads by setting the main_mode parameter to the value justrun. Using this value can improve the performance of JSP applications.

Note that before you set main_mode to justrun, make sure that the JSP is compiled at least once. You can compile the JSP by invoking it through a Web browser or by running your application (using the recompile value for main_mode). This compilation assures that the JSP is compiled before you set the justrun flag.


See Also:

Chapter 3, "Getting Started," in the Oracle Application Server Containers for J2EE Support for JavaServer Pages Developer's Guide for further information about the main_mode configuration parameter

4.1.7 Use Available JSP Tags In Tag Library to Create Clean and Reusable Code

JSP tags make the JSP code cleaner, and more importantly, provide easy reuse. In some cases, there is also a performance benefit. Oracle Application Server ships with a very comprehensive JSP tag library that will meet most needs. In cases where custom logic is required or if the provided library is insufficient, you can build a custom tag library, if appropriate.

4.1.8 Minimize Context Switching Between Servlets and EJBs to Avoid Performance Issues

Minimize context switching between different Enterprise JavaBeans (EJB) and servlet components, especially when the EJB and Web container processes are different. If context switching is required, co-locate EJBs whenever possible.

4.1.9 Package JSP Files In EAR File Rather Than Standalone to Standardize Deployment Process

Oracle Application Server supports deploying of JSP files by copying them to the appropriate location. This functionality is very useful when developing and testing the pages. This functionality is not recommended for releasing your JSP-based application for production. You should always package JSP files into an Enterprise Archive (EAR) file. With an EAR file, you deploy JSPs in a standard manner, even across multiple application servers.

4.1.10 Use Compile-Time Object Introspection to Improve Application Performance

Developers should try to rely on compile-time object introspection on the beans and objects generated by the tag library instead of request-time introspection.

4.1.11 Choose Static Versus Dynamic Includes Appropriately

JSP pages have two different include mechanisms:

  • Static includes, which have a page directive, such as:

     <%@ include file="filename.jsp" %>
    
    
  • Dynamic includes, which have a page directive, such as:

    <jsp:include page="filename.jsp" flush="true" />
    
    

Static includes create a copy of the include file in the JSP. Therefore, it increases the page size of the JSP, but it avoids additional trips to the request dispatcher. Dynamic includes are analogous to function calls. Therefore, they do not increase the page size of the calling JSP, but they do increase the processing overhead because each call must go through the request dispatcher.

Dynamic includes are useful if you cannot determine which page to include until after the main page has been requested. A page that is dynamically included must be an independent entity, which can be translated and executed on its own.

4.1.12 Disable JSP Page Buffer If Not Used to Improve Performance

In order to allow part of the response body to be produced before the response headers are set, JSPs can store the body in a buffer.

When the buffer is full or at the end of the page, the JSP runtime will send all headers that have been set, followed by any buffered body content. This buffer is also required if the page uses dynamic content type settings, forwards, or error pages. The default size of a JSP page buffer is 8 KB.

Implementation Details

If you need to increase the buffer size, for example to 20 KB, you can use the following JSP attribute and directive:

<%@page buffer="20kb" %>

If you are not using any JSP features that require buffering, you can disable it to improve performance; memory will not be used in creating the buffer, and output can go directly to the Web browser. You can use the following directive to disable buffering:

<%@ page buffer="none" %>

4.1.13 Use Forwards Instead of Redirects to Improve Browser Experience

For JSPs, you can pass control from one page to another by using forward or redirect, but forward is always faster. When you use forward, the forwarded target page is invoked internally by the JSP runtime, which continues to process the request. The Web browser is totally unaware that such an action has taken place.

When you use redirect, the Web browser actually has to make a new request to the redirected page. The URL shown in the Web browser is changed to the URL of the redirected page, but it stays the same in a forward operation.

Therefore, redirect is always slower than the forward operation. In addition, all request-scope objects are unavailable to the redirected page because redirect involves a new request. Use 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.

4.1.14 Use JSP Cache Tags to Save Development Time

Using the Java Object Cache in JSP pages, as opposed to servlets, is particularly convenient because JSP code generation can save much of the development effort. Oracle JSP provides the following tags for using the Java Object Cache:

  • ojsp:cache

  • ojsp:cacheXMLObj

  • ojsp:useCacheObj

  • ojsp:invalidateCache

Implementation Details

Set the ojsp:cacheXMLObj or ojsp:cache tag to enable caching and specify cache settings. Use ojsp:useCacheObj to cache any Java serializable object. Use the ojsp:invalidateCache tag to invalidate a cache block. Alternatively, you can arrange invalidation through the invalidateCache attribute of the ojsp:cacheXMLObj or ojsp:cache tag.

4.1.15 Use well_known_taglib_loc to Share Tag Libraries

As an extension of standard JSP "well-known URI" functionality described in the JSP 1.2 specification, the OC4J JSP container supports the use of a shared tag library directory where you can place tag library JAR files to be shared across multiple Web applications. The benefits are:

  • Avoidance of duplication of tag libraries between applications

  • Easy maintenance, as the TLDs can be in a single JAR file

  • Minimized application size

Implementation Details

The Oracle JSP configuration parameter well_known_taglib_loc configuration parameter specifies the location of the shared tag library directory. The default location is the ORACLE_HOME/j2ee/home/jsp/lib/taglib/ directory..

You must add the shared directory to the server-wide CLASSPATH by specifying it as a library path element. The default location is set in the application.xml file.


See Also:


4.1.16 Use jsp-timeout Attribute to Provide Efficient Memory Utilization

Resource utilization is a key factor for any efficient application. Oracle Application Server provides the <orion-web-app> attribute jsp-timeout. The jsp-timeout attribute specifies an integer value, in seconds, after which any JSP page will be removed from memory if it has not been requested. This attribute frees up resources in situations where some pages are called infrequently. The default value is 0 for no timeout.

Implementation Details

4.1.17 Use reduce_tag_code Parameter to Reduce the Size of Generated Java Method

The JVM limits the amount of code to 65536 bytes for each Java method. Sometimes, as the JSPs grow larger, there is a possibility of hitting this limit. To reduce the size of generated code for custom tag usage:

  • Use the reduce_tag_code configuration parameter.

  • Design smaller JSPs for your Web application.

Implementation Details

Set the reduce_tag_code configuration parameter in the:

  • <orion-web-app> element in the OC4J global-web-application.xml file to apply to all applications in an OC4J instance

  • <orion-web-app> element in the deployment-specific orion-web.xml file to apply to a specific application


See Also:

Chapter 3, "Getting Started," in the Oracle Application Server Containers for J2EE Support for JavaServer Pages Developer's Guide for further information about the reduce_tag configuration parameter

4.1.18 Use Workarounds to Avoid Reaching JVM Code Size Limit

The JVM limits the amount of code to 65536 bytes for each Java method. Sometimes, as JSPs grow larger, there is a possibility of reaching this limit. The following are some suggestions to workaround this limitation:

  • Design smaller JSPs for your Web application.

  • If your JSP primarily uses tag libraries, and if you are reaching the 64 KB limit, use the reduce_tag_code configuration parameter to reduce the size of generated code for custom tag usage. Note that this configuration setting may impact performance.

  • The version of the JDK you are using can impact the size of the code generated. Generally, JDK 1.4.2_04-b05 generates far less code compared to JDK 1.4.1_03-b02.

4.1.19 Hide JSP Pages to Prevent Access

There are situations when you do not want to allow access to specific JSPs from a Web browser. For example, when a JSP is not presented in the Web browser but is part of application logic, which gets accessed by other JSPs or servlets.

Implementation Details

Put the JSPs you want to hide into a /WEB-INF directory. Provide access within your application code with the following:

4.2 Sessions Best Practices

This section describes best practices for sessions. It includes the following topics:

4.2.1 Persist Session State If Appropriate to Preserve State with Browser

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.

Therefore, it is always safe to save the session state in database. This functionality imposes a performance penalty. If this overhead is acceptable, then persisting sessions is 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, you can write a state-safe application so that the session state exists in the JVM heap for active requests only, which is typically 100 times fewer than the number of active sessions.

Implementation Details

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.

4.2.2 Replicate Sessions If Persisting Is Not an Option to Improve Performance

For the category of applications where the HTTP session state information cannot be persisted and retrieved on each HTTP request (due to the performance overhead), OC4J provides an intermediate option, replication.It can replicate the session state information across an island of servers that are in the same cluster. This functionality provides a performance improvement, because the sessions remain in memory and provide fault tolerance. Fault tolerance occurs because Oracle HTTP Server automatically routes the HTTP requests to a different server in the island, if the original OC4J and the session it contains is down.Set up at least two servers in an island, so that they can back session state for each other.

4.2.3 Avoid Storing Objects in Sessions to Reuse Shared Resources

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.

4.2.4 Set Session Timeout Appropriately to Optimize Performance

Set session timeout appropriately (setMaxInactiveInterval()) so that sessions do not frequently time out or live forever and consume memory.

Implementation Details

4.2.5 Monitor Session Memory Usage to Determine Data to Store in Session Objects

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.

4.2.6 Use Small Islands to Improve Fault Tolerance

Setting up an island of OC4J JVMs causes the sessions to be replicated across all JVMs. This functionality provides better fault tolerance, since a server failure does not necessarily result in a lost session. Oracle Application Server automatically re-routes request to another server in the island. As a result, an end-user never finds out about a failure. This replication overhead increases as more servers are added to the island. For example, if your session object requires 100 KB for each user, and there are 100 users for each server, there is a 10 MB memory requirement for session replication for each server. If you have five servers in an island, the memory requirement increases five-fold. Since islands provide session replication, it is, in general, not prudent to exceed an island size beyond three.Setting up multiple islands, with few servers in an island is a better choice compared to having a fewer number of larger-sized islands.

4.2.7 Use a Mix of Cookie and Sessions to Improve Performance

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, parse the long-term cookie 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 clientside cookie should then be set that contains only an ID to identify the serverside session object. This functionality automatically occurs for any JSP page that uses sessions.Because the session object contents do not have to be re-created from the long-term cookie, there is a performance benefit. The other option is to save the user settings in a database on the server, and have the user login. The unique user ID can then be used to retrieve the contents from the database and store the information in a session.

4.2.8 Use Coarse Objects Inside HTTP Sessions to Reduce Update Events

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

4.2.9 Use Transient Data in Sessions Whenever Appropriate to Reduce Replication Overhead

Oracle Application Server does not replicate transient data in a session across servers in the island. This behavior reduces the replication overhead, as well as the memory requirements. Therefore, use the transient-type liberally.

4.2.10 Invalidate Sessions to Prevent Memory Usage Growth

The number of active users is generally quite small compared to the number of users on the system. For example, of the 100 users on a Web site, only 10 may actually be doing something.A session is typically established for each user on the system, which uses memory.Simple things, like a logout button, provide an opportunity for quick session invalidation and removal. Session invalidation and removal avoids memory usage growth, because 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.

4.2.11 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 the file system or in database using JDBC. Storing data persistently is an expensive operation. Instead, you 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 serializable and useful for failover. This data is expensive, as the data has to be serializable and replicated among peer processes.

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

4.3 Enterprise Java Bean Best Practices

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

4.3.1 Use Local, Remote, and Message-Driven EJBs Appropriately to Improve Performance

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 computers and have 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.

4.3.2 Use EJB Judiciously

An EJB is a reusable component backed by component architecture with several useful services, such as persistence, transactions security, and naming. 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 Java Bean, or implement the required functionality using JSPs or servlets.

4.3.3 Use Service Locator Pattern

Most J2EE services or resources require acquiring a handle to them through an initial Java Naming and Directory Interface (JNDI) call. These resources could be an EJB home object or a JMS topic. These calls are expensive to the server computer 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.

To avoid this issues, use a service locator, which in some sense is a local proxy for the JNDI service, so that the client programs communicates with the local service locator, which in turn communicates to the real JNDI service, 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.

4.3.4 Cluster Your EJBs

Cluster your EJBs only when you require:

  • Load Balancing: The EJB clients 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 standalone EJB systems, with manual partitioning of clients across servers. This configuration can be difficult and does not provide 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 (Section 4.3.1, "Use Local, Remote, and Message-Driven EJBs Appropriately to Improve Performance"). 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.

4.3.5 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 generated by the container.

4.3.6 Understand EJB Lifecycle

As a developer, it is imperative that you understand the EJB lifecycle. You can avoid many problems by following the lifecycle and the expected actions during callbacks more closely.

This is especially true with entity beans and stateful session beans. For example, in a small test environment, a bean may never be made passive. Therefore, a faulty 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.

4.3.7 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 recommendation also applies to foreign-key constraints that are mirrored by EJB relationships with EJB 2.0.

4.3.8 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.

Implementation Details

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.

4.3.9 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.

4.3.10 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, and so on. In some sense, these patterns are best practices for a particular problem. Research and follow these patterns.

Here are some examples:

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

  • Message facade: 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 or Web sites on this subject.

4.3.11 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. CMP 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 you must write SQL statements and concurrency control into the entity bean and are therefore specific to the container 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 through 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.

Finally, tools are available to aid in the creation of CMP entity beans, so that minimal work is required from developers for persistence. These tools enable developers to focus on business logic. Oracle JDeveloper 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.

4.3.12 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.

4.3.13 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 hard codes the domain model in the client. It also introduces difficulty when managing both remote and local interfaces for entity beans.

Implementation Details

Create a session bean facade layer by grouping together all natural use cases. This layer 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.

4.3.14 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.

Implementation Details

You can switch this check by setting the do-select-before-insert=ÓfalseÓ for your entity bean in the orion-ejb-jar.xml file.

4.3.15 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 enables you to avoid maintaining an extra table and an extra SQL statement generated by container to maintain the relationships.

4.3.16 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 slowdown the operations of your database.

4.3.17 Set prefetch-size Attribute to Reduce Round Trips to Database

Oracle JDBC drivers have extensions that allows 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.

Implementation Details

Specify the prefetch-size attribute for your finder method in the orion-ejb-jar.xml file.

4.3.18 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.

4.3.19 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 for EJB-QL 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.

4.4 Data Access Best Practices

This section describes data access best practices. It includes the following topics:

4.4.1 Use Datasources Connections Caching and Handling to Prevent Running Out of Connections

Connections must not be closed within finalize() methods. This can cause the connection cache to run out of connections to use, since the connection is not closed until the object that obtained it is garbage collected.

The current connection cache does not provide any mechanism to detect "abandoned" connections, reclaim them, and return them to the cache. The application can explicitly close all connections.

If a connection is declared as static, then it is possible that the same connection object is used on different threads at the same time. Do not declare connections as static objects.

Use the FIXED_WAIT_SCHEME when using the connection cache, especially when writing Web applications. This guarantees enforcement of the MaxLimit on the connection cache as well as retrieval of a connection from the cache when a connection is returned to the cache.

Always use connection cache timeouts such as CacheInactivityTimeout to close unused physical connections in the cache and cause "shrinking" of the cache, thus releasing valuable resources.

4.4.1.1 DataSource Connection Caching Strategies

In order to minimize the lock up of resources for long periods of time but allow for recycling of connections from the connection cache, you should use the most appropriate strategy for obtaining and releasing connections as follows:

  • Many clients, few connections: Open and close a connection in the same method that needs to use the connection. In order to ensure that connections are returned to the pool, all calls to this method should happen within try-catch, try-finally, or try-catch-finally blocks. This strategy is useful when you have a large number of clients sharing a few connections at the cost of the overhead associated with getting and closing each connection.

  • Private client pool: Take advantage of the BMP life cycle. Get a connection within setEntityContext() and release the connection in unsetEntityContext(). Make connections available to all methods by declaring it a member instance.

  • Combined strategy: You may take further advantage of BMP life cycle and implement a strategy, which combines the preceding two strategies.

4.4.2 Use Data Source Initialization

It is a good practice to put the JNDI lookup of a DataSource as part of the application initialization code, since DataSources are simply connection factories.

For example, when using servlets, it is a good idea to put the DataSource lookup code into the init() method of the servlet.

4.4.3 Disable Escape Processing to Improve Performance

Escape processing for SQL92 syntax is enabled by default, which results in the JDBC driver performing escape substitution before sending the SQL code to the database.

Implementation Details

If you want the driver to use regular Oracle SQL syntax, which is more efficient than SQL92 syntax and escape processing, then disable escape processing using the following statement:

stmt.setEscapeProcessing(false);

4.4.4 Define Column Types to Save Round-trips to Database Server

The Oracle-specific defining column types feature provides the following benefits:

  • Saves a round-trip to the database server.

  • Defines the datatype for every column of the expected result set.

  • For VARCHAR, VARCHAR2, CHAR, and CHAR2, specifies their maximum length.

The following example illustrates the use of this feature. It assumes you have imported the oracle.jdbc.* and java.sql.* interfaces and classes.

//ds is a DataSource object
Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareStatement("select empno, ename, hiredate from emp");
 
//Avoid a round-trip to the database and describe the columns
((OraclePreparedStatement)pstmt).defineColumnType(1,Types.INTEGER);
 
//Column #2 is a VARCHAR, we need to specify its max length
((OraclePreparedStatement)pstmt).defineColumnType(2,Types.VARCHAR,12);
((OraclePreparedStatement)pstmt).defineColumnType(3,Types.DATE);
ResultSet rset = pstmt.executeQuery();
while (rset.next())
System.out.println(rset.getInt(1)+","+rset.getString(2)+","+rset.getDate(3));
pstmt.close();…
 

4.4.5 Prefetch Rows to Improve Performance

Row prefetching improves performance by reducing the number of round trips to a database server. For most database-centric applications, Oracle recommends the use of row prefetching as much as possible. The default prefetch size is 10.

The following example illustrates the use of row prefetching. It assumes you have imported the oracle.jdbc.* and java.sql.* interfaces and classes

//ds is a DataSource object Connection conn = ds.getConnection();

//Set the default row-prefetch setting for this connection
((OracleConnection)conn).setDefaultRowPrefetch(7);

//The following statement gets the default row-prefetch value for
//the connection, that is, 7 Statement stmt = conn.createStatement();
//Subsequent statements look the same, regardless of the row

//prefetch value. Only execution time changes.
ResultSet rset = stmt.executeQuery("SELECT ename FROM emp");
System.out.println( rset.next () );
while( rset.next () )
System.out.println( rset.getString (1) );

//Override the default row-prefetch setting for this

//statement
( (OracleStatement)stmt ).setRowPrefetch (2);
ResultSet rset = stmt.executeQuery("SELECT ename FROM emp");
System.out.println( rset.next () );
while( rset.next() )
System.out.println( rset.getString (1) );
stmt.close();
…
.
.

Implementation Details


See Also:

Oracle Database JDBC Developer's Guide and Reference from the Oracle Database documentation library

4.4.6 Update Batching to Improve Performance

Update batching sends a batch of operations to the database in one trip. When using it, always disable auto-commit mode with update batching. Use a batch size of around 10. Do not mix the standard and Oracle models of update batching.


See Also:

Oracle Database JDBC Developer's Guide and Reference from the Oracle Database documentation library

4.4.6.1 Oracle Update Batching

The following example illustrates how you use the Oracle update batching feature. It assumes you have imported the oracle.jdbc.driver.* interfaces.

//ds is a DataSource object
Connection conn = ds.getConnection();
//Always disable auto-commit when using update batching
conn.setAutoCommit(false);
PreparedStatement ps =
conn.prepareStatement("insert into dept values (?, ?, ?)");
//Change batch size for this statement to 3
((OraclePreparedStatement)ps).setExecuteBatch (3);
//--------#1------------
ps.setInt(1, 23);
ps.setString(2, "Sales");
ps.setString(3, "USA");
ps.executeUpdate(); //JDBC queues this for later execution
//--------#2------------
ps.setInt(1, 24);
ps.setString(2, "Blue Sky");
ps.setString(3, "Montana");
ps.executeUpdate(); //JDBC queues this for later execution
//--------#3------------
ps.setInt(1, 25);
ps.setString(2, "Applications");
ps.setString(3, "India");
ps.executeUpdate(); //The queue size equals the batch value of3
 
//JDBC sends the requests to the database
//--------#1------------
ps.setInt(1, 26);
ps.setString(2, "HR");
ps.setString(3, "Mongolia");
ps.executeUpdate(); //JDBC queues this for later execution
((OraclePreparedStatement)ps).sendBatch(); // JDBC sends the
//queued request
conn.commit();
ps.close();
...

4.4.6.2 Standard Update Batching

This example uses the standard update batching feature. It assumes you have imported the oracle.jdbc.driver.* interfaces.

//ds is a DataSource object
Connection conn = ds.getConnection();
//Always disable auto-commit when using update batching
conn.setAutoCommit(false);
Statement s = conn.createStatement();
s.addBatch("insert into dept values ('23','Sales','USA')");
s.addBatch("insert into dept values ('24','Blue
Sky','Montana')");
s.addBatch("insert into dept values
('25','Applications','India')");
//Manually execute the batch
s.executeBatch();
s.addBatch("insert into dept values ('26','HR','Mongolia')");
s.executeBatch();
conn.commit();
ps.close();
...

4.4.7 Use More Than One Database Connection Simultaneously in the Same Request to Avoid a Deadlock in the Database

Using more than one database connection simultaneously in a request can cause a deadlock in the database. This result is most common in JSPs. First, a JSP will get a database connection to do some data accessing. Before the JSP commits the transaction and releases the connection, it invokes a bean, which gets its own connection for its database operations. If these operations are in conflict, they can result in a deadlock.

Furthermore, you cannot easily roll back any related operations if they are done by two separate database connections in case of failure.

Unless your transaction spans multiple requests or requires some complex distributed transaction support, you should try to use just one connection at a time to process the request.

4.4.8 Tune the Database and SQL Statements to Optimize the Handling of Database Resources

Current Web applications are still very database-centric. From 60 percent to 90 percent of the execution time on a Web application can be spent in accessing the database. No amount of tuning on the mid-tier can give significant performance improvement if the database computer is saturated or the SQL statements are inefficient.

Monitor frequently executed SQL statements. Consider alternative SQL syntax, use PL/SQL or bind variables, pre-fetch rows, and cache rowsets from the database to improve your SQL statements and database operations.

Web applications often access a database at the backend. One must carefully optimize handling of database resources, since a large number of concurrent users and high volumes of data may be involved. Database performance tuning falls into two categories:

  • Tuning of SQL tables and statements.

  • Tuning of JDBC calls to access the SQL database.

This section contains the following topics:

4.4.8.1 Tune JDBC

JDBC objects such Connections, Statements, and Result Sets are quite often used for database access in Web applications. Frequent creation and destruction of these objects is quite detrimental to the performance and scalability of the application, as these objects are quite heavyweight. Therefore, always cache these JDBC resources

4.4.8.2 Cache JDBC Connections

Creating a new database connection is an expensive operation that you should avoid. Instead, use a cache of database connections to avoid frequent session creations and tear-downs. EJBs, servlets and JSPs can use or share the connection cache within a JVM. Create as a single object during startup, so that they can be shared across multiple requests.

Implementation Details


See Also:

Oracle Database JDBC Developer's Guide and Reference from the Oracle Database documentation library

4.4.8.3 Cache JDBC Statements

JDBC statement caching avoids cursor creation and tear-down, as well as cursor parsing. It provide two types of statement caching:

  • Implicit: Saves Metadata of cursor but clears the State and Data content of the cursor across calls

  • Explicit: Saves Metadata, Data, and State of the cursor across calls

You can use statement caching with pooled connection and connection cache, for example:

conn.setStmtCacheSize(<cache-size>)

Implementation Details


See Also:

Oracle Database JDBC Developer's Guide and Reference from the Oracle Database documentation library

4.4.8.4 Cache JDBC Rowsets

JDBC cached rowsets provide the following benefits:

  • Disconnected, serializable, and scrollable container for retrieved data

  • Free up connections and cursors faster

  • Local scrolling on cached data

This feature is useful for small sets of data that do not change often.

Implementation Details


See Also:

Oracle Database JDBC Developer's Guide and Reference from the Oracle Database documentation library

4.4.9 Configure Data Source Configurations Options


See Also:

Section "Setting Up Data Sources - Performance Issues," in the Oracle Application Server Performance Guide for more information about setting up data source configuration options for global data sources

4.5 J2EE Class Loading Best Practices

This section describes best practices for J2EE class loading. It includes the following topics:

4.5.1 Avoid Duplicating Libraries to Prevent Loading Problems

Avoid duplicating copies of the same library at different location in your Oracle Application Server installation. Duplication of class libraries can lead to several class loading problems and may consume additional memory and disk space. If your class library is used by multiple applications, then you can put it at the application server level by using the <library> tag in the application.xml file. Or, use the <parent> attribute in the server.xml file to share libraries in two applications.

For a class library that is to be shared by all applications running on your OC4J instance, you can use the default global shared library directory. To do this, place the class library in the $ORACLE_HOME/j2ee/home/applib directory. This directory is configured as a default in the $ORACLE_HOME/j2ee/home/config/application.xml configuration file.

If you have a library that is shared between multiple modules in the same application, that is, two Web modules in the same EAR file, then use the WAR file manifest Class-Path element to share the class libraries between the modules instead of duplicating the libraries in the WEB-INF/lib for every module. In order to enable the use of a Class-Path entry in a WAR file manifest, the following has to be defined in the orion-web.xml for your Web application:

<web-app-class-loader include-war-manifest-class-path="true" />

If you have a library that is shared between multiple modules in the same EAR file, for example, two EJB modules in the same EAR file, then you can place the shared class library and an orion-application.xml file within your EAR file and define the relative path to the shared library using a <library> tag within the orion-application.xml file.

4.5.2 Load Resources Appropriately to Avoid Errors

If you are using dynamic class loading or are loading a resource, such as the properties file in your application, use the correct loader.

If you call Class.forName(), always explicitly pass the loader returned by:

Thread.currentThread().getContextClassLoader();

If you are loading a properties file, use:

Thread.currentThread().getContextClassLoader().getResourceAsStream();

4.5.3 Enable Class Loading Search Order within Web Modules

The servlet 2.3 specification requires that the search order within a Web module is WEB-INF/classes first, then WEB-INF/lib. The specification recommends another class loading related implementation detail:

"the application classloader [could] be implemented  so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container wide library JARs".

To enable this local classes class loading behavior for a Web application, you configure it on a for each Web-application basis by having the following tag in the orion-web.xml file:

<web-app-class-loader search-local-classes-first=ÓtrueÓ/>

This tag is commented out by default and is not the default behavior.

4.5.4 Declare and Group Dependencies to Prevent Hidden or Unknown Dependencies

Make dependencies between your class libraries explicit. Hidden or unknown dependencies will be left behind when you move your application to another environment. Use available mechanisms such as Class-Path entries in manifest files to declare dependencies among class libraries and applications.

Group dependencies between your class libraries and ensure that all dependencies are visible at the same level or preceding. If you must move a library, make sure all the dependencies are still visible.

4.5.5 Minimize Visibility to Satisfy Dependencies

Place the dependency libraries at the lowest-visibility level that satisfies all dependencies. For example, if a library is only used by a single Web application, it should only be included in the WEB-INF/lib directory of the WAR file.

4.5.6 Create Portable Configurations

Create configurations that are as portable as possible. Specify configuration options in the following order:

  • Standard J2EE options

  • Options that can be expressed within the EAR file

  • Server-level options

  • J2SE extension options

4.5.7 Do Not Use the lib Directory for Container-Wide Shared Libraries to Prevent Loading Issues

Do not place container-wide shared application libraries in the $ORACLE_HOME/j2ee/home/lib directory. With Oracle Application Server 10g, this directory is no longer used for loading custom container-wide shared libraries. The container will not load additional libraries placed in this directory.

If you wish to use a container-wide shared library, place your class library in the $ORACLE_HOME/j2ee/home/applib directory or define it using a <library> tag.

4.6 Java Message Service Best Practices

This section describes JMS best practices. It includes the following topics:

4.6.1 Set the Correct time_to_live Value to Avoid Messages Never Expiring

OracleAS JMS message expiration is set in the JMSExpiration header field. If this value is set to zero (the default), then the message will never expire. If the amount of used table space (memory for OC4J) is a concern, then optimally setting the time_to_live parameter will keep messages from accumulating. This is especially true in the publish-subscribe domain where messages may sit forever waiting for the final durable subscriber to return to retrieve the message.


See Also:

Chapter 7, "Developing and Deploying JMS Web Services," in the Oracle Application Server Web Services Developer's Guide for further information about the JMSExpiration header field

4.6.2 Do Not Grant Execute Privilege of the AQ PL/SQL Package to a User or Role

While there are outstanding OracleAS JMS session blocking on a dequeue operation this might cause the granting operation to be blocked and even time out. Execute granting calls before other OracleAS JMS operations.

Another way to avoid the blocking or time out is to grant roles instead of granting specific privileges to the user directly. Oracle Advanced Queuing has an AQ_ADMINISTRATOR_ROLE that you can use, or users may create their own tailored role. You can then grant the execute privilege of a PL/SQL package to this role. Provided the role was created before hand, the granting of the role to the user does not require a lock on the package. This will allow the granting of the role to be executed concurrently with any other OracleAS JMS operation.

Implementation Details


See Also:

Oracle Application Server Integration InterConnect Adapter for AQ Installation and User's Guide

4.6.3 Close JMS Resources No Longer Needed to Keep JMS Objects Available

When JMS objects like JMS connections, JMS sessions, and JMS consumers are created, they acquire and hold on to server-side database and client-side resources.

If JMS programs do not close JMS objects when they are done using them either during the normal course of operation or at shutdown, then database and client-side resources held by JMS objects are not available for other programs to use.

The JVM implementation does not guarantee that finalizers will kick in and clean up JMS object held resources in a timely fashion when the JMS program terminates.

4.6.4 Reuse JMS Resources Whenever Possible to Perform Concurrent JMS Operations

JMS objects like JMS connections are heavyweight and acquire database resources not unlike JDBC connection pools. Instead of creating separate JMS connections based on coding convenience, Oracle recommends that a given JMS client program create only one JMS connection against a given database instance for a given connect string and reuse this JMS connection by creating multiple JMS sessions against it to perform concurrent JMS operations.

JMS administrable objects like queues, queue tables, durable subscribers are costly to create and lookup. This is because of the database round trips and in some cases, JDBC connection creation and tear-down overhead. Oracle recommends that JMS clients cache JMS administrable objects once they are created or looked up and reuse them rather than create or look them up each time the JMS client wants to enqueue or dequeue a message. The Oracle Application Server Java Object Cache could be used to facilitate this caching.

4.6.5 Use Debug Tracing to Track Down Problems

OracleAS JMS enables users to turn debug tracing by setting oracle.jms.traceLevel to values between one and five. (One captures fatal errors only and five captures all possible trace information including stack traces and method entries and exits). Debug tracing allows one to track down silent or less understood error conditions.

4.6.6 Understand Handle/Interpret JMS Thrown Exceptions to Handle Runtime Exceptions

OracleAS JMS is required by the JMS specification to throw particular JMS defined exceptions when certain error or exception conditions occur. In some cases the JMS specification allows or expects OracleAS JMS to throw runtime exceptions when certain conditions occur. Code the JMS client program to handle these conditions gracefully.

The catch all JMS exception, JMSException, that OracleAS JMS is allowed to throw in certain error/exception cases provides information as to why the error/exception occurred as a linked exception in the JMSException. Code the JMS programs to obtain and interpret the linked exception in some cases.

For instance, when resources like processes, cursors, or tablespaces run out or when database timeouts/deadlocks occur, SQL exceptions are thrown by the backend database, which are presented to the JMS client program as linked SQL exceptions to the catch all JMSException that is thrown. It would be useful for JMS programs to log or interpret the ORA error numbers and strings so that the administrator of the database can take corrective action.

The following code segment illustrates a way to print both the JMSException and its linked exception:

try
{...}
catch (JMSException jms_ex){
 
jms_ex.printStackTrace();
if (jms_ex.getLinkedException() != null)
jms_ex.getLinkedException().printStackTrace();}
 

4.6.7 Connect to the Server and Database From the Client Computer to Debug JMS Connection Creation Problems

When debugging JMS connection creation problems or problems with receiving asynchronous messages/notifications make sure that you can:

  • Ping the database using tnsping

  • Connect to the database with its connect string using sqlplus

  • Resolve the name or the IP address of the server computer from the client (by using a simple program that accesses a socket) and vice versa.

4.6.8 Tune Your Database Based on Load to Improve Performance

OracleAS JMS performance is greatly improved by proper database tuning. OracleAS JMS performance is dependent on Oracle Advanced Queuing enqueue/dequeue performance. Oracle Advanced Queuing performance will not scale even if you run the database on a computer with better physical resources unless the database is tuned to make use of those physical resources.

4.6.9 Ensure OracleAS JMS Connection Parameters are Correct to Avoid Runtime Exceptions

Make sure that the parameters that control the datasources underlying OracleAS JMS connections are set appropriately (see datasources best practices). Runtime exceptions that are caused when transaction, inactivity, and cache-connection-availability timeouts occur or connection creation attempts fail can lead to message-driven beans (MDBs) and JMS objects becoming unusable until the underlying cause is resolved. In some cases the change cannot be made dynamically and in others container redeployment maybe needed for the changes to take effect.

Make sure that the EJB-location is used to look up the emulated datasources underlying JMS Connections and MDB instances in case these MDBs and JMS Connections need to participate in Container Managed Transactions. If datasource location is used instead MDBs will not receive messages and JMS Connections will not participate in CMTs.

Since datasource support is not available in OC4J for the application client deployment mode in this release, OracleAS JMS requires that the JMS code use a URL definition to access MS resources within application clients. When the application is a standalone client, configure the <resource-provider> element with a URL property that has the URL of the database where OracleAS JMS is installed and, if necessary, provides the username and password for that database. The following demonstrates a URL configuration:

<resource-provider class="oracle.jms.OjmsContext" name="ojmsdemo">
   <description> OJMS/AQ </description>
      <property name="url" value="jdbc:oracle:thin:@hostname:port number:SID">
      </property>
      <property name="username" value="user">
      </property>
      <property name="password" value="passwd">
      </property>
</resource-provider>

Since OC4J does not support distributed transactions in the application client deployment mode in this release, OracleAS JMS only supports local transactions through it's transacted sessions. If the JMS application requires transaction co-ordination then make sure that it is deployed inside a container and not as an application client.

In this release OC4J optimizes transacted Oracle database operations, including OracleAS JMS operations, so that a 2-PC is not required for them in a distributed transaction that they are involved in when the operations take place against the same Oracle database instance and the same database schema. If possible, make sure that the Oracle Advanced Queuing queues against, which OracleAS JMS operations take place are located in the same schema if the applications using them will perform distributed operations against them. when the underlying database version is 10g or later, if the database operations (including OracleAS JMS operations) take place against different schemas then for the first time the transacted operations take place a complete 2-PC with prepare and commit phases is performed and in subsequent operations a 1-PC optimization kicks in. If it is required that the Oracle Advanced Queuing queues and database tables being used in a distributed transaction be on different schemas, make sure you upgrade to database 10g.

4.6.10 Provide Correct OracleAS JMS Configuration to Avoid Java JMS Exceptions

OracleAS JMS does not validate invalid configuration information, like host and port, at OC4J start up time and these misconfigurations manifest themselves at runtime as Java JMS exceptions. Make sure that the configuration information that you are providing is correct before deploying your JMS application.

OracleAS JMS throws a java.lang.instantiation exception during OC4J startup when the port specified for the JMS Server is already in use. Make sure that the port specified is not already in use when starting up the OC4J instance with a JMS Server enabled.

Make sure that run-time exceptions do not occur in the onMessage call of an MDB instance that uses OracleAS JMS by catching the exceptions is a try-catch block (if feasible). This is because in this release runtime exceptions in the onMessage call can cause the MDB to enter into a endless redelivery loop.

4.7 Oracle Application Server XML Developer's Kit Best Practices

This section describes Oracle XML Developer's Kit best practices. It includes the following topics:

4.7.1 Choose Correct XML Parsers to Improve Efficiency

Choosing the right XML parser is critical to XML applications because it determines how efficient an application can access the XML data. Oracle XDK provides three XML parsers:

  • Document Object Model (DOM) Parser: Parses the XML data and represents the XML data as an in-memory tree object. The DOM parser provides a set of object-oriented interfaces defined by the W3C DOM recommendation to access the XML data.

  • The Simple API for XML (SAX) Parser: Supports an event-based XML parsing standard, which parses XML data and represents XML data as a set of events. The SAX parser pushes out all the events to the callback functions in the registered content handlers.

  • The Java API for XML Parsing) (JAXP) Parser: Supports the JSR-63 standard and provides the standard interfaces in Java for both SAX and DOM XML parsing, XSLT transformation and XML Schema validation.

An XML application may not need to use all of parsers. Normally, an XML parser is chosen based on the application requirements.

Implementation Details

The DOM parser represents XML documents as in-memory tree objects. Therefore, using DOM parsers will leads to high memory footprint, especially when processing large XML documents. Because of the maintaining of in-memory objects, the DOM parser allows applications to dynamically update the XML data and the document structure. The DOM parser is ideal for applications, which need to edit or transform XML documents because these applications require extensive and random document transversals. In order to reduce the memory footprint, you can use the DOMParser.setNodeFactory() to customize the building of the DOM object tree. In addition, you can use the DOMParser.setAttribute(DOMParser.USE_DTD_ONLY_FOR_VALIDATION, true) to avoid including the DTD object in DOM.

The SAX parser parses XML data with limited memory use. Therefore, SAX parsing performs well and is scalable. The SAX parser is good for XML applications that retrieve, filter, and search large XML documents. The SAX parser is not easy to use, because it does not maintain the hierarchical structure of the XML document. As a result, the SAX parser usually is not used for XML transformations or XPath content navigations. Additionally, because the SAX parser does not allow the in-place XML document updates, it is also not good for implementing document-editing tools.

The JAXP parser is based on the JSR-063 standard. The benefit of using the JAXP parser is that the XML applications are more portable, which means that the applications can easily switch to other XML parsers later. JAXP applications are not portable in many cases, because of the incompatibility of DOM objects across different XML parser implementations. For example, a DOM created through JAXP interface using the Apache Xerces parser can't be used by the JAXP XSLT processor based on the Oracle XDK XSLT processor. This is because the Oracle XDK XSLT processor doesn't recognized the DOM object created by the Apache Xerces parser. The same applies the other way around. Additionally, the JAXP parser introduces extra overheads because the implement is wrapped around the existing DOM/SAX implementations.

4.7.2 Improve XSLT Performance

XSLT transformations are widely used by XML applications to convert data from one format to the other or add presentation formats to the XML data before publishing the XML documents. The performance for XSLT has great impact on these kinds of applications. The following section gives you some help on how to make the XSLT transformations perform well.

Implementation Details

  • To boost the performance when transforming XML using XSLT, use SAX to build XSLStylesheet objects and reuse the XSLT objects for subsequent transformations. In the XSL stylesheets, you also need to avoid unconstrained axis like //foo because the Oracle XDK XSLT processor takes full DOM traversal for this kind of XPath evaluations, which is very time-consuming

  • Since the size of DOM object significantly affects the memory use of XSLT transformations, you need to set <xsl:strip-space elements="*"/> to reduce the size of DOM object. Since in many case whitespaces do not affect the transformation result and this option dramatically reduce the size of DOM, it delivers better performance.

4.7.3 Use the Stream-based XML Schema and DTD Validation to Improve Performance

To ensure the XML documents conforms to the defined XML schemas or DTDs, the XML schema or DTD validation is needed. To get the best performance and be able to handle large XML documents, the steam-based XML Schema or DTD validation is needed.

Oracle XDK provides stream-based XML Schema validation if no key/keyref is defined in XML schemas. Here is an example of the SAX-based XML schema validation:

// Build the XML schema object
XSDBuilder builder = new XSDBuilder();
byte [] docbytes = xsd.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(docbytes);
XMLSchema schemadoc = (XMLSchema)builder.build(in,null);
//Parse the input XML document with the XML schema validation
SAXParser parser = new SAXParser();
parser.setXMLSchema(schemadoc);
parser.setValidationMode(XMLParser.SCHEMA_VALIDATION);
parser.parse(xml.getCharacterStream());

The following example shows the stream-based DTD XML document validation:

// Build the DTD object
DOMParser dp = new DOMParser();
dp.parseDTD(fileNameToURL(args[1]), args[2]);
DTD dtddoc = dp.getDoctype();
//Parse and validate the XML document against DTD using SAX
SAXParser parser = new SAXParser();
parser.setDoctype(dtddoc);
//When set Oracle XDK to perform DTD validation
parser.setFeature("http://xml.org/sax/features/validation", true);
parser.parse(fileNameToURL(args[0]));

Since no DOM object is built during the XML schema validation or DTD validation, the process is more scalable.

4.7.4 Process DOM using the JAXB Interface to Access and Operate on XML Data

JAXB (Java Architecture for XML Binding) allows XML applications to access and operate on XML data with easy-to-use Java get and set methods. Oracle XDK JAXB allows JAXB XML applications to directly update the existing DOM objects. Comparing with other JAXB implementations which generate a DOM copy for JAXB operations, the Oracle XDK JAXB DOM support delivers better performance.

Implementation Details

To use this feature:

  1. Build a DOM object as follows:

    DocumentBuilderFactory dbf = DocumentBuilder
    Factory.newInstance();
    dbf.setNamespaceAware(true);
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document currentDoc=b.newDocument();
    
    
  2. Unmarshall the DOM object to the JAXB objects:

    JAXBContext jc = JAXBContext.newInstance("oracle.example.resource");
    oracle.example.resource.ExObject exObject= (oracle.example.resource.ExObject) unmarshaller.unmarshal(currentDoc);
    
    
  3. Use the JAXB set and get APIs to update the DOM object.

    After the DOM updates, the currentDoc will contain all the changes. No extra step of the serialization/deserialization is needed for getting the DOM with the updates. This reduces the overall processing time.

4.8 Oracle Application Server TopLink Best Practices

This section describes best practices for Oracle Application Server TopLink (OracleAS TopLink). It includes the following topics:

OracleAS TopLink is both an integrated and a stand alone component within Oracle Application Server; recommendations in this section are specific to the use of OracleAS TopLink and may not apply to the rest of Oracle Application Server. OracleAS TopLink is compatible with Oracle Application Server or other application servers. OracleAS TopLink fully supports persistence for any Java application with any JDBC 2.0 compliant database.

OracleAS TopLink provides flexibility to interact with any application design, not only at initial construction but also as the application evolves. It also interacts with the complexities of the underlying relational database. This flexibility enables the two domains to interact to form a high performance system, and also evolve separately, while minimizing complexity in the application or database domains. Ultimately, supplying a general best practices is difficult as each situation will be different. Therefore, the reader must understand that these guidelines will not apply in all situations..

4.8.1 Use OracleAS TopLink Mapping Guidelines to Persist Application Data

These are some general guidelines for use in mapping object models or designing relational models to persist application data.

  • If in doubt, always use indirection for any relationship. Indirection does more than just provide deferred reading. It also provides deferred cloning when an object is made transactional.

  • For Inheritance, it is usually advantageous to flatten out the inheritance hierarchy in the relational model. Just because there are several levels of inheritance in the object model, this does not mean that you necessarily need that many levels of joined tables in the relational model.

  • Use version numbers, instead of timestamps, for Optimistic Locking.

  • For very simple aggregation, for example, involving only one database field, consider TypeConversionMappings with an extended ConversionManager.

4.8.2 Use Caching and Concurrency Protection to Improve Performance

OracleAS TopLink has its own specialized object caching mechanism, which is separate from other caching solutions in Oracle Application Server. It is tightly integrated with the rest of the OracleAS TopLink runtime and provides additional performance benefits.

This section describes caching best practices. It includes the following topics:

4.8.2.1 OracleAS TopLink Cache Refreshing Policies

OracleAS TopLink does not automatically perform cache invalidation. In OracleAS TopLink, objects are refreshed in one of the following ways:

  • If the cache holds a weak reference, for example, using WeakIdentityMaps, then objects that are no longer referenced by the application are simply garbage collected on a regular basis

  • If a query is set to refreshIdentityMapResult() then all objects returned from the query are refreshed with the most recent data from the database.

    The query also supports cascading, enabling the developer to control the effects of refreshing on related objects.

  • Objects can be explicitly refreshed using the refreshObject API on the OracleAS TopLink session.

  • Objects are sometimes implicitly refreshed as a result of OracleAS TopLink merging in a remote ChangeSet. This technique is used by the OracleAS TopLink's cache coordination whenever a OracleAS TopLink session is configured to use cache synchronization.

4.8.2.2 Avoid Stale Cache Content

J2EE applications often share data with legacy applications or are running in a clustered environment. When using caching technology in environments such as this, your applications need a well thought out caching strategy to minimize stale data and concurrency failures where the database could be corrupted.

This section summarizes some options a developer has when wanting to explicitly refresh or clear out possible stale caches and how to do this as efficiently as possible. There is also a short discussion on cache coordination. It is important to understand caching and locking and the various configuration options made available. The following article on .

The basic approach to developing a caching strategy involves understanding the volatility of your persistence types and the amount they are shared between users. Then, based on this information, developers must:

  1. Configure an appropriate locking policy on an entity type where there is potential for concurrent modification. This will prevent the usage of cached data from corrupting the database.

  2. Configure the single node caching for each entity type with its initial size.

  3. Use query refreshing or descriptor default refreshing to ensure that persistent instances can be refreshed on critical use cases

  4. Enable cache coordination for entity types that are read-mostly, have shared usage, and are only modified through the TopLink-enabled application.

You can make the refresh more efficient if you are using optimistic locking. As OracleAS TopLink is refreshing each result from a query, you can query it to check the optimistic lock version first to see if the refresh is actually necessary. This option is set at the descriptor level. Here is an example:

public static void amendCustDescriptor(Descriptor d)
{
        d.onlyRefreshCacheIfNewerVersion(); 
}
 

See Also:


4.8.2.3 Cache Coordination

OracleAS TopLink allows developers to use cache coordination when running in a clustered environment. Cache coordination provides messaging between sessions' shared caches. This enables changes made in one node of the cluster to be reflected in other nodes to avoid stale data. It is important to note that cache coordination does not provide a replicated database type cache where all nodes are guaranteed to have the same values. The goal of cache coordination is simply to minimize stale data and the potential for locking failures.

Cache coordination works particularly well in read intensive applications. This feature requires experimentation to see if it is appropriate for your application's use cases and can be affected by a number of issues, such as the volume and frequency of updates, network, JVM, communication protocol, operating system, number of nodes in cluster, and many other factors. Where cache coordination is not feasible, employ the previously mentioned refresh strategies as they work well in a clustered environment.

4.8.3 Use Sequencing to Apply Project-Wide Properties to All Descriptions

At the project level, the Sequencing tab applies two project-wide properties that are applied to all descriptors that use sequencing:

  • Whether OracleAS TopLink uses database native sequencing objects or a table to manage sequences. The preference is to use Oracle native sequencing.

  • The sequence preallocation size determines how many sequences OracleAS TopLink grabs and caches in one call. If you use Oracle native sequencing then the increment property of the sequence object must match the OracleAS TopLink sequence preallocation size.

At the descriptor level, the Use Sequencing section enables you to specify:

  • Name: For native sequencing this will be the name of the database sequence object to be used.

  • Table: The table that contains the field that the sequence will be applied. You choose the table from a drop-down list of all the tables the descriptor is mapped.

  • Field: The table field that the sequence will be applied. You choose the fields from a drop-down list for the chosen table that are mapped by the descriptor.

4.8.4 Implement Performance Options to Improve Performance

This section describes performance options best practices. It includes the following topics:

4.8.4.1 Performance Diagnostics

When the OracleAS TopLink UnitOfWork commits, every object registering into this UnitOfWork is automatically inspected for changes. This process can be time-consuming. One of the easiest ways to improve performance is to minimize the number of objects that require inspection.

Developers should have an idea of the size of the cache. If this transaction involves the editing of five or 10 objects, then try to ensure that there are only five or 10 objects registered. A large cache size for a comparatively small transaction means that the UnitOfWork will be performing a lot of needless work during it's commit cycle.

Implementation Details


See Also:

White paper Oracle Application Server TopLink Unit of Work Primer available from the Oracle Technology Network at http://www.oracle.com/technology/products/ias/toplink/technical/unitOfWorkWP.pdf

4.8.4.2 Tuning

This section describes tuning best practices. It includes the following topics:

Tuning affects three important aspects of OracleAS TopLink performance:

  • Minimizing the number of objects in the UnitOfWork transactional cache

  • Minimizing the number of objects read in from the Database

  • Taking advantage of named queries

4.8.4.2.1 Reducing The Size of the Transactional Cache

After determining that the UnitOfWork is checking too many objects, one must look for ways of reducing the size of this transactional cache. There are several techniques to use:

  • Try not to register objects that are not going to be changed

  • Use Indirection. The importance of this cannot be overstated. Indirection allows OracleAS TopLink to only register related objects into a transaction when they are accessed in the context of that transaction. Without indirection, OracleAS TopLink will have to check all related objects for updates.

  • Avoid querying against a UnitOfWork. If only a few objects from a query will actually be changed then registered the whole result set into the UnitOfWork will be a lot of overhead (both at query time and at commit time). Querying against a UnitOfWork is very convenient but it can be very dangerous if not used properly.

  • Make use of the UnitOfWork unregisterObject API. It is worthwhile un-registering objects from a UnitOfWork if you know that there have been no changes to the objects.

4.8.4.2.2 Analyzing the Object-Building phase

After executing an OracleAS TopLink ObjectLevelQuery, OracleAS TopLink has to build all objects that have not been cached in a previous query. This operation can be expensive if it is allowed to go unchecked. After building an object, OracleAS TopLink will throw a postBuild descriptor event. This event can be useful to diagnose situations where slow performance is caused by building too many objects. The following is an example of a postBuild descriptor event:

public class CaminusSessionListener extends SessionEventAdapter{
   /**
   * catch the postBuild event for EVERY class in the current system
   */
   public void preLogin(SessionEvent arg0)
   {
         Session session = arg0.getSession(); 
                for (Iterator it = session.getDescriptors().values().iterator(); it.hasNext();) {
            Descriptor desc = (Descriptor)it.next(); desc.getEventManager().addListener(
                 new DescriptorEventAdapter() {
                      public void postBuild(DescriptorEvent event)
                         // do we want to keep a running tally on the number of objects that are built    
                                // during a single transaction in any case, we have access to the object which    
                                // has just been built.  Here it is:
                              {
                                  object object = event.getObject();
                         };
                        };
        }

In this example, the postBuild event for every class in the current OracleAS TopLink project is noted. Look for situations where you are building more objects than you feel are actually required to handle the current request. After determining that too many objects are being built, developers should consider some of the following:

  • Further qualify the selection criteria of the DatabaseQuery if possible.

  • Use Indirection. Indirection allows OracleAS TopLink to avoid building related objects that are not accessed during request processing.

  • Increase the sub-cache sizes if using "soft" identity maps, such as SoftCacheWeakIdentityMap and HardCacheWeakIdentityMap.

  • Are there some "ToMany" mappings which have become overly large? For example, are you mapping collections of objects involving thousands of objects when typical Use Cases involve accessing only a small subset of these objects? In these situations, it is advisable to unmap the "ToMany" relationship and have the parent query for the subset of children directly.

  • Use ReportQueries to build summary reports rather than actual objects.

4.8.4.2.3 Use of Named Queries

The importance of named queries is typically underestimated. Defining queries in one place and then referencing them by name allows OracleAS TopLink to optimize several key steps in the execution of a query. In addition, centralizing query definitions can save weeks of development effort during the performance tuning phase of application development.

Without named queries, users end up with the following types of usage patterns scattered throughout the system:

ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(Person.class);
Expression exp = query.getExpressionBuilder().get("id").equal(argument));
query.setSelectionCriteria(exp);
getTopLinkSession().executeQuery(query);


There are several problems with this approach. You must build and parse the preceding expression every time this query is executed. In addition, the argument is statically bound into the expression, and into the generated select statement. This means that this statement will have to be prepared over and over again.

In an alternative implementation, the query is defined outside of the application code. This is best done within the mapping editors, such as Oracle JDeveloper or the standalone OracleAS TopLink Workbench. This allows the developer to graphically build and tune a named query where the definition is stored within the map (project) XML. This simplifies development, eliminating significant code that requires maintenance, but more importantly allows for changes to the models and mapping to reflect any queries that are no longer accurate. This ability to be notified of mapping changes that break queries greatly improves developer productivity at design time.

Alternative queries can be defined or customized in descriptor after-load methods. The following is an example of the descriptor after-load method.

public static void afterLoad(ClassDescriptor desc)
{
   ReadObjectQuery query = new ReadObjectQuery();
   ExpressionBuilder builder = query.getExpressionBuilder();
   query.addArgument("ARG");
   
   Expression exp = builder.get("id").equal(builder.getParameter("ARG"));
   query.setSelectionCriteria(exp);

   desc.getQueryManager().addQuery("findByArg",query);
}
 

Using this query definition, application code can execute this query over and over again without having to re-build, re-parse, and re-prepare the any of the underlying implementation details.

getTopLinkSession().executeQuery("findByArg".Person.class,argument);

Using named queries, along with enabling bindAllParameters in the OracleAS TopLink, the DatabaseLogin can significantly improve the performance of all DatabaseQueries.

The use of named queries is conceptually very simple. Logistically, it is very difficult if the system has already been developed with DatabaseQuery definitions spread haphazardly through the application code. This aspect of application design needs to be addressed early in the development cycle.

4.9 Oracle Application Server Forms Services Best Practices


See Also:

Whitepaper Oracle 10gR2AS Forms Services - Best Practices for Application Development available from the Oracle Technology Network at http://www.oracle.com/technology/products/forms/pdf/bestpractices10gr2.pdf