Skip Headers
Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers
10g Release 3 (10.1.3.0)

Part Number B25947-02
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

8 Implementing Business Services with Application Modules

This chapter describes how to implement the overall functionality of your business services using the combination of application modules, view objects, and entity objects.

This chapter includes the following sections:

8.1 Introduction to Application Modules

An application module is Oracle ADF Business Component component that encapsulates the business service methods and active data model for a logical unit of work related to an end-user task.

By the end of this chapter, you'll understand all the concepts illustrated in Figure 8-1, and more:

Figure 8-1 Application Module Is a Business Service Component Encapsulating a Unit of Work

Image of how application module encapsulates a unit of work

Previous chapters explained the details of including view object instances in an application module's data model and of how client-accessible view objects cooperate internally with the entity objects in your reusable business domain layer. This chapter describes how to combine business service methods with that data model to implement a complete business service.

Note:

The examples in this chapter use the same basic SRService application module from Chapter 7, "Building an Updatable Data Model With Entity-Based View Objects", including the entity-based view objects shown in Figure 8-1. To experiment with a working version, download the DevGuideExamples workspace from Example Downloads page at http://otn.oracle.com/documentation/jdev/b25947_01 and see the ApplicationModules project.

8.2 Creating an Application Module

In a large application, you typically create one application module to support each coarse-grained end-user task. In a smaller-sized application like the SRDemo application, you may decide that creating a single application module is adequate to handle the needs of the complete set of application functionality. Section 8.9, "Deciding on the Granularity of Application Modules" provides additional guidance on this subject.

8.2.1 Creating an Application Module

To create an application module:

  1. Open Create Application Module Wizard. The wizard is available from the New Gallery in the Business Tier > ADF Business Components category.

  2. In step 1 on the Name pane, provide a package name and an application module name.

  3. In step 2 on the Data Model page, include instances of view object you have previously defined and adjust the view object instance names to be exactly what you want clients to see. Then click Finish.

For more step by step details, see Section 5.3, "Using a View Object in an Application Module's Data Model".

8.2.2 What Happens When You Create an Application Module

When you create an application module, JDeveloper creates the XML component definition file that represents its declarative settings and saves it in the directory that corresponds to the name of its package. For example, given an application module named SRService in the devguide.model package, the XML file created will be ./devguide/model/SRService.xml under the project's source path. This XML file contains the information needed at runtime to re-create the view object instances in the application module's data model. If you're curious to see its contents, you can see the XML file for the application module by selecting the view object in the Application Navigator and looking in the corresponding Sources folder in the Structure Window. Double-clicking on the SRService.xml node will open the XML in an editor so that you can inspect it.

Note:

If your IDE-level Business Components Java generation preferences so indicate, the wizard may also create an optional custom application module class SRServiceImpl.java.

8.2.3 Editing an Existing Application Module

After you've created a new application module, you can edit any of its settings by using the Application Module Editor. Choose the Edit menu option on the right-mouse context menu in the Application Navigator, or double-click on the application module, to launch the editor. By visiting the different panels of the editor, you can adjust the data model, Java generation settings, remote deployment options, client interface methods, and custom properties.

8.2.4 Configuring Your Application Module Database Connection

You configure your application module to use a database connection by identifying either a Java Database Connectivity (JDBC) URL or a JDBC Datasource name in the Connection Type section of the Configuration editor (see Figure 5-12).

8.2.4.1 Using a JDBC URL Connection Type

The default YouAppModuleLocal configuration uses a JDBC URL connection. It is based on the named connection definition set on the Business Components page of the Project Properties dialog for the project containing your application module. Figure 8-2 shows what this section would look like in a configuration using a JDBC URL connection based on the SRDemo connection name. When you use a JDBC URL connection type as the SRServiceLocalTesting configuration does in the SRDemo application, you can use the application module in any context where Java can run. In other words, it is not restricted to running inside a J2EE application server with this connection type.

Figure 8-2 Connection Type Setting in Configuration Editor

Image of connection type setting in Configuration editor

Note:

See Section 29.5.2, "Database Connection Pools" and Section 29.7, "Database Connection Pool Parameters" for more information on how database connection pools are used and how you can tune them.

8.2.4.2 Using a JDBC Datasource Connection Type

The other type of connection you can use is a JDBC datasource. You define a JDBC datasource as part of your application server configuration information, and then the application module looks up the resource at runtime using a logical name. You define the datasource connection details in two parts:

  • a logical part that uses standard J2EE deployment descriptors to define the datasource name the application will use at runtime,

  • a physical part that is application-server specific which maps the logical datasource name to the physical connection details.

For example, Example 8-1 shows the resource-ref tags in the web.xml file of the SRDemo application. These define two logical datasources named jdbc/SRDemoDS and jdbc/SRDemoCoreDS. When you use a JDBC datasource connection for your application module, you reference this logical connection name after the prefix java:comp/env. Accordingly, if you inspect the SRServiceLocal configuration for the SRService application module in the SRDemo application, you'll see that the value of its JDBC Datasource Name field is java:comp/env/jdbc/SRDemoDS.

Example 8-1 Logical Datasource Resource Names Defined in web.xml

<!-- In web.xml -->
  <resource-ref>
    <res-ref-name>jdbc/SRDemoDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>
  <resource-ref>
    <res-ref-name>jdbc/SRDemoCoreDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

Once you've defined the logical datasource names in web.xml and referenced them in a configuration, you then need to include additional, server-specific configuration files to map the logical datasource names to physical connection definitions in the target application server. Example 8-2 shows the contents of the SRDemo application's data-sources.xml file. This file is specific to the Oracle Containers for J2EE (OC4J) and defines the physical details of the datasource connection pools and connection names. You would need to provide a similar file for different J2EE application servers, and the file would have a vendor-specific format.

Example 8-2 Datasource Connection Details Defined External to Application

<data-sources ... >
    <connection-pool name="jdev-connection-pool-SRDemo">
        <connection-factory
            factory-class="oracle.jdbc.pool.OracleDataSource"
            password="->DataBase_User_8PQ34e6j3MDg3UcQD-BZktUAK-QpepGp"
            user="srdemo" url="jdbc:oracle:thin:@localhost:1521:XE"/>
    </connection-pool>
    <managed-data-source name="jdev-connection-managed-SRDemo"
                        jndi-name="jdbc/SRDemoDS" 
                        connection-pool-name="jdev-connection-pool-SRDemo"/>
    <native-data-source name="jdev-connection-native-SRDemo"
                        jndi-name="jdbc/SRDemoCoreDS" ... />
