Skip Headers

Oracle® Application Server 10g Best Practices
10g (9.0.4)
Part No. B12223-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous Next  

7 J2EE Applications

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

7.1 Java Server Pages Best Practices

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

7.1.1 Pre-Translate JSPs Before Deployment

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.

7.1.2 Separate Presentation Markup From Java

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.

7.1.3 Use JSP Template Mechanism

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.

7.1.4 Set Sessions=False If Not Using 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. To disable sessions, set the directive as follows:

<%@page session="false" %>

7.1.5 Always Invalidate Sessions When No Longer Used

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 per 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. You can change this for a specific application by setting the <session-timeout> parameter in the <session-config> element of the web.xml file.

7.1.6 Set Main_Mode Attribute To "justrun"

This attribute, found in the global-web-application.xml file, determines whether classes are automatically reloaded or JSPs are automatically recompiled. In a deployment environment set main_mode to justrun. The runtime dispatcher does not perform any timestamp checking, so there is no re-compilation 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.

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 assures that the JSP is compiled before you set the justrun flag.

7.1.7 Use Available JSP Tags In Tag Library

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.

7.1.8 Minimize Context Switching Between Servlets and EJBs

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.

7.1.9 Package JSP Files In EAR File For Deployment Rather Than Standalone

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

7.1.10 Use Compile-Time Object Introspection

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.

7.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. Note that a page that can be dynamically included must be an independent entity, which can be translated and executed on its own.

7.1.12 Disable JSP Page Buffer If Not Used

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. If you need to increase the buffer size, for example to 20KB, 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" %>

7.1.13 Use Forwards Instead of Redirects

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.

7.1.14 Use JSP Tagged Cache

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. OracleJSP provides the following tags for using the Java Object Cache:

  • ojsp:cache

  • ojsp:cacheXMLObj

  • ojsp:useCacheObj

  • ojsp:invalidateCache

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

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

  • allow easy maintenance as the TLDs can be in a single JAR file

  • application size is minimized

OC4J JSP well_known_taglib_loc configuration parameter specifies the location of the shared tag library directory. The default location is j2ee/home/jsp/lib/taglib/ under the ORACLE_HOME directory. If ORACLE_HOME is not defined, it is the current directory (from which the OC4J process was started).

The shared directory must be added to the server-wide CLASSPATH by specifying it as a library path element. The default location is set in the application.xml file in the OC4J configuration files directory (j2ee/home/config by default) and can be altered.

7.1.16 Use JSP-Timeout for Efficient Memory Utilization

Resource utilization is a key factor for any efficient application. Oracle Application Server introduces the <orion-web-app> attribute jsp-timeout that can be specified in the OC4J global-web-application.xml file or orion-web.xml file. 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 frees up resources in situations where some pages are called infrequently. The default value is 0, for no timeout.

Like other attributes use the <orion-web-app> element of the OC4J global-web-application.xml file to apply to all applications in an OC4J instance. To set configuration values to a specific application, use the <orion-web-app> element of the deployment-specific orion-web.xml file.

7.1.17 Workarounds for the 64K Size Limit for the Generated Java Method

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

As a general rule, design smaller JSPs for your web application.

If your JSP uses tag libraries heavily, and if you are hitting the 64k limit, use the reduce_tag_code config parameter to reduce the size of generated code for custom tag usage. Note that this may impact performance.

7.1.18 Workarounds for the Size Limit

The JVM limits the amount of code to 65536 bytes per 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 64k limit, use the reduce_tag_code configuration parameter to reduce the size of generated code for custom tag usage. However, note that this 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.

7.1.19 Hiding JSP Pages

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. Put the JSPs you want to hide into a /WEB-INF directory.

Access within your application code with the following:

7.2 Sessions Best Practices

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

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

7.2.2 Replicate Sessions if Persisting is Not an Option

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 (which are in the same cluster). This provides a performance improvement because the sessions remain in memory, and fault tolerance. This is 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.Hence, the best practice here is to at least setup two servers in an island, so that they can back session state for each other.

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

7.2.4 Set Session Timeout Appropriately

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

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

7.2.6 Always Use Islands, But Keep Island Size Small

Setting up an island of OC4J JVMs causes the sessions to be replicated across all JVMs. This provides better fault tolerance, since a server crash does not necessarily result in a lost session. Oracle Application Server automatically re-routes request to another server in the island; thus an end-user never finds out about a failure.However, this replication overhead increases as more servers are added to the island. For example, if your session object requires 100KB per user, and there are 100 users per server. This results in a 10MB memory requirement for session replication per server. If you have 5 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 3.Hence, setting up multiple islands, with few servers in an island is a better choice compared to having a fewer number of larger sized islands.

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

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

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

