Showing posts with label ADF. Show all posts
Showing posts with label ADF. Show all posts

Wednesday, February 15, 2017

FAQ #39 - Handling session expiration redirection in ADF 12c

Introduction

So how is this handled in 12c? I asked this to myself when I had to migrate an 11g ADF application to 12c. It seems that other people are asking about it too (see: https://community.oracle.com/thread/3952768, https://community.oracle.com/message/12531687#12531687, https://community.oracle.com/message/12497105#12497105 and so on) but no answers yet. The tried and tested implementation in 11g was to implement a custom filter, detect a session expiration and if that occurs redirect to a session-expired page. This is all good but... it does not work in 12c.

Main Theme

Google this

It is unfortunate for yet another instance there is practically no documentation on this at all. After googling intensly on the topic for a couple of days (talk about "agile" programming), I landed on this Oracle A-TEAM blog post, which seemed promising: http://www.ateam-oracle.com/customizing-session-time-out-pop-ups/. The post details how to customize the session expiration dialogs. Right at the top of the post there is a link with tips-on-dealing-with-the-session-time-out-popup. Eureka! As an older Greek colleague once said. So I navigated to it (http://www.ateam-oracle.com/tips-on-dealing-with-the-session-time-out-popup/) only to find out - to my dissapointment - that this was the good old 11g approach. Bammer.

Deep poetry

Well, not everything was lost. A small glimpse of hope appeared thanks to an initially seeming esoteric comment at the bottom by Deep Saurabh (Thank you Mr. Saurabh). And the comment goes something like this:

"If neither of above two is working , override NewSessionURLProvider service/provider.
( http://docs.oracle.com/middleware/1212/adf/FACAR/oracle/adf/view/rich/context/NewSessionURLProvider.html )
In the web lib’s META-INF/services folder you should have file:
oracle.adf.view.rich.context.NewSessionURLProvider
(for quick test run from jdev create META-INF/services under your ViewController/public_html/WEB-INF/classes/)

Which should contain FQCN of your custom overridden AdfcNewSessionURLProvider class."

Hmm? What did the Deep poet mean?

Enter NewSessionURLProvider

Well, other than the Deep comment above - which at the time I did not quite understand - the only other thing I had to go was the NewSessionURLProvider class. Here is the 12.2.1 class JavaDoc: https://docs.oracle.com/middleware/1221/adf/api-reference-faces/oracle/adf/view/rich/context/NewSessionURLProvider.html
The class is simple enough. It is an abstract class that needs to be overridden in the 12c ADF application by implementing the getNewSessionURL() method. As the documentation states, getNewSessionURL() is "Used to provide the view with a URL to create a new session if the current session expires while the current page is displayed.". Like I said, simple enough. That would be the first step. Override NewSessionURLProvider in your ADF application and provide an implementation for getNewSessionURL(). The code should look like this:

package com.jdeveloper.faq.services;

public class MyNewSessionURLProvider extends NewSessionURLProvider {
  @override
  public String getNewSessionURL(FacesContext fc) {
    return "pages/YourSessionExpirationPage.html";
  }
}

All you have to do is return the page that you want the application to redirect upon session expiration.

What next?

It is unclear what needs to happen next, to miraculously connect your ADF 12c application to the ADF framework and instruct it to redirect to your page. If you decypher the line "In the web lib’s META-INF/services folder you should have file: oracle.adf.view.rich.context.NewSessionURLProvider" from the comment above, it simply states what needs to be done next:

In your application's JDeveloper web ViewController project, under the src/META-INF directory create a new directory called services.In the src/META-INF/services directory create a text file (no extension) literally called oracle.adf.view.rich.context.NewSessionURLProvider. Edit the src/META-INF/services/oracle.adf.view.rich.context.NewSessionURLProvider text file and in it type the FQCN of your overridden NewSessionURLProvider class. This is what the src/META-INF/services/oracle.adf.view.rich.context.NewSessionURLProvider text file should look like:

com.jdeveloper.faq.services.MyNewSessionURLProvider

That's all?

Pretty much. The only other thing that needs to be done is to ensure that the src/META-INF/services/oracle.adf.view.rich.context.NewSessionURLProvider file in included in your deployed WAR file (during the application EAR deployment). In order to do that, edit the web ViewController deployment profile properties and under the WEB-INF/classes file group Contributors ensure that the Project Source Path is checked. Verify that the services/oracle.adf.view.rich.context.NewSessionURLProvider is included in the file list by selecting Filters under the WEB-INF/classes file group. Once you deploy your application, open the WAR and ensure that the services/oracle.adf.view.rich.context.NewSessionURLProvider text file is indeed included in the archive.

You are now ready to test it. Don't forget to remove the 11g custom filter implementation from web.xml.

Conclusion

I personally like the session expiration redirection implementation in 12c. No need to come with custom filters and all. Hopefully this will be documented in future releases of ADF.

Until the next post,(and that may be a long long time...) have fun with JDeveloping!

Thursday, December 6, 2012

Oracle JDeveloper 11gR2 Cookbook

After almost a year of truly great effort by everyone involved, I am happy to announce that my Oracle JDeveloper 11gR2 Cookbook book is published by Packt Publishing. This book accumulates to a large degree my practical experience amassed over the last four years working on real world ADF projects.

Special Thanks goes to Frank Nimphius, Edwin Biemond and Spyros Doulgeridis for their insight, knowledge and advice.  

For further details about the book click here

Here are some peer references/reviews of the book from the blog sphere:

Friday, March 9, 2012

FAQ #38 - How to add help to an ADF application

Introduction

Adding help to an ADF Fusion Web application is thoroughly explained in section 19.5 Displaying Help for Components of the Web User Interface Developer’s Guide for Oracle Application Development Framework guide. Yet, as it seems from this post https://forums.oracle.com/forums/message.jspa?messageID=10190922#10190922 in the JDeveloper and ADF forum at OTN, some of us still have questions around this subject. In this post I will attempt to clarify the subject of adding help to your ADF application by presenting pretty much the same information but in a different approach, so I suggest that before continuing with the reading this post you first take a look at the aforementioned link in the guide. Then, if you still have questions, read on.

Main Theme

Help added to an ADF Fusion Web application is categorized based on where the help contents are stored. Currently the help contents can be stored in any of the following mediums:
  • In a resource bundle
  • In a managed bean, or 
  • In an XML Localization Interchange File Format (XLIFF) XML file
So the first step would be to determine where your help contents will be stored. As mentioned, you have the choices listed above. So the first step entails having to make a design decision which one to use. What gets a little bit confusing is that each one of the three choices above allows you to display help using an external URL, which will open the help contents in a separate browser window.

So how do you add help to your ADF Fusion Web application? Simply by adding a help-provider tag to the adf-settings.xml configuration file. The adf-settings.xml file resides in the .adf/META-INF directory and it is accessible in JDeveloper in the Application Resources part of the Application Navigator via the Descriptors | ADF META-INF node. Depending on the help contents location (one of the three choices listed above) you need to provide values for the help-provider-class and property tags under the help-provider tag. Here is an example for the case where the help contents are stored in a resource bundle:

In the table below I have listed the appropriate help provider classes and property names/values depending on the location of the help contents:


You can provide external URL help in each case (resource bundle, managed bean, XLIFF) by providing your own custom help provider class. Just make sure that in each case you extend the appropriate default help provider class (shown in the table above), i.e. oracle.adf.view.rich.help.ResourceBundleHelpProvider for resource bundle based help or oracle.adf.view.rich.help.ELHelpProvider for managed bean or XLIFF based help.

For the remainder of this post, we will see how to add help utilizing the resource bundle method, which is in my opinion the most straightforward case. In this case all you have to do is to create a resource bundle, i.e. a text file with the file extension .properties and add the help contents to it. The help contents are made up of text lines that conform to the following naming convention:

help_topic_id=help_topic_data

where help_topic_id is the help topic identifier and help_topic_data is the actual help shown to the user. Here is an example:

PREFIX_TOPIC1_INSTRUCTIONS=Example for help instructions
PREFIX_TOPIC1_DEFINITION=Example for help definition
A few things to notice about the help topic identifiers:
  • For the same topic (TOPIC1 in the example above), you can define both instructions help and definition help by appending _INSTRUCTIONS and _DEFINITION to the end of the id respectively. Instructions help usually appears as you tab from one UI component to another, while definition help usually appears as a help icon in front of the UI component but this may vary depending the UI component. Again, take a look at section 19.5 Displaying Help for Components of the Web User Interface Developer’s Guide for Oracle Application Development Framework guide for more information on this.
  • Notice the prefix (PREFIX_ in the example above) used by the help topic identifiers. The prefix identifies the help provider and is specified in the help-provider tag in the adf-settings.xml configuration file.

Finally, how do you assign help topic identifiers to UI components? For this you can use the HelpTopicId property under Appearance in the Property Inspector, just don't add the _INSTRUCTIONS or _DEFINITION part of the help topic id:


The result of using PREFIX_TOPIC1 for an af:inputText UI component is shown below:

As soon as you tab into the inputText component the instructions help is shown. The definition help is shown by hovering the mouse on the question mark icon added by the framework in front of the inputText component.

One last thing: the question mark icon could actually become a button that when clicked will display help from an external URL in a separate browser window. As mentioned earlier, you do this by extending the default help provider. But again, this is explained nicely in the documentation.

Conclusion

Hopefully this post clarified any additional questions that you might have had related to adding help to your ADF Fusion Web application.

Until the next post, have fun with JDeveloping!

Thursday, March 8, 2012

FAQ #37 - How to create audit trail records using ADFBC, Pt. 2

Introduction

In this second part of creating historical audit trail records using ADFBC we will see how to integrate, in a generic way, the stored procedures introduced in part 1 of these series. For more information on the database schema and stored procedures that are required to support this technique refer to FAQ #36 - How to create audit trail records using ADFBC, Pt. 1.

Main Theme

As explained in part 1 (FAQ #36 - How to create audit trail records using ADFBC, Pt. 1), historical audit trails are generated by calling the stored procedures AUDIT_TABLE and AUDIT_DETAILS. Both of these procedures are part of the AUDIT_PKG database package.

The AUDIT_TABLE procedure is called to create a historical audit trail of the corresponding table. As explained in part 1, corresponding history tables are created for each table being audited. These history tables conform to the following naming convention: table_name_HISTORY where table_name is the table being audited.  For example the corresponding history REGIONS table is called REGIONS_HISTORY.

To create a generic implementation, we will need to create framework extension classes for both the oracle.jbo.server.EntityImpl and oracle.jbo.server.ApplicationModuleImpl classes. Once these framework extension classes are created ensure that the ADFBC project settings are changed to reflect this fact. You do this by bringing up the Project Properties dialog and selecting ADF Business Components | Base Classes as it is shown in the picture below:

The next step is to override the EntityImpl doDML() method for the EntityImpl extension framework class and add the following code to it:

Note a few things about this overridden doDML() method:

  1. We call getEntityDef().getProperty(DISABLE_AUDIT_PROPERTY) to determine whether auditing for the specific entity object has been disabled. This is controlled by the presence of an entity object property identified by the constant DISABLE_AUDIT_PROPERTY. As long as this property has not been defined in the entity object, auditing is enabled by default.
  2. We call getEntityDef().getSourceType() to determine the source type of the entity object. We will audit entity objects based on database tables only. This is done by comparing the return value of getEntityDef().getSourceType() to DBOBJ_TYPE_TABLE. DBOBJ_TYPE_TABLE is a constant defined in oracle.jbo.server.EntityDefImpl indicating that the entity object is based on a database table.
  3. The first parameter required by the AUDIT_TABLE stored procedure is the user's current session identifier. This can be retrieved by calling SessionImpl.findOrCreateSessionContextManager().getCurrentSession().getId(). Take a look at this post for information on this: Bit #39 - Retrieving the current session id at the ADFBC layer.
  4. The second parameter passed to AUDIT_TABLE stored procedure is the commit identifier. We would like to keep track changes for each commit done in the same user session. We retrieve the commit identifier from a database sequence by calling the helper getCommitId() (see source code below).
  5. We pass the DML operation as a string (INSERT, UPDATE or DELETE) based on the value of the operation parameter passed to doDML() by the ADFBC framework.
  6. We call getEntityDef().getSource() to get the name of the table associated with the entity object.
  7. Finally we call the getEntityId() helper (see code below) to get the entity object's primary key. Note that we convert the primary key to a string before passing as a parameter to the AUDIT_TABLE stored procedure.
Here is the code for the getCommitId() and getEntityId() helpers:

Notice a couple of things about these helper methods:
  1. In getEntityId() we iterate through the entity object attributes in order to determine the primary key attribute. We do this by first calling getStructureDef().getAttributeDefs() to get the attribute definitions and then by calling isPrimaryKey() for each attribute to determine if it is a primary key attribute. If it is, we call getAttribute() to retrieve and return its value. Notice that you will have to expand this implementation if you have an entity object with a composite primary key.
  2. In getCommitId() we add the commit identifier returned from the database sequence to the session user data by calling getDBTransaction().getSession().getUserData(). We do this because we will need the same commit identifier at a later stage when calling the AUDIT_DETAILS stored procedure.
As explained in part 1 of this post, to create the specific details of the audit trail we need to call AUDIT_PKG.AUDIT_DETAILS stored procedure. To do this, override the afterCommit() in the ApplicationModuleImpl framework extension class and add the following code to it:


Notice in this case how we call getSession().getUserData().get(ExtEntityImpl.COMMIT_ID) to get the same commit identifier and pass it as a parameter to the AUDIT_DETAILS stored procedure. Also, notice that we remove the commit identifier from the session user data once we are done with calling the stored procedure.

Conclusion

In this two-part series we've seen a custom implementation of creating a historical audit trail based on a stored procedure implementation in the database. Notice that this technique is fairly generic as it does not require any additional implementation in the specific entity objects used throughout the application. As mentioned above, auditing is enabled by default and can be disabled by the presence of a property defined in the entity object. This can be easily reversed so that auditing is not done by default. If you found this useful, let me know so I can email you or post the source code as well.

Until the next time, have fun JDeveloping!


Wednesday, March 7, 2012

FAQ #36 - How to create audit trail records using ADFBC, Pt. 1

Introduction

ADFBC allows for the creation of historical audit trail records at the entity object level via the use of history columns. By default the framework allows for keeping track of historical information for attributes indicated as Created On, Created By, Modified On, Modified By and Version Number. This is explained in section 4.10.12 How to Track Created and Modified Dates Using the History Column of the Fusion Developer's Guide for Oracle Application Development Framework 11g Release 2 guide. The guide also explains how to create your own custom history types in section 4.16 Creating New History Types. In this post we will detail a generic technique to create historical audit trail records using a PL/SQL stored procedure in the database. For this we will use the HR schema and demonstrate how to create an audit trail for the REGIONS and COUNTRIES tables.

Main Theme

Schema changes

Before you start on implementing an audit trail in your ADF application you need to decide on what specific information you need to create a historical trail on. This might include creating audit trail information for specific tables and/or specific columns in these tables. For the sake of this post, I will demonstrate how to create historical information for the REGIONS and COUNTRIES tables in the HR schema. For this purpose you will need to create corresponding REGIONS_HISTORY and COUNTRIES_HISTORY tables. These tables will hold the historic information. Here is a structure of the REGIONS_HISTORY table used for this implementation:

This table holds the historical data (columns REGION_ID and REGION_NAME) and information related to audit trail such as the user session that created the audit trail (SESSION_ID), the commit number within the session (COMMIT_ID), the type of audit operation, i.e. INSERT, UPDATE, DELETE (OPERATION) and a link to the HISTORY table (HISTORY_ID). The HISTORY table provides the ability to browse for the complete audit trail and its structure is shown below:

The table holds such information as the date/time of the specific audit trail, the user that has created the audit trail and the user session identifier. For each HISTORY record there are a number of history lines that detail the specific changes. This detailed information is stored in a table called HISTORY_LINE. The structure of the HISTORY_LINE table is shown below:

As you can see, this table holds audit trail information specific for each change done to the table being audited. The information is kept in this table on a per session commit basis, i.e. for each commit done in the user session. Some of the columns of interest are the following:
  • HISTORY_TABLE - The name of the table that was audited
  • FIELD_NAME - The name of the column with the HISTORY_TABLE that was audited
  • OPERATION - The operation on the column, i.e. INSERT, UPDATE, DELETE
  • VALUE_OLD - The original column data value
  • VALUE_NEW - The new column data value
Given the information in the HISTORY_LINE table we will be able to link to the corresponding history table (REGIONS_HISTORY, COUNTRIES_HISTORY, etc.) and be able to present a "picture" of what the data in the table looked like prior to the change. We can then compare it with the latest data in the corresponding table (REGIONS, COUNTRIES, etc.). Here an example of what the data in the HISTORY_LINE table might look like:

PL/SQL support

Historical audit trail information is created by calling two separate PL/SQL procedures called AUDIT_TABLE and AUDIT_DETAILS. These procedures have been implemented as part of the AUDIT_PKG in the database. AUDIT_TABLE is called to create the corresponding _HISTORY (REGIONS_HISTORY, COUNTRIES_HISTORY) table row while AUDIT_DETAILS is called to create the HISTORY and HISTORY_LINE rows. This is what the AUDIT_TABLE procedure looks like:


The code uses dynamic SQL to call the specific table audit procedures. The AUDIT_PKG package declares an array called tables_to_audit which defines the specific tables to audit. If the table is to be audited it must be added to this array. Here is its declaration for this example:

Part of this implementation requires that you provide the specific table audit procedures. These procedures are called dynamically by AUDIT_TABLE as mentioned above. The specific audit procedures must conform to the following naming format: AUDIT_table_name, where table_name is the table being audited. For the REGIONS table for instance, the procedure should be called AUDIT_REGIONS. Here is an example implementation of AUDIT_REGIONS:

The AUDIT_PKG.AUDIT_TABLE procedure will be called from the ADFBC layer to create an audit record for the specific table. The procedure is expecting the current session identifier, a commit identifier, the audit operation (INSERT, UPDATE, DELETE), the table name being audited and the table's primary key. Since the table's primary key is specified as a VARCHAR2 type, it will need to converted to this type if it is of different type. In this case also note the limitation of creating an audit trail for a table that uses a composite primary key. We will seen in part 2 of this post how this is done.

As mentioned earlier, the AUDIT_DETAILS procedure creates or locates the HISTORY record for the specific user session and uses dynamic SQL to create the individual HISTORY_LINE rows. It is shown below:


In this case you will also need to implement the specific audit details procedures to generate the HISTORY_LINE rows for the columns that were affected for each specific table. These procedures must conform to the following naming format since they are dynamically called: AUDIT_table_name_DETAILS, where table_name is the table being audited. Here is an example implementation of the AUDIT_REGIONS_DETAILS:

These procedures determine the differences between the original table (i.e. REGIONS) and the _HISTORY table (i.e. REGIONS_HISTORY) that have occurred for the specific commit session and produce the data in the HISTORY_LINE table.

The AUDIT_PKG.AUDIT_DETAILS procedure will be called from the ADFBC layer specifying the user's session identifier, the commit identifier and the user name. We will see this in the second part of this post.

Conclusion

We will conclude in the next part where we will see how to hook up this implementation to the ADFBC framework. We will do this in a generic way that will require minimal changes only to framework extension classes.

Until then, enjoy JDeveloping!

Tuesday, June 7, 2011

FAQ #35 - How to set default values for View object row attributes

Introduction

In this post we will see how to set default values to View object attributes. There are a number of places where you can do this:

  • In the overridden View object row create() method
  • Declaratively using a Groovy expression
  • In the attribute getter method

To demonstrate each case, consider the use case of setting the employee’s hire date to the current date for a newly created View object row.

Main Theme

Setting default attribute values in the overriden ViewImpl create()

To set the default value for a View object attribute in the overridden create() method, follow these steps:

  • Create a View Row custom Java implementation class for the Employees View object.
  • Open the EmployeesRowImpl.java custom View Row Java implementation class and override the create() method using the Override Methods… button – the green left arrow on the editor toolbar.
  • To set the default employee’s hire date to today’s date, add the following code to create() immediately after the call to super.create():
              // set the default hire date to today
             this.setHireDate((Date)Date.getCurrentDate());

Setting default attribute values with a Groovy expression

To set the attribute default value using a Groovy expression, follow these steps:
  • Open the Employees View object definition and go to the Attributes page.
  • Select the attribute that you want to initialize – HireDate in this case, and click on the Edit selected attribute(s) button (the pen icon).
  • On the Edit Attribute dialog select the View Attribute node.
  • Select Expression for the Value Type radio button in the Attribute section.
  • Enter the following Groovy expression in the Value field: adf.currentDate


Returning a default value from the attribute getter

Another way to set a default value for a View object attribute is in the attribute getter method. Follow these steps to set the default employee hire date:

  • Locate the View object attribute getter in the View object custom row implementation class. In this example is the getHireDate() method in EmployeesRowImpl.java.
  • Replace the existing code in getHireDate() with the following:

          // get the HireDate attribute value
          Date hireDate = (Date)getAttributeInternal(HIREDATE);
          // check for null and return today's date if needed
          return (hireDate == null) ? (Date)Date.getCurrentDate() : hireDate;

Note that using this technique we don’t actually set the attribute value; rather we return a default value, which can be subsequently applied to the attribute. Also notice that this is done only if the attribute does not already have a value (the check for null).

Actually setting an attribute value in a getter is not a recommended practice.

Attribute dependency

A common use case related to this topic is setting an attribute’s value based on the value of another related attribute. Consider for instance the use case where the employee’s commission should be set to a certain default value if the employee is part of the Sales department. Also consider the case where the employee’s commission should be cleared if the employee is not part of the Sales department. In addition to accomplishing this task with Groovy as stated earlier, it can also be implemented in the employee’s DepartmentId setter, i.e. in the setDepartmentId() method as it is shown below:

    public void setDepartmentId(Number value) {
        // set the department identifier
        setAttributeInternal(DEPARTMENTID, value);
        // set employee's commission based on employee's department
        try {
            // check for Sales department
            if (value != null && SALES_DEPARTMENT_ID == value.intValue()) {
                // if the commission has not been set yet
                if (this.getCommissionPct() == null) {
                    // set commission to default
                    this.setCommissionPct(new Number(DEFAULT_COMMISSION));
                }
            } else {
                // clear commission for non Sales department
                this.setCommissionPct(null);
            }
        } catch (SQLException e) {
            // log the exception
            LOGGER.severe(e);
        }
    }

Conclusion

As you can see, there are a number of different ways for initializing your View object row attributes. Which one you choose depends on the specific use case at hand. It is common on large projects that a combination of all (and more) is used. Setting a standard approach could be appropriate in this case.

Until the next time, keep on JDeveloping!

Monday, May 9, 2011

FAQ #34 - How to utilize WebLogic Work Managers for long-running ADF processes, Pt. 1

Introduction

The WebLogic Work Manager functionality along with its supporting Java API - CommonJ, can greatly improve performance for those long running processes in your ADF applications, especially for those notorious ones - you know, the one that cause "stuck" threads? This is true because these long running processes can now run asynchronously on a specific Work Manager that you define, one that is configured to ignore "stuck" threads. Let's take a look.


Main Theme

Adding and configuring a Work Manager in WebLogic

You define a Work Manager in WebLogic by selecting Work Managers under the Environment node in the Domain Structure window and clicking New on the Summary of Work Managers page on the right on the Global Work Managers, Request Classes and Constraints table.


On the Select Work Manager Definition type page select Work Manager and click Next.


On the Work Manager Properties page enter the name of the Work Manager and press Next.


On the Select deployment targets page select the Work Manager targets and click Finish to complete the creation of the Work Manager.


The Work Manager will now appear in the Summary of Work Managers table. Click on it to bring up the Configuration page. In it select Ignore Stuck Threads and click Save. This will configure the Work Manager to ignore "stuck" threads for long running processes. We won't be changing the other configuration settings for this Work Manager.



Configuring the Work Manager for your application

To configure the Work Manager for your application, edit the web.xml file and enter the following resource reference:

<resource-ref>
   <res-ref-name>wm/TestWorkManager</res-ref-name>
   <res-type>commonj.work.WorkManager</res-type>
   <res-auth>Container</res-auth>
   <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>


This is necessary so that you will be able to access the Work Manager programmatically as it shown below.

Programmatically submitting works to the Work Manager


Here is an example of a long running process in an ADF application and a good candidate to produce a "stuck" thread:

    public void selectAll() {
          RowSetIterator iterator = getMyViewObject().createRowSetIterator(null);

          iterator.reset();
         
          // loop over 1,000,000 records...
          while (iterator.hasNext()) {
              MyViewObject myViewObject = (MyViewObject)iterator.next();
              myViewObject.setProcessed(true);
          }
          iterator.closeRowSetIterator();
    }


This is a method in the Application Module that loops over a large number of records - possibly millions - in order to set some boolean flag. Below we will re-write the code using the Work Manager Java API CommonJ.

First you will need to add support for the CommonJ API in your ADF application. The CommonJ API is located in the weblogic.jar library, so use the Libraries and Classpath option in the Project Properties and add it. The library can be found in the wlserver_10.3/server/lib directory in your WebLogic installation directory.


Now modify the method above to the following:

        public void selectAll() {
        try {
          //get Work Manager from local JNDI
          InitialContext ctx = new InitialContext();
          WorkManager workManager = (WorkManager)ctx.lookup("java:comp/env/wm/TestWorkManager");
          //create instance of Work and WorkListener implementations
          Work work = new SelectAllWorkImpl(getMyViewObject().createRowSetIterator(null));
          WorkListener workListener=new SelectAllWorkListener();
          //create work item for execution
          WorkItem workItem = workManager.schedule(work, workListener);
          //create list of WorkItems that we want to execute
          List workItemList=new ArrayList();
          workItemList.add(workItem);
          //run the work items in parallel; don't wait
          workManager.waitForAll(workItemList, WorkManager.IMMEDIATE);
          //check results; use only if you are blocking
          //SelectAllWorkImpl workImpl= (SelectAllWorkImpl)workItem.getResult();
        } catch (NamingException e) {
        } catch (WorkException e) {
        } catch (InterruptedException e) {
        }
    }




Conclusion

In this first installment of discovering Work Managers in WebLogic, we've seen how to create one and how to configure it and use it in your application. We will wrap up this topic on the next installment. There we will explain the code above and provide the implementations of Work and WorkListener interfaces. This is indeed a promising feature that should help in certain cases where long running processes can cause "stuck" threads.

Until the next time, keep on JDeveloping!









Tuesday, March 22, 2011

FAQ #33 - How to configure ADF diagnostics logging in WLS, Pt. 3

Introduction

In this third installment of posts related to the configuration of ADF diagnostics logging we will focus on the following topics: 1) how logs could be added programmatically to the diagnostics log, 2) how new Loggers could be added and configured to the diagnostics logging configuration and 3) how to dynamically configure the WebLogic diagnostics logging. This includes changing the logging levels for the configured loggers at run-time without the need to restart the server.