</data-sources>

The last step in the process involves mapping the physical connection details to the logical resource references for the datasource. In the OC4J server, you accomplish this step using the orion-web.xml file shown in Example 8-3.

Example 8-3 Server-Specific File Maps Logical Datasource to Physical Datasource

<orion-web-app ... >
  <resource-ref-mapping location="jdbc/SRDemoDS" name="jdbc/SRDemoDS"/>
  <resource-ref-mapping location="jdbc/SRDemoCoreDS" name="jdbc/SRDemoCoreDS"/>
</orion-web-app>

Once these datasource configuration details are in place, you can successfully use your application module in a J2EE application server as part of a web application.

8.2.5 Managing Your Application Module's Runtime Configurations

In addition to creating the application module XML component definition, JDeveloper also adds a default configuration named SRServiceLocal to the bc4j.xcfg file in the subdirectory named common relative to directory containing the SRService.xml file. To manage your application module's configurations, select it in the Application Navigator and choose Configurations... from the right-mouse context menu.

8.2.6 What You Might Need to Know About Application Module Connections

When testing your application module with the Business Component Browser, you should be aware of the connection configuration.

8.2.6.1 The Business Components Browser Requires a JDBC URL Connection

In previous chapters you've learned how valuable the Business Components Browser tool can be for testing your application module's data model interactively. Since it runs outside the context of a J2EE application server, it cannot test application modules using a configuration that depends on a JDBC datasource. The solution is simply to test the application module by selecting a configuration that uses a JDBC URL connection. You do this by choosing it from the Business Component Configuration Name dropdown list on the Connect dialog of the Business Components Browser.

8.2.6.2 Testing the SRService Application Module in the Business Components Browser

To test the SRDemo application's SRService application module using the Business Components Browser, choose the SRServiceLocalTesting configuration. Incidentally, this is the same configuration used by the JUnit tests in the UnitTests project in the workspace. The tests also run outside of a J2EE application server so for the same reason as the Business Components Browser cannot use a configuration with a JDBC datasource connection type.

8.3 Adding a Custom Service Method

An application module can expose its data model of view objects to clients without requiring any custom Java code. This allows client code to use the ApplicationModule, ViewObject, RowSet, and Row interfaces in the oracle.jbo package to work directly with any view object in the data model. However, just because you can programmatically manipulate view objects any way you want to in client code doesn't mean that doing so is always a best practice.

Whenever the programmatic code that manipulates view objects is a logical aspect of implementing your complete business service functionality, you should encapsulate the details by writing a custom method in your application module's Java class. This includes code that:

By centralizing these implementation details in your application module, you gain the following benefits:

8.3.1 How to Generate a Custom Class for an Application Module

To add a custom service method to your application module, you must first enable a custom Java class for it. If you have configured your IDE-level Business Components Java generation preferences to automatically generate an application module class, you're set. If you're not sure whether your application module has a custom Java class, as shown in Figure 8-3, check the context menu for the Go to Application Module Class option. This option lets you quickly navigate to your application module's custom class, but if you don't see the option in the menu then that means your application module is currently an XML-only component.

Figure 8-3 Quickly Navigating to an Application Module's Custom Java Class

Image of context menu in Application Navigator

To enable the class, open the Application Module Editor, visit the Java page, select the Generate Java File checkbox for an Application Module Class, then click OK to finish the wizard.

8.3.2 What Happens When You Generate a Custom Class for an Application Module

For an application module named devguide.model.SRService, the default name for its custom Java file will be SRServiceImpl.java. The file is created in the same ./devguide/model directory as the component's XML component definition file.

The Java generation options for the application module continue to be reflected on the Java page on subsequent visits to the Application Module Editor. Just as with the XML definition file, JDeveloper keeps the generated code in your custom java classes up to date with any changes you make in the editor. If later you decide you didn't require a custom Java file for any reason, unchecking the relevant option in the Java page causes the custom Java file to be removed.

8.3.3 What You May Need to Know About Default Code Generation

By default, the application module Java class will look similar to what you see in Example 8-4 when you've first enabled it. Of interest, it contains:

  • Getter methods for each view object instance in the data model

  • A main() method allowing you to debug the application module using the Business Components Browser

Example 8-4 Default Application Module Generated Code

package devguide.model;
import devguide.model.common.SRService;
import oracle.jbo.server.ApplicationModuleImpl;
import oracle.jbo.server.ViewLinkImpl;
import oracle.jbo.server.ViewObjectImpl;
// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------
public class SRServiceImpl extends ApplicationModuleImpl {
  /** This is the default constructor (do not remove) */
  public SRServiceImpl() { }
  /** Sample main for debugging Business Components code using the tester */
  public static void main(String[] args) {
    launchTester("devguide.model", /* package name */
                 "SRServiceLocal"  /* Configuration Name */);
  }
  /** Container's getter for YourViewObjectInstance1 */
  public ViewObjectImpl getYourViewObjectInstance1() {
    return (ViewObjectImpl)findViewObject("YourViewObjectInstance1");
  }

  // ... Additional ViewObjectImpl getters for each view object instance

  // ... ViewLink getters for view link instances here
}

As shown in Figure 8-4, your application module class extends the base ADF ApplicationModuleImpl class to inherit all the default behavior before adding your custom code.

Figure 8-4 Your Custom Application Module Class Extends ApplicationModuleImpl

Image of application module class extending model

8.3.4 Debugging the Application Module Using the Business Components Tester

As you learn more about the overall business service role of the application module in this chapter, you'll find it useful to exercise your application module under the JDeveloper debugger while using the Business Components Browser as the testing interface. To debug an application module using the tester, select the application module in the Application Navigator and then either:

  • In the Application Navigator, right-click the application module and choose Go to Application Module Class from the context menu.

  • Select Debug from the right-mouse context menu in the code editor

8.3.5 How to Add a Custom Service Method to an Application Module