7.2.10 Invalidate Sessions

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

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

7.3 Enterprise Java Bean Best Practices

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

7.3.1 Local, Remote, and Message Driven EJBs

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.

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

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

7.3.4 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 (Section 7.3.1, "Local, Remote, and Message Driven 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.

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

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

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

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

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.

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

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

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

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

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

7.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. You can switch this check by setting the do-select-before-insert=ÓfalseÓ for your entity bean in the orion-ejb-jar.xml file.

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

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

7.3.17 Set prefetch-size 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. You can specify the prefetch-size attribute for your finder method in the orion-ejb-jar.xml file.

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

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

7.4 Data Access Best Practices

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

7.4.1 Datasources Connections Caching and Handling

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. All connections can be explicitly closed by the application.

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.

7.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 two above.

7.4.2 Datasource 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.

7.4.3 Disable Escape Processing for Better 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. 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);

7.4.4 Defining Column Types

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

  • Saves a roundtrip 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 roundtrip 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();…
 

7.4.5 Prefetching Rows Improves 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();
…
.
.

7.4.6 Update Batching Improves 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.

7.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();
...

7.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();
...

7.4.7 Use Emulated and Non-Emulated Data Sources Appropriately

For speed and performance reasons emulated data sources are preferred over non-emulated ones.

A non-emulated datasource provides JDBC v2.0 compliance and additional capabilities such as XA which may not be required for all applications.

Some of the performance related configuration options have different affects, depending on the type of the data source. OC4J supports two types of data sources, emulated and non-emulated.

The pre-installed default data source is an emulated data source. Emulated data sources are wrappers around Oracle data sources. If you use these data sources, your connections are extremely fast, because they do not provide full XA or JTA global transactional support. Oracle recommends that you use these data sources for local transactions or when your application requires access or update to a single database.

You can use emulated data sources for Oracle or non-Oracle databases. You can use the emulated data source to obtain connections to different databases by changing the values of the URL and connection-driver parameters.

The following is a definition of an emulated data source:

<data-source
class="com.evermind.sql.DriverManagerDataSource"
name="OracleDS"
location="jdbc/OracleCoreDS"
xa-location="jdbc/xa/OracleXADS"
ejb-location="jdbc/OracleDS"
connection-driver="oracle.jdbc.driver.OracleDriver"
username="scott"
password="tiger"
url="jdbc:oracle:thin:@localhost:5521:oracle"
inactivity-timeout="30"
/>

Non-emulated data sources are pure Oracle data sources. These are used by applications that want to coordinate access to multiple sessions within the same database or to multiple databases within a global transaction.

7.4.8 Use the EJB-Aware Location Specified in Emulated Data Sources

Each data source is configured with one or more logical names that allow you to identify the data source within J2EE applications. The EJB-location is the logical name of an EJB data source. In addition, use the EJB-location name to identify data sources for most J2EE applications, where possible, even when not using EJBs. The EJB-location only applies to emulated data sources. You can use this option for single phase commit transactions or emulated data sources.

Using the EJB-location, the data source manages opening a pool of connections, and manages the pool. Opening a connection to a database is a time-consuming process that can sometimes take longer than the operation of getting the data itself. Connection pooling allows client requests to have faster response times, because the applications do not need to wait for database connections to be created. Instead, the applications can reuse connections that are available in the connection pool.

Oracle recommends that you only use the EJB-location JNDI name in emulated data source definitions for retrieving the data source. For non-emulated data sources, you must use the location JNDI name.

7.4.9 Set the Maximum Open Connections in Data Sources

The max-connections option specifies the maximum number of open connections for a pooled data source. To improve system performance, the value you specify for the number max-connections depends on a combination of factors including the size and configuration of your database server, and the type of SQL operations that your application performs. The default value for max-connections and the handling of the maximum depends on the data source type, emulated or non-emulated.

For emulated data sources, there is no default value for max-connections, but the database configuration limits that affect the number of connections apply. When the maximum number of connections, as specified with max-connections, are all active, new requests must wait for a connection to be become available. The maximum time to wait is specified with wait-timeout.

For non-emulated data sources, there is a property, cacheScheme, that determines how max-connections is interpreted. The following lists the values for the cacheScheme property (DYNAMIC_SCHEME is the default value for cacheScheme).

FIXED_WAIT_SCHEME: In this scheme, when the maximum limit is reached, a request for a new connection waits until another client releases a connection.