Main Theme

For some background information regarding diagnostics logging as it relates to ADF, JDeveloper and WebLogic refer to these earlier posts: FAQ #6 - How to fine tune ADF diagnostics logging in JDeveloper 11g R1  , FAQ #7 - How to configure ADF diagnostics logging in standalone WLS using JDeveloper 11g R1 and FAQ #8 - How to perform log analysis using the Diagnostic Log Analyzer in JDeveloper 11g . The main advantages of using Oracle Diagnostics Logging (ODL) when compared to other logging frameworks is its tight integration with WebLogic and JDeveloper. In WebLogic, the logs produced conform to and integrate with the diagnostics logging facility. Diagnostic logs include in addition to the message logged additional information such as the session and user that produced the log entry at run-time. This is essential when analyzing the logs. In JDeveloper the log configuration and analysis is integrated via the Oracle Diagnostics Logging Configuration and Oracle Diagnostics Log Analyzer respectively. Both are discussed in detail in the above referenced posts.

Programmatic Usage

Diagnostic logs can be generated programmatically from within your code by using the ADFLogger class. You simply instantiate an ADFLogger via the static createADFLogger() method and use its log() method. log() accepts a java.util.logging.Level parameter indicating the log level of the message that you are logging and it can be any of the ADFLogger.INTERNAL_ERROR, ADFLogger.ERROR, ADFLogger.WARNING, ADFLogger.NOTIFICATION and ADFLogger.TRACE. You can also use the severe(), warning(), info(), config(), fine(), finer() and finest() methods to do your logging. Here is an example:

// create the ADFLogger
private ADFLogger logger = ADFLogger.createADFLogger("package.class");
// log a trace
logger.log(ADFLogger.TRACE, "A trace log");


Configuration of Custom Loggers in JDeveloper

In the ADFLogger example above, "package.class" is a custom logger which is associated with the ADFLogger. You will need to configure this custom logger in the diagnostics logging configuration file logging.xml. Ensure that you load the appropriate logging.xml file for the WebLogic server you are configuring. The file can be found in the domain config\fmwconfig\servers directory for each server. So, open the appropriate logging.xml configuration file in JDeveloper and create a custom logger called "package.class" by clicking on the Add Logger icon as it shown below.

In the Add Persistent Logger dialog that is presented add the logger class specified above in the ADFLogger (i.e. package.class), set its logging level and click OK.


The custom logger will appear in the list of Loggers as it shown below. With its logging level set appropriately, your trace log in the earlier example will show in the diagnostics log.



Dynamic Configuration of Logging in WebLogic

Diagnostics logging can be configured dynamically in WebLogic via the wlst tool. Ensure that you run the wlst script located in the oracle common directory  - %MIDDLEWARE_HOME%\oracle_common\common\bin for a JDeveloper installation - and not the one under the WebLogic server home - %MIDDLEWARE_HOME%\wlserver_10.3\common\bin for the stand-alone WebLogic installed along with JDeveloper. The former provides custom commands for configuring diagnostics logging. First you will need to connect to the admin server via the connect() command as in connect('weblogic','weblogic1','t3://address:7001'). To change the logging level for the custom logger class above, use the setLogLevel() command as in setLogLevel(target="ManagedServer1", logger="package.class", level="ERROR"). Specify the target WebLogic server with the target= argument (ManagedServer1 in this example), the logger class with the logger= argument and the new logging level with the level= argument. It can be either a Java level or an ODL level. Some valid Java levels are: SEVERE, WARNING, INFO, CONFIG, FINE, FINER, or FINEST. Valid ODL levels include a message type followed by a colon and a message level. The valid ODL message types are: INCIDENT_ERROR, ERROR, WARNING, NOTIFICATION, TRACE, and UNKNOWN. The message level is represented by an integer value that qualifies the message type. Possible values are from 1 (highest severity) through 32 (lowest severity). Additional commands for dynamic configuration of the diagnostics log will allow you to configure the Log Handler for each logger class, display the currently configured loggers and so on. For more details, check out the documentation references sited below.