To add a custom service method to an application module, simply navigate to application module's custom class and type in the Java code for a new method into the application module's Java implementation class. Use the following guidelines to decide on the appropriate visibility for the method. If you will use the method only inside this component's implementation as a helper method, make the method private. If you want to allow eventual subclasses of your application module to be able to invoke or override the method, make it protected. If you need clients to be able to invoke it, it must be public. See the examples of the SRServiceImpl methods in previous chapters.

Note:

The SRService application module in this chapter is using the strongly typed, custom entity object classes that you saw illustrated in the SRServiceImpl2.java example at the end of Chapter 6, "Creating a Business Domain Layer Using Entity Objects".

Example 8-5 shows a private retrieveServiceRequestById() helper method in the SRServiceImpl.java class for the SRService application module. It uses the static getDefinition() method of the ServiceRequestImpl entity object class to access its related entity definition, uses the createPrimaryKey() method on the entity object class to create an appropriate Key object to look up the service request, then used the findByPrimaryKey() method on the entity definition to find the entity row in the entity cache. It returns an instance of the strongly typed ServiceRequestImpl class, the custom Java class for the ServiceRequest entity object.

Example 8-5 Private Helper Method in Custom Application Module Class

// In devguide.model.SRServiceImpl class
/*
 * Helper method to return a ServiceRequest by Id
 */
private ServiceRequestImpl retrieveServiceRequestById(long requestId) {
  EntityDefImpl svcReqDef = ServiceRequestImpl.getDefinitionObject();
  Key svcReqKey =
    ServiceRequestImpl.createPrimaryKey(new DBSequence(requestId));
  return (ServiceRequestImpl)svcReqDef.findByPrimaryKey(getDBTransaction(),
                                                        svcReqKey);
}

Example 8-6 shows a public createProduct()method that allows the caller to pass in a name and description of a product to be created. It uses the getDefinition() method of the ProductImpl entity object class to access its related entity definition, uses the createInstance2() method to create a new ProductImpl entity row whose Name and Description attributes it populates with the parameter values passed in before committing the transaction.

Example 8-6 Public Method in Custom Application Module Class

/*
 * Create a new Product and Return its new id
 */  
public long createProduct(String name, String description) {
  EntityDefImpl productDef = ProductImpl.getDefinitionObject();
  ProductImpl newProduct = 
    (ProductImpl)productDef.createInstance2(getDBTransaction(),null);
  newProduct.setName(name);
  newProduct.setDescription(description);
  try {
    getDBTransaction().commit();
  }
  catch (JboException ex) {
    getDBTransaction().rollback();
    throw ex;
  }
  DBSequence newIdAssigned = newProduct.getProdId();
  return newIdAssigned.getSequenceNumber().longValue();
}

8.4 Publishing Custom Service Methods to Clients

When you add a public custom method to your application module class, if you want clients to be able to invoke it, you need to include the method on the application module's client interface.

8.4.1 How to Publish Custom Service Methods to Clients

To include a public method from your application module's custom Java class on the client interface, use the Client Interface page of the Application Module Editor. As shown in Figure 8-5, select one or more desired methods from the Available list and press > to shuttle them into the Selected list. Then click OK to dismiss the editor.

Figure 8-5 Adding a Public Method to an Application Module's Client Interface

Image of Client Interface dialog

8.4.2 What Happens When You Publish Custom Service Methods to Clients

When you publish custom service methods on the client interface, as illustrated in Figure 8-6, JDeveloper creates a Java interface with the same name as the application module in the common subpackage of the package in which your application module resides. For an application module named SRService in the devguide.model package, this interface will be named SRService and reside in the devguide.model.common package. The interface extends the base ApplicationModule interface in the oracle.jbo package, reflecting that a client can access all of the base functionality that your application module inherits from the ApplicationModuleImpl class.

Figure 8-6 Custom Service Interface Extends the Base ApplicationModule Interface

Image of interface extending application module interface

As shown in Example 8-7, the SRService interface includes the method signatures of all of the methods you've selected to be on the client interface of your application module.

Example 8-7 Custom Service Interface Based on Methods Selected in the Client Interface Panel

package devguide.model.common;
import oracle.jbo.ApplicationModule;
// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---------------------------------------------------------------------
public interface SRService extends ApplicationModule {
  long createProduct(String name, String description);
  String findServiceRequestStatus(long requestId);
  String findServiceRequestTechnician(long requestId);
  void updateRequestStatus(long requestId, String newStatus);
}

Each time you add or remove methods from the Selected list in the Client Interface page, the corresponding service interface file is updated automatically. JDeveloper also generates a companion client proxy class that is used when you deploy your application module for access by a remote client. For the SRService application module in this example, the client proxy file is called SRServiceClient and it is created in the devguide.model.client subpackage.

Note:

After adding new custom methods to the client interface, if your new custom methods do not appear to be available using JDeveloper's Code Insight context-sensitive statement completion when trying to use the custom interface from client code, try recompiling the generated client interface. To do this, select the application module in the Application Navigator, select the source file for the interface of the same name in the Structure window, and choose Rebuild from the context menu. Consider this tip for new custom methods added to view objects and view rows as well.

8.4.3 How to Generate Client Interfaces for View Objects and View Rows

In addition to generating a client interface for your application module, it's also possible to generate strongly typed client interfaces for working with the other key client objects that you can customize. In a manner identical to how you have learned to do for application modules, you can open the Client Interface and Client Row Interface pages of the View Object Editor to add custom methods to the view object client interface and the view row client interface, respectively.

If for the ServiceRequests view object in the devguide.model.queries package you were to enable the generation of a custom view object Java class and add one or more custom methods to the view object client interface, JDeveloper would generate the ServiceRequestsImpl class and ServiceRequests interface, as shown in Figure 8-7. As with the application module custom interface, notice that it gets generated in the common subpackage.

Figure 8-7 Custom View Object Interface Extends the Base ViewObject Interface

Image of view object interface extending another interface

Likewise, if for the same view object you were to enable the generation of a custom view row Java class and add one or more custom methods to the view row client interface, JDeveloper would generate the ServiceRequestsRowImpl class and ServiceRequestsRow interface, as shown in Figure 8-8.

Figure 8-8 Custom View Row Interface Extends the Base Row Interface