FIXED_RETURN_NULL_SCHEME: In this scheme, the maximum limit cannot be exceeded. Requests for connections when the maximum has already been reached return null.

DYNAMIC_SCHEME: In this scheme, there is no maximum limit.

For some applications you can improve performance by limiting the number of connections to the database (this causes the system to queue requests in the middle-tier).

For example, for one application that performed a combination of updates and complex parallel queries into the same database table, performance was improved by over 35% by reducing the maximum number of open connections to the database by limiting the value of max-connections.

7.4.10 Set the Minimum Open Connections in Data Sources

The min-connections option specifies the minimum number of open connections for a pooled data source.

For applications that use a database, performance can improve when the data source manages opening a pool of connections, and manages the pool. This can improve performance because incoming requests don't need to wait for a database connection to be established; they can be given a connection from one of the available connections, and this avoids the cost of closing and then reopening connections.

By default, the value of min-connections is set to 0. When using connection pooling to maintain connections in the pool, specify a value for min-connections other than 0.

For emulated and non-emulated data sources, the min-connections option is treated differently.

For emulated data sources, when starting up the initial min-connections connections, connections are opened as they are needed and once the min-connections number of connections is established, this number is maintained.

For non-emulated data sources, after the first access to the data source, OC4J then starts the min-connections number of connections and maintains this number of connections.

Limiting the total number of open database connections to a number your database can handle is an important tuning consideration. You should check to make sure that your database is configured to allow at least as large a number of open connections as the total of the values specified for all the data sources min-connections options, as specified in all the applications that access the database.

7.4.11 Setting the Cache Connection Inactivity Timeout in Data Sources

The inactivity-timeout specifies the time, in seconds, to cache unused connections before closing them.

To improve performance, you can set the inactivity-timeout to a value that allows the data source to avoid dropping and then re-acquiring connections while your J2EE application is running.

The default value for the inactivity-timeout is 60 seconds, which is typically too low for applications that are frequently accessed, where there may be some inactivity between requests. For most applications, to improve performance, Oracle recommends that you increase the inactivity-timeout to 120 seconds.

To determine if the default inactivity-timeout is too low, monitor your system. If you see that the number of database connections grows and then shrinks during an idle period, and grows again soon after that, you have two options:

  • You can increase the inactivity-timeout.

  • You can increase the min-connections.

7.4.12 Set the Wait for Free Connection Timeout in Data Sources

The wait-timeout specifies the number of seconds to wait for a free connection if the connection pool does not contain any available connections (that is, the number of connections has reached the limit specified with max-connections and they are all currently in use).

If you see connection timeout errors in your application, increasing the wait-timeout can prevent the errors. The default wait-timeout is 60 seconds.

If database resources, including memory and CPU are available and the number of open database connections is approaching max-connections, you may have limited max-connections too stringently. Try increasing max-connections and monitor the impact on performance. If there are not additional machine resources available, increasing max-connections is not likely to improve performance.

You have several options in the case of a saturated system:

  • Increase the allowable wait-timeout.

  • Evaluate the application design for potential performance improvements.

  • Increase the system resources available and then adjust these parameters.

7.4.13 Set the Connection Retry Interval in Data Sources

The connection-retry-interval specifies the number of seconds to wait before retrying a connection when a connection attempt fails.

If the connection-retry-interval is set to a small value, or a large number of connection attempts is specified with max-connect-attempts this may degrade performance if there are many retries performed without obtaining a connection.

The default value for the connection-retry-interval is 1 second.

7.4.14 Set the Maximum Number of Connection Attempts in Data Sources

The max-connect-attempts option specifies the maximum number of times to retry making a connection. This option is useful to control when the network is not stable, or the environment is unstable for any reason that sometimes makes connection attempts fail.

If the connection-retry-interval option is set to a small value, or a large number of connection attempts is specified with max-connect-attempts this may degrade performance if there are many retries performed without obtaining a connection.

The default value for max-connect-attempts is 3.

7.4.15 Use JDBC Connection Pooling and Connection Caching

Constant creation and destruction of resource objects can be very expensive in Java. Oracle suggests using a resources pool to share resources that are expensive to create. The JDBC connections are one of the most common resources used in any Web application that requires database access. They are also very expensive to create; overhead from hundreds of milliseconds to seconds (depending on the load) in establishing a JDBC connection on a mid-size system with 4 CPUs and 2 GB memory.