Conclusion

If you are considering a logging framework for an application to be deployed on WebLogic, I suggest that you take a closer look at the Oracle Diagnostics Logging facility. Its tight integration with JDeveloper (logging configuration and log analysis) and WebLogic (diagnostics log format, dynamic configuration) makes it a comparable choice.

Until the next time, keep on JDeveloping!


References

Logging Custom WLST Commands
Using Custom WLST Commands









Sunday, November 7, 2010

FAQ #29 - How to deploy the ADF runtime libraries to a WebLogic cluster

Introduction

In FAQ #27 and FAQ #28 we went through a WebLogic cluster setup comprised of two WebLogic Managed Servers running on separate physical machines each. In this FAQ we will take a look at how to extend the clustered domain with the ADF runtime libraries.


Main Theme

The steps involved for extending a clustered domain are:

1. Backup the Middleware Home that contains the domain about to be extended
2. Install the ADF Runtime
3. Extend the WebLogic domain
4. Verify that the cluster is operating as expected

Note that steps 1 through 3 must be repeated for each physical machine that is part of the cluster setup.

Let's follow these steps then to extend the clustered domain that we setup in FAQ #27 and FAQ #28.

1. Backup the Middleware Home that contains the domain about to be extended

Before backing up the Middleware Home directory ensure that the domain is shutdown and that the node manager is not running on the specific machine. We will use tar and gzip to create and compress the Middleware Home archive, so change directory to your Middleware Home and type tar cvf hostname.date.middlewarehome.tar middlewareHomeDirectory, where hostname.date.middlewarehome.tar is the tar file that we will create and  middlewareHomeDirectory is the Middleware Home directory to archive, for example: tar cvf kvvm0007.20101107.middlewarehome.tar Oracle/