image view row interface extending the Base Row Interface

8.4.4 What You May Need to Know About Method Signatures on the Client Interface

You can include any custom method in the client interface that obeys these rules:

  • If the method has a non-void return type, the type must be serializable.

  • If the method accepts any parameters, all their types must be serializable.

  • If the method signature includes a throws clause, the exception must be an instance of JboException in the oracle.jbo package.

In other words, all the types in its method signature must implement the java.io.Serializable interface, and any checked exceptions must be JboException or its subclass. Your method can throw any unchecked exception — java.lang.RuntimeException or a subclass of it — without disqualifing the method from appearing on the application module's client interface.

Note:

If the method you've added to the application module class doesn't appear in the Available list, first check to see that it doesn't violate any of the rules above. If it seems like it should be a legal method to appear in the list, try recompiling the application module class before visiting the Application Module Editor again.

8.4.5 What You May Need to Know About Passing Information from the Data Model

The private implementation of an application module custom method can easily refer to any view object instance in the data model using the generated accessor methods. By calling the getCurrentRow() method on any view object, it can access the same current row for any view object that the client user interface sees as the current row. Due to this benefit, in some cases while writing application module business service methods there is no need to pass in parameters from the client if they would only be passing in values from the current rows of other view object instances in the same application module's data model.

For example, the createServiceRequest() method in the SRDemo application's SRService application module accepts no parameters. Internally it calls getGlobals().getCurrentRow() to access the current row of the Globals view object instance. Then it uses the strongly typed accessor methods on the row to access the values of the ProblemDescription and ProductId attributes to set them as the values of corresponding attributes in a newly-created ServiceRequest entity object row.

Example 8-8 Using View Object Accessor Methods to Access a Current Row

// In SRServiceImpl.java, createServiceRequest() method
GlobalsRowImpl globalsRow = (GlobalsRowImpl)getGlobals().getCurrentRow();
newReq.setProblemDescription(globalsRow.getProblemDescription());
newReq.setProdId(globalsRow.getProductId());

8.5 Working Programmatically with an Application Module's Client Interface

After publishing methods on your application module's client interface, you can invoke those methods from a client.

8.5.1 How to Work Programmatically with an Application Module's Client Interface

To work programmatically with an application module's client interface, do the following:

  • Cast ApplicationModule to the more specific client interface.

  • Call any method on the interface.

Note:

For simplicity, this section focuses on working only with the custom application module interface; however; the same downcasting approach works on the client to use a ViewObject interface as a view object interface like ServiceRequests or a Row interface as a custom view row interface like ServiceRequestsRow.

Example 8-9 illustrates a TestClientCustomInterface class that puts these two steps into practice. You might recognize it from Section 6.8, "Working Programmatically with Entity Objects and Associations" where you tested some example application module methods from within the main() method of the SRServiceImpl class itself. Here it's calling all of the same methods from the client using the SRService client interface.

The basic logic of the example follows these steps:

  1. Acquire the application module instance and cast it to the more specific SRService client interface.

    Note:

    If you work with your application module using the default ApplicationModule interface in the oracle.jbo package, you won't have access to your custom methods. Make sure to cast the application module instance to your more specific custom interface like the SRService interface in this example.
  2. Call findRequestStatus() to find the status of service request 101.

  3. Call findServiceRequestTechnician() to find the name of the technician assigned to service request 101.

  4. Call updateRequestStatus() to try updating the status of request 101 to the illegal value Reopened.

  5. Call createProduct() to try creating a product with a missing product name attribute value, and display the new product ID assigned to it.

Example 8-9 Using the Custom Interface of an Application Module from the Client

package devguide.client;
import devguide.model.common.SRService;
import oracle.jbo.JboException;
import oracle.jbo.client.Configuration;
public class TestClientCustomInterface {
  public static void main(String[] args) {
    String        amDef = "devguide.model.SRService";
    String        config = "SRServiceLocal";
    /*
     * This is the correct way to use application custom methods
     * from the client, by using the application module's automatically-
     * maintained custom service interface.
     */
    // 1. Acquire instance of application module, cast to client interface
    SRService service = 
      (SRService)Configuration.createRootApplicationModule(amDef,config);
    // 2. Find the status of service request 101
    String status = service.findServiceRequestStatus(101);
    System.out.println("Status of SR# 101 = " + status);
    // 3. Find the name of the technician assigned to service request 101
    String techName = service.findServiceRequestTechnician(101);
    System.out.println("Technician for SR# 101 = " + techName);
     try {
       // 4. Try updating the status of service request 101 to an illegal value
       service.updateRequestStatus(101,"Reopened");
     }
     catch (JboException ex) {
       System.out.println("ERROR: "+ex.getMessage());
     }
     long id = 0;
     try {
       // 5. Try creating a new product with a missing required attribute
       id = service.createProduct(null,"Makes Blended Fruit Drinks");
     }
     catch (JboException ex) {
       System.out.println("ERROR: "+ex.getMessage());
     }
     // 6. Try creating a new product with a missing required attribute
     id = service.createProduct("Smoothie Maker","Makes Blended Fruit Drinks");
     System.out.println("New product created successfully with id = "+id);
    Configuration.releaseRootApplicationModule(service,true);
  }
}

8.5.2 What Happens When You Work with an Application Module's Client Interface

If the client layer code using your application module is located in the same tier of the J2EE architecture, this configuration is known as using your application module in "local mode." In local mode, the client interface is implemented directly by your custom application module Java class. Typical situations that use an application module in local mode are:

  • A JavaServer Faces application, accessing the application module in the web tier

  • A JSP/Struts application, accessing the application module in the web tier

  • A Swing application, accessing the application module in the client tier (2-tier client/server style)

In contrast, when the client layer code accessing your application module is located in a different tier of the J2EE architecture, this is known as using the application module in "remote mode." In remote mode, the generated client proxy class described above implements your application module service interface on the client side and it handles all of the communications details of working with the remotely-deployed application module service. The typical situation that uses "remote mode" is a thin-client Swing application accessing the application module on a remote application server.