In JDBC 2.0, a connection-pooling API allows physical connections to be reused. A pooled connection represents a physical connection, which can be reused by multiple logical connections. When a JDBC client obtains a connection through a pooled connection, it receives a logical connection. When the client closes the logical connection, the pooled connection does not close the physical connection. It simply frees up resources, clears the state, and closes any statement objects associated with the instance before the instance is given to the next client. The physical connection is released only when the pooled connection object is closed directly.

The term pooling is extremely confusing and misleading in this context. It does not mean there is a pool of connections. There is just one physical connection, which can be serially reused. It is still up to the application designer to manage this pooled connection to make sure it is used by only one client at a time.

To address this management challenge, Oracle's extension to JDBC 2.0 also includes connection caching, which helps manage a set of pooled connections. It allows each connection cache instance to be associated with a number of pooled connections, all of which represent physical connection to the same database and schema. You can use one of Oracle's JDBC connection caching schemes (DYNAMIC, FIXED_WITH_NO_WAIT, or FIXED_WAIT) to determine how you want to manage the pooled connections, or you can use the connection caching APIs to implement your own caching mechanisms.

7.4.16 Use JDBC Statement Caching

Use JDBC statement caching to cache a JDBC PreparedStatement or OracleCallableStatement that is used repeatedly in the application to:

  • prevent repeated statement parsing and recreation

  • reduce the overhead of repeated cursor creation

The performance gain will depend on the complexity of the statement and how often the statement has to be executed. Since each physical connection has its own statement cache, the advantage of using statement caching with a pool of physical connections may vary. That is, if you execute a statement in a first connection from a pool of physical connections, it will be cached with that connection. If you later get a different physical connection and want to execute the same statement, then the cache is not useful.


See Also:

Oracle JDBC Developer's Guide and Reference

7.4.17 Avoid Using More Than One Database Connection Simultaneously in the Same Request

Using more than one database connection simultaneously in a request can cause a deadlock in the database. This is most common in JSPs. First, a JSP will get a database connection to do some data accessing. But then, 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.

7.4.18 Tune the Database and SQL Statements

Current Web applications are still very database-centric. From 60% to 90% 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 machine 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 can be divided into two categories:

7.4.18.1 JDBC Tuning

JDBC objects such Connections, Statements, and Result Sets are quite often used for database access in Web applications. Frequent creation & destruction of these objects can be quite detrimental to the performance and scalability of the application as these objects are quite heavy-weight. So it is always desirable to cache these JDBC resources

7.4.18.2 JDBC Connection Caching

  • Reuse database connections thus avoiding frequent session creations and tear-downs.

  • EJBs, servlets, JSPs can use or share the connection cache within a JVM.

  • Create at startup as a single object so that they can be shared across multiple requests.

7.4.18.3 JDBC Statement Caching

  • Avoids cursor creation and teardown

  • Avoid cursor parsing

  • 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

  • Can be used with Pooled Connection and Connection Cache

    For example: conn.setStmtCacheSize(<cache-size>)

7.4.18.4 JDBC Cached Rowsets

  • Result set implementation that is disconnected, serializable, and scrollable

  • Free up connections and cursors faster

  • Local scrolling on cached data

  • Specially useful for:

    • small read-only data set

    • scrolling for long time

7.5 J2EE Class Loading Best Practices

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

7.5.1 Avoid Duplicating Libraries

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, i.e. 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.

7.5.2 Load Resources Appropriately