Once tar completes, compress the archive by typing gzip hostname.date.middlewarehome.tar




Keep the resulting tar.gz file just in case we need to restore the Middleware Home to its last configuration. 


2. Install the ADF Runtime

The ADF Runtime is distributed - among others - along with the JDeveloper installation package. So we will be using the JDeveloper installation to install it on our domain. Download the appropriate JDeveloper installation package for your operating system and start the JDeveloper installation. For installing on Linux you may refer to FAQ #26 - How to Install JDeveloper on Linux. Click Next on the Welcome screen and on the Choose Middleware Home Directory make sure that you select Use an existing Middleware Home. Verify that the Middleware Home selected is the one that contains the clustered domain to be extended.


On the Choose Products and Components page make sure that you select only the Application Development Framework Runtime - do not select JDeveloper Studio.


Click Next on the JDK Selection page. Verify that you have selected the correct Middleware Home on the Confirm Product Installation Directories page and click Next. On the Installation Summary page click Next to proceed with the installation. Ensure that only the Application Development Framework Runtime is included on the products to be installed.


The installation should proceed with no problems and complete. Uncheck the Run Quickstart and click Done to finish.


3. Extend the WebLogic domain


To extend the domain with the ADF libraries installed run the configuration script called config in wlserver_10.3/common/bin under the Middleware Home.