A unique feature of ADF Business Components is that by adhering to the best-practice interface-only approach for working with client service methods, you can be sure your client code works unchanged regardless of your chosen deployment mode. Even if you only plan to work in "local mode", it is still the best practice in the J2EE development community to adopt an interface-based approach to working with your services. Using application modules, it's extremely easy to follow this best practice in your applications.

Note:

Whether you plan to use your application modules in local deployment mode or remote mode, as described in Section 8.4.4, "What You May Need to Know About Method Signatures on the Client Interface", the JDeveloper design time enforces that your custom interface methods use Serializable types. This allows you to switch at any time between local or remote deployment mode, or to support both at the same time, with no code changes.

8.5.3 How to Access an Application Module Client Interface

The Configuration class in the oracle.jbo.client package makes it very easy to get an instance of an application module for testing. You've seen it used in numerous test client programs in this guide, and you'll see it again in the chapter on testing your application module services as part of the JUnit regression testing fixture. Because it is easy, however, it is tempting for developers to use its createRootApplicationModule() and releaseApplicationModule() methods everywhere they want to access an application module.

However, for web applications you should resist this temptation because there is an even easier way.

8.5.3.1 How to Access an Application Module Client Interface in a JSF Web Application

When working with JSF or Struts/JSP applications using the ADF Model layer for data binding, JDeveloper configures a servlet filter in your ViewController project called the ADFBindingFilter. It orchestrates the automatic acquisition and release of an appropriate application module instance based on declarative binding metadata, and insures that the service is available to be looked up as a data control. You'll learn more about the ADF BindingContext and data controls in later chapters, however here it's enough to remember that you can access the application module's client interface from this BindingContext. Since the BindingContext is available during each web page request by referencing the request-scoped attribute named data, you can reference the binding context in a JSF managed bean.

For instance, if you want to access the custom interface of the devguide.model.SRService application module, follow these basic steps shown in Example 8-10:

  1. Access the JSF FacesContext.

  2. Create value binding for the #{data} EL expression.

  3. Evaluate the value binding, casting the result to BindingContext.

  4. Find the data control by name from the BindingContext .

  5. Access the application module data provider from the data control.

  6. Cast the application module to its client interface.

  7. Call any method on the client interface.

Example 8-10 Accessing the Application Module Client Interface in a JSF Backing Bean

package demo.view;
import devguide.model.common.SRService;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCDataControl;
import oracle.jbo.ApplicationModule;
public class YourBackingBean {
  public String commandButton_action() {
    // 1. Access the FacesContext
    FacesContext fc = FacesContext.getCurrentInstance();
    // 2. Create value binding for the #{data} EL expression
    ValueBinding vb = fc.getApplication().createValueBinding("#{data}");
    // 3. Evaluate the value binding, casting the result to BindingContext
    BindingContext bc = (BindingContext)vb.getValue(fc);
    // 4. Find the data control by name from the binding context
    DCDataControl dc  = bc.findDataControl("SRServiceDataControl");
    // 5. Access the application module data provider
    ApplicationModule am = (ApplicationModule)dc.getDataProvider();
    // 6. Cast the ApplicationModule to its client interface
    SRService service    = (SRService)am;
    // 7. Call a method on the client interface
    service.doSomethingInteresting();
    return "SomeNavigationRule";
  }
}

The SRDemo application includes a JSFUtils class that encapsulates steps of evaluating an EL expression using a JSF value binding, and you can use the dot notation in an EL expression to chain method invocations together on successive beans and to look up beans in maps. So, putting these two ideas together, you can reduce the above steps to the single line like:

// Access the SRService custom interface with a single EL expression
SomeService service = (SomeService)JSFUtils.resolveExpression("#{data.SRServiceDataControl.dataProvider}");

Note:

ISection 10.3.2, "How to Change the Data Control Name Before You Begin Building Pages" explains how to rename the data control for an application module. If, as done in the SRDemo application, you use the technique described there to rename the data control for the SRService from the default name SRServiceDataControl to the shorter SRService name that matches the name of the application module itself, then the line of code above becomes the following:
// Access SRService custom interface with a single EL expression
// NOTE: SRService app module data control renamed to "SRService"
SomeService service = (SomeService)JSFUtils.resolveExpression(
                                 "#{data.SRService.dataProvider}");

8.5.3.2 How to Access an Application Module Client Interface in a JSP/Struts Web Application

If you use Struts and JSP for your view and controller layers, you can access the BindingContext and your application module custom interface from your custom PageController using code like what you see in Example 8-11. Notice that you can directly cast the result of getDataProvider() to the application module client interface without first needing to retrieve it as ApplicationModule.

Example 8-11 Accessing the Application Module Client Interface in ADF Page Controller

package demo.view;
import devguide.model.common.SRService;
import oracle.adf.controller.v2.context.LifecycleContext;
import oracle.adf.controller.v2.lifecycle.PageController;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCDataControl;
public class YourPageController extends PageController {
  public void prepareModel(LifecycleContext lcContext) {
    super.prepareModel(lcContext);
    BindingContext bc = lcContext.getBindingContext();
    DCDataControl  dc = bc.findDataControl("SRServiceDataControl");
    SRService service = (SRService)dc.getDataProvider();
    service.doSomethingInteresting();
  }
}

8.5.3.3 How to Access an Application Module Client Interface in an ADF Swing Application

If you use Swing to create desktop-fidelity applications, you can access the BindingContext and your application module custom interface from inside your Swing panels using code like what you see in Example 8-12.

Example 8-12 Accessing the Application Module Client Interface in ADF Swing Panel

package demo.view.panels;
import devguide.model.common.SRService;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCDataControl;
// etc.
public class YourPanel extends JPanel implements JUPanel {
  // etc.
  private void jButton1_actionPerformed(ActionEvent e) {
    BindingContext bc = getPanelBinding().getBindingContext();
    DCDataControl dc = bc.findDataControl("SRServiceDataControl");
    SRService service = (SRService)dc.getDataProvider();
    service.doSomethingInteresting();
  }
}

8.6 Overriding Built-in Framework Methods

The ApplicationModuleImpl base class provides a number of built-in methods that implement its functionality. While Appendix D, "Most Commonly Used ADF Business Components Methods" provides a quick reference to the most common code that you will typically write, use, and override in your custom application module classes, this section focuses on helping you understand the basic steps to override one of these built-in framework methods to augment the default behavior.