If you are using dynamic class loading or are loading a resource (for example, 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();

7.5.3 Setting 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 per 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.

7.5.4 Declare and Group 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 amongst class libraries and applications.

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

7.5.5 Minimize Visibility

Dependency libraries should be placed 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.

7.5.6 Keep Configurations Portable

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

  • Standard J2EE options

  • Options that can be expressed within your EAR file

  • Server-level options

  • J2SE extension options

7.5.7 Do not Use the lib Directory for Container Wide Shared Libraries

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.

7.6 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 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 JDBC2.0 compliant database.

OracleAS TopLink is 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 interract to form a high performance system, and also evolve separately, while minimizing complexity in the application or database domains. Ultimately, supplying a general best practises is difficult as each situation will be different. Therefore, the reader must understand that these guidelines will not apply in all situations.


See Also:

Oracle Application Server TopLink Application Developer's Guide

7.6.1 OracleAS TopLink Mapping

These are some general guidelines for use in mapping object models and/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.

7.6.2 Team Development

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

7.6.2.1 Team Working with Metadata

Part of the OracleAS TopLink development effort involves the creation of metadata: the Project, Descriptor and Mappings.

Most of this metadata can be defined using the Mapping Workbench (MW) tool.

It is good practice to create an MW administrator role to be responsible for any necessary updates to metadata, whether occasioned by changes to the object model, schema, or business logic.

An administrator will need to deal with change requests from both the Java development and DBA team, and be able to call on expert resource in both.

However, modelling the metadata requires significant knowledge of the business logic. Answering questions such as which attributes should be indirected or not by default, which descriptors are read-only, are reference data, and others should be reflected in the skill set of the role.

The MW project is represented as a series of files under a directory structure. The number and names of these files changes frequently, it is not recommended to enact source control if all the files are contained individually in source management.

The most effective technique for most teams is to zip up the MW directory and place the zip in source control. This mandates that only one person can be updating the workbench at any one time. In practice, after the first few days of a project this is rarely a restriction, and much preferable than attempting to merge changes made simultaneously by more than one person on the highly interdependent mappings.

Not all the metadata can be defined in the MW: other metadata has to be defined in initialization code, usually through descriptor amendment (after load) methods and in a Session preLogin event. (Several examples of metadata configured this way, such as mappings that use ProxyIndirection, are included in the Example 7-1).

Thus it is necessary to consider, for versioning and baselining purposes, the MW project, the sessions.xml file, and any code that further refines the metadata during session initialization as a single unit that together fully defines the static and runtime O-R mapping properties. It is recommended to factor out code that alters the metadata into separate clearly defined classes and version/baseline. Named queries, which effectively represent metadata describing how most efficiently to retrieve data required for some business process, should also be considered part of this unit.

O-R mapping metadata has dependencies both to the Object Model and business logic, and on the database schema. Versioning/baselining should reflect a connection between a specific code version and a specific schema version.

7.6.2.2 Large and/or Geographically Diverse Project Development

It is common practice in large or geographically diverse projects to divide the metadata into several projects. For example, one project for all common objects, and other projects for particular subsystems.

Projects will have interdependencies. For instance, many subsystems will have mappings that reference objects from the common project. To be able to map in the subsystem it is necessary only to create a stub descriptor with the correct name and package (importing the relevant class into the workbench is the easiest way to achieve this).

It is not necessary to map any part of the stub descriptor, it is just there to be mapped to by the descriptors which are really part of the project. Stub descriptors should be "deactivated" which means that they will not be deployed with the active classes in the deployment XML Project source file. This option is on the right-button of the context menu. Use the following steps:

  1. Instantiate all the separate projects.

  2. Choose a base project (the project which represents the common domain objects is almost always the appropriate one).

  3. Iterate through the descriptors of all the other projects, and add each descriptor to the base project.

  4. Only configure and login a session on this base project.

A Session pre-login event can be used to combine the Descriptors from the two separately developed projects together into one super-Project. It is this Project which is actually used to build the runtime OracleAS TopLink Session. However, in this scenario, the different Projects must share exactly the same DatabaseLogin characteristics (for example, target Database, use of ConnectionPools, use of external TransactionController). As long as this is valid, then OracleAS TopLink Projects can be developed separately and later merged into a single run-time Session.

The code sample shown in Example 7-1 represents a preLogin event which combines two Projects (from two separate MW Projects) into one single run-time Session.

Example 7-1 preLogin Event Script

public void preLogin(SessionEvent event)
{
   // get the Project from the current Session
   // (we are going to add metadata to this project)
   Project projectA = event.getSession().getProject();

   //this session should use this conversion manager which will 
   // use the current ClassLoader to load classes

   ConversionManager conversionManager = new ConversionManager();
   ConversionManager.setDefaultLoader(this.getClass().getClassLoader());
   projectA.getLogin().getPlatform().setConversionManager(conversionManager);

   // somehow, we need to look up "other" projects in order to 
   // merge their metadata into the current project. There
   // are many potential ways of managing this (SessionManager,
   // .properties filed, etc.). For now, we will hard code
   // a reference to the "other" Project .class

   Project projectB = new ProjectB();

   // iterate over all of the Descriptors in ProjectB
   // and add them to ProjectA
   (
   for (Enumeration enum = projectB.getDescriptors().elements()); 
   enum.hasMoreElements();
   )
   {
   Descriptor descriptor = (Descriptor)enum.nextElement();
   projectA.addDescriptor(descriptor);
   }
}
 

If you are using a sessions.xml deployment descriptor to define runtime sessions the technique above is still used. Create a uniquely named session for each project in the sessions.xml file. The only runtime information that is relevant is that in the session describing the base project

There are overloaded versions of the SessionManager methods that read in a session from sessions.xml without logging it in. Use these to instantiate each Session object, obtain their Project object and iterate through the descriptors as above. Then login to the base Project session.

7.6.3 Caching

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:

7.6.3.1 OracleAS TopLink Cache Refreshing Policies

This section describe OracleAS TopLink Refreshing Policies. It includes the following topics:

7.6.3.1.1 When the Cache is Refreshed

OracleAS TopLink does not 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 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

  • 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 CacheSynchronizationManager whenever a OracleAS TopLink Session is configured to use cache sync.

7.6.3.1.2 Choosing A Cache Refesh Policy

OracleAS TopLink cache usage, for a single ServerSession, is straight forward in two different situations

  • the cache can always be trusted (for example, no non middle-tier processes are changing the database). The default policy of checkCache is preferred here.

  • the cache can never be trusted. A policy of "alwaysRefreshCache/disableCacheHits" is appropriate in this situation. OracleAS TopLink queries can be configured to handle either of these two extremes.

However, if you the situation lies somewhere in between, there is some criteria that can be used to determine whether or not the a query should be refreshed. The cache usage settings can be adjusted dynamically to create the proper behavior.

In the code example, QueryRedirector is shown which can adjust whether or not a query refreshes itself based on some external criteria. Note that this is still implemented as a named query. It differs only in that the decision to refresh or to check the cache is not made until run time.

Example 7-2 QueryRedirector Example

public void amend(Descriptor descriptor){
   ReadObjectQuery query = new ReadObjectQuery();
   // configure query normally and then add the Redirector
   query.setRedirector
   (
   new QueryRedirector()
   )
   public Object invokeQuery(DatabaseQuery query, DatabaseRow row, Session    session)
   {
   if ( checkCustomRefreshContext() )
   }
   {
   System.out.println("need to refresh");
((ObjectLevelReadQuery)query).setShouldRefreshIdentityMapResult(true);
   }
   else
   {
   System.out.println("execute normally");
   }
   return 
((oracle.toplink.publicinterface.Session)session).executeQuery(query,row);
   }
   );
   descriptor.getQueryManager().addQuery("test",query);}