On the Welcome screen choose Extend an existing WebLogic domain and click Next. On the Select a WebLogic Domain Directory select the appropriate domain for the cluster and click Next.




On the Select Extension Source page select Oracle JRF - 11.1.1.0 [oracle_common] and click Next.



On the Select Optional Configuration select Deployments and Services and click Next.




On the Target Deployments to Clusters or Servers ensure that all Libraries are targeted to both the Administration Server and the Cluster while the Applications are targeted only to the Administration Server. The page should look like the one below:




On the Target Services to Clusters or Servers make sure that all services are targeted to both the cluster and the Administration Server.




On the Configuration Summary page verify your selections and press Extend to extend the domain. The domain should be extended without any errors reported.




This concludes the process of extending the domain with ADF runtime. Remember that you will have to repeat the steps above for all the machines in the cluster.


4. Verify that the cluster is operating as expected


This step should be performed once you gone through steps 1 through 3 above for all the machines on the cluster. Start the domain Administration Server and the node managers for all machines and log in into the Administration Console. Using the Domain Structure tree click on Deployments and on the Summary of Deployments page verify that all the ADF libraries are on Active State.




This being the case, you can now proceed to start the Managed Servers. Click on Environment and then on Servers on the Domain Structure tree and on the Summary of Servers click on the Control tab. In the servers table select all the Managed Servers that comprise the cluster and click on the Start button to start them. Once started all servers should be in RUNNING State. The Health of the server should be indicated as OK.