8.6.1 How to Override a Built-in Framework Method

To override a built-in framework method for an application module, go to the application module Java class and then choose Source | Override Methods... from the JDeveloper main menu. As shown in Figure 8-9, the Override Methods dialog appears. You can scroll the list of methods using the scrollbar, but if you know the method you want to override, begin typing the first few letters of its name to perform an incremental search to find it more quickly in the list.

Assume that you want to override the application module's prepareSession() method to augment the default functionality when a new user session begins working with an application module service component for the first time. Check the checkbox next to the prepareSession(oracle.jbo.Session) method, and click OK.

Note:

Here you've only selected one method, but the Override Methods dialog allows you to select any number of methods to override simultaneously.

Figure 8-9 Overriding a Built-in Framework Method

Image of Override Methods dialog

8.6.2 What Happens When You Override a Built-in Framework Method

When you dismiss the Override Methods dialog, you return to the code editor with the cursor focus in the overridden method, as shown in Figure 8-10. Notice that the method appears with a single line that calls super.prepareSession(). This is the syntax in Java for invoking the default behavior the base class would have normally performed for this method. By adding code before or after this line in the custom application module class, you can augment the default behavior before or after the default functionality.

Also notice that when you override a method in a base class, the code editor's left margin displays a small "up arrow" icon. This is a clue that this method is related to an interface or a base class. Hovering the mouse over the icon as shown in the figure gives positive feedback that this method has the correct signature to override a method in the base class.

While this method was generated by the Override Methods dialog, if you begin typing overridden method names from memory, it's important to have the signature exactly the same as the base class method you want to override. If you make a spelling mistake in what you think is an overridden method, or if you have the wrong number or types of arguments or return value, then you won't get any compile errors but neither will you be overriding the base class method you anticipated. The positive feedback makes it clear you got the method name exactly right. To quickly navigate to the base class to double-check what it's doing when you call the superclass, choose Go to Overridden Method in the right-mouse context menu menu on the override icon in the margin.

Figure 8-10 Code Editor Margin Gives Visual Feedback About Overridden Methods

Image of Code Editor margin

Note:

In addition to the design time visual method override confirmation described above, you can consider adding the JDK 5.0 @Override annotation just before any method in your class whose intent it is to override its superclass. This causes the compiler to generate a compile-time error if it does not match the signature of any method in the superclass.

8.6.3 How to Override prepareSession() to Set Up an Application Module for a New User Session

Since the prepareSession() method gets invoked by the application module when it is used for the first time by a new user session, it's a nice method to override in your custom application module class to perform setup tasks that are specific to each new user that uses your application module. Example 8-13 illustrates an overridden prepareSession() method in the devguide.model.SRServiceImpl class that invokes a findLoggedInUserByEmailInStaffList() helper method to initialize the StaffList view object instance to display the row corresponding to the currently logged-in user.

The helper method does the following:

  1. Calls super.prepareSession() to perform the default processing

  2. Accesses the StaffList view object instance using the generated getStaffList() getter method

  3. Calls the getUserPrincipalName() method to get name of the currently authenticated user

    Note:

    Until you learn about enabling J2EE security in your application in Section 30.4, "Configuring the ADF Business Components Application to Use Container-Managed Security", the getUserPrincipalName() API in the sample below will return null instead of the authenticated user's name. So, the example contains the fallback code that assigns a fixed email ID of sking for testing purposes.
  4. Defines a CurrentUser named bind variable, with the currentUserName member variable as its default value

  5. Sets an additional WHERE clause to find the current user's row by email

  6. Executes the query to retrieve the StaffList row for the current user

After overriding the prepareSession() method in this way, if you test the SRService application module using the Business Components Browser, you'll see that the StaffList view object instance has the single row corresponding to Steven King (email = 'sking').

Example 8-13 Initializing the StaffList View Object Instance to Display a Current User's Information

// In devguide.model.SRServiceImpl class
protected void prepareSession(Session session) {
  // 1. Call the superclass to perform the default processing
  super.prepareSession(session);
  findLoggedInUserByEmailInStaffList();
}
private void findLoggedInUserByEmailInStaffList() {
  // 2. Access the StaffList vo instance using the generated getter method
  ViewObject staffList = getStaffList();
  // 3. Get the name of the currently authenticated user
  String currentUserName = getUserPrincipalName();
  /*
   * Until later when we learn how to integrate J2EE security,
   * this API will return null. For testing, we can default it
   * to the email of one of the staff members like "sking".
   */
  if (currentUserName == null) {
    currentUserName = "sking";
  }
 /*
  * We can't use a simple findByKey since the key for the
  * StaffList view object is the numerical userid of the staff member
  * and we want to find the user by their email address. We could build
  * an "EMAIL = :CurrentUser" where clause directly into the view object
  * at design time, but here let's illustrate doing it dynamically to
  * see this alternative.
  */
  // 4. Define named bind variable, with currentUserName as default value
 staffList.defineNamedWhereClauseParam("CurrentUser",    // bindvar name
                                        currentUserName, // default value
                                        null);
 // 5. Set an additional WHERE clause to find the current user's row by email
 staffList.setWhereClause("EMAIL = :CurrentUser");
 // 6. Execute the query to retrieve the StaffList row for the current user
 staffList.executeQuery();
 /*
  * If the view object needs to be also used during this session 
  * without the additional where clause, you would use
  * setWhereClause(null) and removeNamedWhereClauseParam("CurrentUser") to
  * leave the view object instance back in it's original state.
  */
}

8.7 Creating an Application Module Diagram for Your Business Service

Since your business service's data model and service interface are a key asset to your team, it is often convenient to visualize it using a UML model. JDeveloper supports easily creating a diagram for your application module that you and your colleagues can use for reference.

8.7.1 How to Create an Application Module Diagram

To create a diagram of your application module:

  1. Open the Create Business Components Diagram dialog from the New Gallery in the Business Tier > ADF Business Components category.

  2. The dialog prompts you for a diagram name, and a package name in which the diagram will be created. Enter a diagram name like "SRService Data Model" and a package name like devguide.model.design.

    clicking OK creates the empty diagram and opens the diagrammer.

  3. To add your existing application module to the diagram, select them all in the Application Navigator and drop them onto the diagram surface.

  4. Use the property inspector to:

    • Hide the package name,

    • Change the font

    • Turn off the grid and page breaks

    • Turn off the display of the role names on the view links ("Master"/"Detail")