A pattern such as this can be used to implement queries which refresh themselves only if some external criteria is met. An example of this is a query which changes its cache usage settings on a periodic basis. If the query has not been executed in a set amount of time, then the result would be automatically refreshed.

Note that this discussion addresses problems that occur as a result of non-OracleAS TopLink processes altering the database. For the case of multiple OracleAS TopLink sessions running in a clustered environment, the technique of handling OptimisticLock Exceptions, and possibly even using cache synchronization are still the recommended approaches.

7.6.3.2 Avoiding Stale Cache Content

J2EE Applications often share data with legacy applications and/or are running in a clustered environment. When using caching technology in environments such as this, your applications need a well thought out refresh strategy.

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

If your application requires that a particular object needs to be refreshed, you can make use of the refreshObject API. For example:

clientSession.refreshObject(myObj);

If you are going to execute a query and you know the data is going to become stale quickly, you can tell OracleAS TopLink not to cache the results in the first place:

Here is an example:

ExpressionBuilder stockTick = new ExpressionBuilder();
Expression exp = stockTick.get("symbol").equal("ORCL");
ReadAllQuery raq = new ReadAllQuery(StockTick.class, exp);
//This is going to be stale immediately, so don't cache it
raq.dontMaintainCache();
results = session.executeQuery(raq);

Recommended Approach: On a per Query basis, tell the query to refresh the results of the cache to ensure you have the most up to date data. This may seem inefficient but it is faster to refresh an object than it would be to create a brand new one. Any type of query can be told to refresh with results.

ReadAllQuery raq = new ReadAllQuery(Employee.class);
raq.setSelectionCriteria( … your expression …); 
raq.refreshIdentityMapResult();

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:

Oracle Application Server TopLink Mapping Workbench User's Guide

7.6.3.3 Cache Synchronization

OracleAS TopLink allows developers to use cache synchronization when running in a clustered environment. Cache synchronization works particularly well in read intensive applications. This feature requires experimentation to see if it is appropriate for your applications 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 synchronization is not feasible, employ the previously mentioned refresh strategies as they work well in a clustered environment.

7.6.4 Sequencing

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 Pre-Allocation 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 Pre-Allocation size.

At the Descriptor level, the Use Sequencing section allows you to specify:

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

  • Table: The table which contains the field that the sequence will be applied to (Chosen from drop-down of all the tables a descriptor is mapped to).

  • Field: The table field that the sequence will be applied to (Chosen from a drop down of all the fields from the chosen table that are mapped by the descriptor).