This concludes the installation of the ADF runtime to the cluster.




Conclusion

Installing the ADF runtime on a clustered WebLogic domain involves the extension of the domain on each physical server machine. The ADF runtime can be installed using the JDeveloper installation package.

Until the next time, keep on JDeveloping (on a cluster)!







Sunday, May 2, 2010

FAQ #21 - How to passivate/activate ADF Application Module class instance variables

Introduction

If you are using instance variables in your Application Module (AM) class, as part of some algorithm in your application and you expect these variables to persist from one user request to another so that the algorithm works as expected, passivation (and activation) support for these variables must be added. This FAQ will show you how to do this and at the same time build a "mini sub-framework" to avoid duplication of the basic passivation-activation code that you must write for all your AMs in your project.


Main Theme

Some Introductory Information

Application Module pooling is the process of acquiring an AM from and releasing it back to a pool. This pool of AMs contains a limited number of AMs defined in a configuration file. When all AMs have been associated with user sessions, an already-used AM must be re-used. For this to happen, the existing data associated with the AM must be saved. The passivation process saves information used by the AM into the passivation store -  usually a database, while the activation process does exactly the opposite, restores the state of the  AM from the passivation store. The information is saved as an XML document. Take a look at FAQ #14 - How to test an Application Module for activation-safe in ADF 11g for additional information regarding the passivation and activation processes.