After completing these steps, the diagram looks like what you see in: Figure 8-11

Figure 8-11 UML Diagram of Application Module in a Drag and Drop and a Few Clicks

Image shows UML diagram for application module

8.7.2 What Happens When You Create an Application Module Diagram

When you create a business components diagram, JDeveloper creates an XML file representing the diagram in a subdirectory of the project's model path that matches the package name in which the diagram resides. For the Business Domain Objects diagram above, it would create a matching *.oxd_bc4j file in the ./devguide/model/design subdirectory of the model path. By default the Application Navigator unifies the display of the project contents paths so that ADF components and Java files in the source path appear in the same package tree as the UML model artefacts in the project model path. You can use the Toggle Directories toolbar icon in the Application Navigator to switch between the unified directory view and seeing the distinct project content path folders.

8.7.3 What You May Need to Know About Application Module Diagrams

You can do a number of tasks directly on the diagram, such as editing the application module, controlling display options, filtering methods names, showing related objects and files, publishing the application, and launching the Business Components Browser.

8.7.3.1 Using the Diagram for Editing the Application Module

The UML diagram of business components is not just a static picture that reflects the point in time when you dropped the application module onto the diagram. Rather, it is a UML-based rendering of the current component definitions so it will always reflect the current state of affairs. The UML diagram is both a visualization aid and a visual navigation and editing tool. You can bring up the Application Module Editor for any application module in a diagram by selecting Properties... from the right-mouse context menu (or double-clicking). You can also perform some application module editing tasks directly on the diagram like renaming view object instances, dropping view object definitions onto the data model to create a new view object instance, and removing view object instances by pressing the Delete key.

8.7.3.2 Controlling Display Options

After selecting the application module in the diagram, use the Property Inspector to control its display options. In the Display category, toggle properties like the following:

  • Show Stereotype — to display the type of object (e.g. "<<application module>>")

  • Show Operations — to display service methods

  • Show Package — to display the package name

Note:

The term operation is a more generic, UML name for methods.

In the Operations category, you will typically consider changing the following properties depending on the amount of detail you want to provide in the diagram:

  • Show Method Parameters

  • Show Return Types

  • Show Visibility (public, private, etc.)

On the right-mouse context menu, you can also select to View As:

  • Standard — to show service operations

  • Expanded — to show operations and data model (default)

  • Compact — to show only the icon and the name

8.7.3.3 Filtering Method Names

Initially, if you show the operations for the application module the diagram displays all the methods. Any method it recognizes as overridden framework methods display in the <<Framework>> operations category. The rest display in the <<Business>> methods category.

The Exclude Operations Filter property is a regular expression that you can use to filter out methods you don't want to display on the diagram. For example, by setting the Exclude Operations Filter property to

findLoggedInUser.*|retrieveService.*|get.*

you can filter out all of the following application module methods:

  • findLoggedInUserByEmailInStaff

  • retrieveServiceRequestById

  • All the generated view object getter methods

8.7.3.4 Show Related Objects and Implementation Files

After selecting the application module on the diagram — or any set of individual view object instances in its data model — you can choose Show > Related Elements from the right-mouse context menu to display related component definitions on the diagram. In a similar fashion, selecting Show > Implementation Files includes the files that implement the application module on the diagram. You can repeat these options on the additional diagram elements that appear until the diagram includes the level of detail you want to convey.

Note:

Deleting components from the diagram only removes their visual representation on the diagram surface. The components and classes remain on the file system and in the Application Navigator.

Figure 8-12 illustrates what you'll see if you include the implementation files for the devguide.model.SRService application module, showing the related elements for the SRServiceImpl class, and drawing an additional dependency line between the SRService application module and the SRServiceImpl class. Note the generated SRService client interface that you used above.

Figure 8-12 Adding Detail to a Diagram Using Show Related Elements and Show Implementation Files

Image of UML diagram

8.7.3.5 Publishing the Application Module Diagram

To publish the diagram to PNG, JPG, SVG, or compressed SVG format, choose Publish Diagram... from the right-mouse context menu on the diagram surface.

8.7.3.6 Testing the Application Module From the Diagram

To launch the Business Components Browser for an application module in the diagram, choose Test... from the right-mouse context menu.

8.8 Supporting Multipage Units of Work

During the span of time your end user is interacting with your application, she might:

This section provides a brief overview of the application module pooling and state management features that simplify implementing scalable, well-performing applications that address these requirements.

8.8.1 Overview of Application Module Pooling and State Management

Applications you build that leverage an application module as their business service take advantage of an automatic application module pooling feature. This facility manages a configurable set of application module instances that grows and shrinks as the end user load on your application changes during the day. Due to the natural "think time" inherent in the end user's interaction with your application user interface, the number of application module instances in the pool can be smaller than the overall number of active users using the system.

As shown in Figure 8-13, as a given end user visits multiple pages in your application to accomplish her logical task, on each page request an application module instance in the pool is acquired automatically from the pool for the lifetime of that one request. At the end of the request, the instance is automatically returned to the pool for use by another user session. In order to protect the end user's work against application server failure, the application module supports the ability to "dehydrate" the set of pending changes in its entity caches to a persistent store by saving an XML "snapshot" describing the change set. For scalability reasons, this state snapshot is typically saved in a state management schema that is a different database schema than the one containing the application data.

Figure 8-13 Using Pooled Application Modules Throughout a Multipage, Logical Unit of Work

Image of using pooled application modules

The pooling algorithm affords a tunable optimization whereby a certain number of application module instances will attempt to stay "sticky" to the last user session that returned them to the pool. The optimization is not an iron-clad guarantee, but when a user can benefit by the optimization they continue to work with the same application module instance from the pool as long as system load allows. When load is too high, the pooling algorithm uses any available instance in the pool to service the user's request and the dehydrated "snapshot" of their logical unit of work is rehydrated from the persistent store to allow the new instance of the application module to continue where the last one left off. The end user continues to work in this way until they commit or rollback their changes.