7.6.5 Performance Options

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

7.6.5.1 Performance Diagnostics

When the OracleAS TopLink UnitOfWork commits, every object registering into this UnitOfWork is automatically inspected for changes. This is obviously a time-consuming process and one of the easiest ways to improve performance is to minimize the number of objects that require inspection. One technique to analyze this is to check the size of the UnitOfWork transactional cache just before beginning the commit cycle. The appropriate location for this code depends on whether or not the OracleAS TopLink Session is using an External Transaction Controller.

// "session" refers to a TopLink DatabaseSession
// "uow" refers to the current UnitOfWork
for (Enumeration enum = session.getDescriptors().keys(); enum.hasMoreElements();)
{
   Class javaClass = (Class)enum.nextElement();
   IdentityMap map = uow.getIdentityMapManager().getIdentityMap(javaClass);
   System.out.println(javaClass.getName()+":  "+map.getSize());
}

This code shows the user how many of each type of object are registered into the current UnitOfWork. Since every object in this transactional cache must be checked for changes, a large cache implies a longer commit cycle.

Developers should have an idea of what the size of this cache should be. If this transaction involves the editing of 5 or 10 objects then try to insure that there are only 5 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.

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

7.6.5.2.1 Analyzing the UnitOfWork Commit Cycle

When the OracleAS TopLink UnitOfWork commits, every object registered into this UnitOfWork is automatically inspected for changes. This is obviously a time-consuming process and one of the easiest ways to improve performance is to minimize the number of objects that require inspection. One technique to analyze this is to check the size of the UnitOfWork "transactional" cache just before beginning the commit cycle. The appropriate location for this code depends on whether or not the OracleAS TopLink Session is using an External Transaction Controller.

// "session" refers to a TopLink DatabaseSession
// "uow" refers to the current UnitOfWork
for (Enumeration enum = session.getDescriptors().keys(); enum.hasMoreElements();)
{
   Class javaClass = (Class)enum.nextElement();
   IdentityMap map = uow.getIdentityMapManager().getIdentityMap(javaClass);
   System.out.println(javaClass.getName()+":  "+map.getSize());
}

This code shows the user how many of each type of object are registered into the current UnitOfWork. Since every object in this transactional cache must be checked for changes, a large cache implies a longer commit cycle.

Developers should have an idea of what the size of this cache should be. If this transaction involves the editing of 5 or 10 objects then try to insure that there are only 5 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.

7.6.5.2.2 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 can not 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.

7.6.5.2.3 Analyzing the Object-Building phase

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

Example 7-3 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. Developers should be looking for situations where they are building more objects than they 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 (for example, SoftCacheWeakIdentityMap, HardCacheWeakIdentityMap).

  • Are there some "ToMany" mapping 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.

7.6.5.2.4 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. The expression used above must be built and parsed 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 system. The following is an example of the Descriptor Amendment Method).