You can add custom passivation and activation logic in your AM class by overriding the ApplicationModuleImpl methods passivateState() and activateState() respectively. The passivateState() method should create the necessary XML elements for each AM instance variable that you need to passivate. The activateState()  method should detect the specific XML elements that identify the instance variables in the passivated XML document and restore them back into the instance variables.


A "mini sub-framework" for passivating/activating Application Module instance variables

If you are using multiple AMs, you can avoid code duplication of this basic implementation of the passivateState() and activateState() methods by putting it in a base AM class. Then derive all of your other AMs from this base AM. The base AM class can then provide activation/passivation "triggers" to which the derived AMs can respond. The implementation of passivateState()  in the base AM should look like this:




As you can see, after calling super.passivateState() to allow the framework to do its own processing, we call onStartPassivation() to signal derived AMs that a passivation process is taking place. Derived AMs should override onStartPassivation() in order to provide a list of the identifiers - each identifier tied to a specific instance variable - to be passivated. Below is an example implementation of onStartPassivation() by a derived AM class. 


In this case a single passivation identifier call EMPLOYEE_LIST_ID is returned to indicate a list of Employees class instance variable.

passivateState() then loops for all passivation identifiers, creates an XML node for each identifier in the XML passivation document and calls onPassivate() passing the passivation identifier as an argument to it. A  derived AM will override this method in order to return the passivation data for the specific passivation identifier. An example implementation is shown below:




In this case the passivation identifier is checked and if it matches the expected identifier, the passivation data is supplied. 

Finally, passivateState() calls onEndPassivation()  to indicate the end of the passivation process.

The reverse process of activation is similar. 
activateState() calls onStartActivation() to get the activation identifiers. It then loops looking for each activation identifier in the activated XML document. If the identifier is found, calls onActivate() supplying the activation data along with its activation identifier as arguments. The derived AM can identify the instance variable being activated - based on the activation identifier, and restore it.

To test the framework, bring up the Edit Business Components Configuration dialog and  disable Application Module Pooling as it shown below. You do this by right-clicking on the AM in the Application Navigator and selecting Configurations... Then you click on Edit... to edit the specific configuration.




 This will in effect initiate a passivation-activation cycle for each request when the application is running. Finally save the configuration, enable debug logging on the console and run the application. You should see your AM class instance variables being activated.





To further explore this framework, the source code is supplied below.

Conclusion

If you are using class instance variables in the BC layer and you want their values to persit from one request to another, then passivating-activating these variables becomes necessary. The ADF BC framework allows you to do this by overriding the passivateState() and activateState() methods.


Until the next time, keep on JDeveloping!

Code

http://jdeveloperfaq.googlecode.com/files/JDeveloperFAQNo21.rar








Related Posts Plugin for WordPress, Blogger...