Using these facilities, the application module delivers the productivity of a stateful development paradigm that can easily handle multi-page work flows, in an architecture that delivers the runtime performance near that of a completely stateless application. You will learn more about these application module features in Chapter 28, "Application Module State Management" and about how to tune them in Chapter 29, "Understanding Application Module Pooling".

Note:

This application module pooling and state management is also available for thin-client, desktop-fidelity Swing applications and web-style user interfaces.

8.8.2 Experimenting with State Management in the Business Components Browser

For a quick taste of what the state management functionality does, you can launch the Business Components Browser on the devguide.model.SRService application module and try the following steps:

  1. Double-click on the OpenProblemsAndAssignees view object instance to query its data.

  2. Make a note of the current values of the Status and the Assigned To attributes for a few rows.

  3. Update those rows to have a different value for Status and Assigned To, but do not commit the changes.

  4. Choose File | Save Transaction State from the Business Components Browser main menu.

    A Passivated Transaction State dialog appears, indicating a numerical transaction ID number. Make a note of this number.

    Exit the Business Components Browser and then restart it.

  5. Exit out of the Business Components Browser completely.

  6. Restart the Business Components Browser and double-click on the OpenProblemsAndAssignees view object instance to query its data.

  7. Notice that the data is not changed. The queried data from the data reflects the current state of the database without your changes.

  8. Choose File | Restore Transaction State from the Business Components Browser main menu, and enter the transaction ID you noted in step 4.

At this point you'll see that your pending change set is reflected again in the rows you modified. If you commit the transaction now, your changes are permanently saved to the database.

8.9 Deciding on the Granularity of Application Modules

A common question from developers related to application modules is, "How big should my application module be?" In other words, "Should I build one big application module to contain the entire data model for my enterprise application, or many smaller application modules?" The answer is different for different situations. This section provides some tips about how to answer this question for your own application.

In general, application modules should be as big as necessary to support the specific use case you have in mind for them to accomplish. They can be assembled from finer-grained application module components using a "nesting" feature described below. Since a complex business application is not really a single use case, a complex business application implemented using ADF will typically not be just a single application module.

In the case of a sample like the SRDemo application, there is really only one main use case that it is implementing which allows users to manage service requests. You could argue that the application's functionality of managing technicians skills is a separate "back-end" use case, and you'd be right. However, in practice, modeling the demo that way implied having a second application module with just two view object instances in it, so the SRDemo developers took the liberty of including everything for this small-sized sample application into a single application module for simplicity's sake.

8.9.1 Use Cases Assist in Planning Your Application Modules

In the early analysis phases of application development, often architects and designers use UML use case techniques to iteratively refine a high-level description of the different kinds of end-user functionalities that the system being built will need to support.

Each high-level, end-user use case identified during this design phase typically identifies two kinds of logical outputs:

  • The domain business objects involved

    What core business data is relevant to the use case?

  • The user-oriented view of business data required

    What subset of columns, what filtered set of rows, sorted in what way, grouped in what way, is needed to support the use case?

The identified domain objects involved in the use case help you know which entity objects from your business domain layer will participate in the use case. The user-oriented view of business data required helps developers define the right SQL queries captured as view objects to retrieve the data for the end user in the exactly way that they expect. For best performance, this includes retrieving the minimum required details necessary to support the use case as well. In addition to leveraging view object queries to shape the data, you've learned how to use view links to setup natural master/detail hierarchies in your data model to match exactly the kind of end-user experience you want to offer the user to accomplish the use case.

The application module is the "workunit" container that includes instances of the reusable view objects required for the use case in question, related through metadata to the underlying entity objects in your reusable business domain layer whose information the use case is presenting or modifying.

8.9.2 Application Modules Are Designed to Support Assembly

Use cases map to modular functions. Certain higher-level functions might "reuse" a "subfunction" that is common to several business work flows. Application modules support the ability to create software components that mimic this natural modularity by creating composite application modules that you assemble using instances of other application modules. When you nest an instance of an application module inside another, you aggregate not only the view objects in its data model, but also any custom service methods it defines. While this feature of "nesting" or reusing an instance of one application module inside of another is not typically the focus of simple sample like the SRDemo application, it is one of the most powerful design aspects of the ADF Business Components layer of Oracle ADF for implementing larger-scale, real-world application systems.

Using the basic logic that an application module represents an end-user use case or work flow, you can build application modules that cater to the data required by some shared, modular use case, and then reuse those application modules inside of other more complicated application modules that are designed to support a more complex use case. For example, imagine that after creating application modules SRService and ProductMaintenaceService, you later need to build an application that uses both of these services as an integral part of a new CompositeService. Figure 8-14 illustrates what this CompositeService would look like in a JDeveloper business components diagram. Notice that an application module like CompositeService can contain a combination of view object instances and application module instances.

Figure 8-14 Application Module Instances Can Be Reused to Assemble Composite Services

Image shows application module instances reuse

If you leverage nested application modules in your application, make sure to read Section 10.3.7, "How Nested Application Modules Appear in the Data Control Palette" as well to avoid common pitfalls when performing data binding involving them.

8.9.3 Root Application Modules Versus Nested Application Module Usages

At runtime, your application works with a main — or what's known as a root — application module. Any application module can be used as a root application module, however in practice the application modules that are used as root application modules are the ones that map to a more complex end-user use case, assuming you're not just building a straightforward CRUD application. When a root application module contains other nested application modules, they all participate in the root application module's transaction and share the same database connection and a single set of entity caches. This sharing is handled for you automatically by the root application module and its Transaction object.

At runtime, each top-level application module that your application uses will get an application module pool created for it. More sophisticated customers like Oracle Applications who are building large applications with many, many business functions, often have written custom code to make more efficient use of a "generic" pool of container application modules to avoid ending up with hundreds of different application module pools. Based on the customer workflow that needs to be started, they have written code that takes a generic application module — which is basically an application module with no view object instances of its own or any nested application modules of its own defined at design time — and they programmatically use the createApplicationModule() API on this "generic" application module to dynamically nest an instance of the appropriate "use case"-based application module at runtime. When the user is done, they call remove() on that dynamically nested application module instance, leaving the generic application module "empty" again, ready to be used by another end user for whatever "use case" they need to accomplish.