public static void amend(Descriptor 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; however, it can be logistically very difficult if the system has already been developed with DatabaseQuery definitions spread haphazardly through the application code. This is an aspect of application design that needs to be addressed early in the development cycle.

7.7 Oracle Application Server XML Developer's Kit Best Practices

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

7.7.1 Choosing XML Parsers

Efficient XML parsing is critical to XML applications because it determines how XML data can be accessed. Oracle XDK in Oracle Application Server 10g supports a DOM, SAX and JAXP of XML Parsers:

  • Document Object Model (DOM): a W3C standard, which represents XML data as an object tree in memory and provides object-oriented interface to access the data.

  • Simple API for XML (SAX): an event-based XML parsing standard in push mode, which represents XML data as a set of events. SAX parsers push out all the events to the registered content handlers through function callbacks.

  • Java API for XML Parsing (JAXP): the JSR-63 standard, which provides standard interfaces in Java for both SAX and DOM XML parsing

However, your particular deployment or use may not need all of them. You need to choose the XML parser according to your application requirements so that it is easy-to-use and delivers high performance.

Oracle Application Server DOM implementations employ object-based XML parsing that creates memory objects to store XML data. SAX use event-based or stream-based XML parsing, which fire off events when transversing the XML data. JAXP simply provides a standard Java interface to call either SAX or DOM.

Representing an XML document in-memory as an object tree, using DOM allows dynamic access and updates to the XML content and structure. Therefore, it is good for XML document editing and transformations, which require extensive and random document transversals.

However, object XML processing may lead to high memory costs, especially when processing large XML documents. To overcome this limitation, you can leverage the customized DOM building features using DOMParser.setNodeFactory() in XDK to minimize its performance impact. The DOMParser.setAttribute() interface also allow you to avoid including DTD object in DOM by setting the DOMParser.USE_DTD_ONLY_FOR_VALIDATION to be TRUE.

Stream-based XML parsing, such as SAX, is the performance choice when processing large XML document because of its limited memory use. Therefore, it is good for retrieving, filtering, and searching large XML documents. However, the streaming XML processing is not easy to use. Because the stream-based XML processing does not maintain the hierarchical structure of XML documents, it is not good for XML transformations or XPath content navigations. Because it does not allow XML document updates in place, it is not recommended for implementing document-editing tools.

JAXP is a standard XML parsing interface. However, it introduces extra overheads because the implement wraps around the existing DOM and SAX implementations. Additionally, JAXP applications are not portable in many cases because of the incompatibility of DOM objects across different XML parser implementations. For example, if you use JAXP based on Apache Xerces to parse XML and create DOM, you cannot use the JAXP XSLT interface implemented based on Oracle XDK to transform the DOM.

7.7.2 High-Performance XSLT Transformations

To boost the performance when transforming XML using XSLT, you can use SAX to build XSLStylesheet objects and reuse the XSLT objects for subsequent transformations.

In your XSL stylesheets, avoid unconstrained axis like "//" because the Oracle XDK XSL processor takes full DOM traversal for this kind of XPath evaluation.

Since the size of a DOM object significantly affects the memory use of XSLT transformations, you need to set <xsl:strip-space elements="*"/> to reduce. Since whitespaces do not affect the transformation result and they dramatically reduces the size of a DOM, the ultimately result is better performance.

7.7.3 Streaming XML Schema Validations

Oracle XDK provides stream-based XML Schema validation if no key or keyref is defined in the XML schemas. The following 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 Schema Validation
SAXParser parser = new SAXParser();
parser.setXMLSchema(schemadoc);
parser.setValidationMode(XMLParser.SCHEMA_VALIDATION);      
parser.parse(xml.getCharacterStream());

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

7.8 Java Message Service Best Practices

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

7.8.1 Set the Correct time_to_live Value

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.

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

While there are outstanding OJMS session blocking on a dequeue operation this might cause the granting operation to be blocked and even time-out. Granting calls should be executed before other OJMS operations.

Another way to avoid the blocking or time out is to grant roles instead of granting specific privileges to the user directly. AQ has an AQ_ADMINISTRATOR_ROLE that can be used, 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 OJMS operation.

7.8.3 Close JMS Resources No Longer Needed

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.

7.8.4 Reuse JMS Resources Whenever Possible

JMS objects like JMS connections are heavy weight and acquire database resources not unlike JDBC connection pools. Instead of creating separate JMS connections based on coding convenience, it is recommended 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 teardown overhead. It is recommended 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.

7.8.5 Use Debug Tracing to Track Down Problems

OJMS allows users to turn debug tracing by setting oracle.jms.traceLevel to values between 1 and 5 (1 captures fatal errors only and 5 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.

7.8.6 Understand Handle/Interpret JMS Thrown Exceptions

OJMS is required by the JMS specification to throw particular JMS defined exceptions when certain error/exception conditions occur. In some cases the JMS specification allows or expects OJMS to throw runtime exceptions when certain conditions occur. The JMS client program should be coded to handle these conditions gracefully.

The catch all JMS exception, JMSException, that OJMS 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. JMS programs should be coded 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 code segment below 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();}
 

7.8.7 Ensure You Can Connect to the Server and Database From the Client Computer

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.

7.8.8 Tune Your Database Based on Load

OJMS performance is greatly improved by proper database tuning. OJMS performance is dependent on AQ enqueue/dequeue performance. AQ 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.

7.8.9 OJMS

Make sure that the parameters that control the datasources underlying OJMS connections are set appropriately (see datasources best practices). Runtime exceptions that are caused when transaction/inactivity/cache-connection-availability timeouts occur or connection creation attempts fail can lead to MDBs and OJMS 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, OJMS requires that the JMS code use a URL definition to access OJMS resources within application clients. When the application is a standalone client (that is, when it runs outside of OC4J), configure the <resource-provider> element with a URL property that has the URL of the database where OJMS 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, OJMS 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 OJMS 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. So if possible make sure that the AQ queues against which OJMS 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 OJMS 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. So if it is required that the AQ queues and database tables being used in a distributed transaction be on different schemas, make sure you upgrade to database 10g.

7.8.10 OracleAS JMS Best Practices

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