Wednesday Feb 26, 2014

Customizing the Axis Labels in ADF Graphs

The various data visualization (DVT) graphs provided as part of the ADF Faces component set provide a very rich and comprehensive set of visualizations for representing your data.  One of the issues with them, that some folks struggle with however, is the fact that not all the features are controlled in an a completely declarative manner. 

In this article I want to concentrate on labeling capabilities for a graph axis, looking first of all that the declarative approaches, but then following that up with the more advanced programmatic option. 

Managing Labels Declaritively 

Control over the labels on the axis tick points is a good example of making the simple things declarative and the advanced things possible.  For basic numeric formatting you can do everything with tags - for example formatting as currency, percentage or with a certain precision.    

This is a default (bar)graph plotting employee salary against name, notice how the Y1 Axis has defaulted to a fairly sensible representation of the salary data using 0-14K:


I can change that default scaling by setting the scaling attribute in the <dvt:y1TickLabel> tag. This allows scaling at the level of none | thousand | million | billion | trillion | quadrillion (enough to show national debt then!):

<dvt:y1TickLabel id="y1TickLabel1" scaling="none"/>

Changes the graph to:


We can then further change the pattern of the numbers themselves by embedding <af:convertNumber> inside of the <dvt:y1TickLabel> tag.

e.g.

<dvt:y1TickLabel id="y1TickLabel1" scaling="none">
  <af:convertNumber type="currency" currencyCode="USD"/>
</dvt:y1TickLabel> 

Adds currency formatting:

And using the <dvt:graphFont> we can change colors and style:

<dvt:y1TickLabel id="y1TickLabel1" scaling="none">
  <dvt:graphFont name="SansSerif" size="8" color="#FF0000" bold="true" italic="true" />
  <af:convertNumber type="currency" currencyCode="USD"/>
</dvt:y1TickLabel> 

Giving:


Need More Control?  Using the TickLabelCallback...

So we can achieve quite a lot by simply using the tags.  However, what about a more complex requirement such as replacing a numerical value on an axis with a totally different string e.g. converting to a Roman Numeral (I, IV XII etc.) or maybe converting a millisecond value to a formatted date string?  To do this, ADF provides a simple callback that you can implement to map a value to whatever string you need.  Here's a simple case where I've plotted the salaries in department 100 of the standard HR EMPLOYEES demo table against the HireDate on a scatter plot.  For the sake of illustration I've actually converted the HireDate to it's time value (e.g. a long value representing the number of milliseconds since 01/01/1970) .  In a real graph I'd use the proper support that we have for representing time and date and just map a date object. Then you get to use the timeSelector and can directly set the formatting, however, bear with me because I'm just illustrating the point here.

Here's the default output with the millisecond version of the date, as you can see the millisecond value gets automatically scaled to the billions level.

To override the default representation of the millisecond value we will need to create a java class that implements the oracle.dss.graph.TickLabelCallback interface.  Here's the simple example I'll use in this case:

import java.text.SimpleDateFormat;
import oracle.dss.graph.DataTickLabelInfo;
import oracle.dss.graph.GraphConstants;
import oracle.dss.graph.TickLabelCallback;
import oracle.dss.graph.TickLabelInfo;

public class MSToDateFormatLabelCallback implements TickLabelCallback, Serializable {

  @Override
  public String getTickLabel(TickLabelInfo tickLabelInfo, int axisID) {
    String label = null;
    if (axisID == GraphConstants.X1TICKLABEL) {
      long timeInMillis = (long) ((DataTickLabelInfo) tickLabelInfo).getValue();
      SimpleDateFormat fmt = new SimpleDateFormat("MM/yy");
      label = fmt.format(timeInMillis);
    } else {
      label = "" + ((DataTickLabelInfo) tickLabelInfo).getValue();
    }
    return label;
  }
} 

As you can see the formatting  is applied only to the specified axis and we have to upcast the tickLabelInfo argument to a DataTickLabelInfo in order to gain access to the value that is being applied. Note that the callback class must also be Serializable. 

Once you have this class, then you need to apply it to the chart instance by calling the relevant set*TickLabelCallback. So for example I might set this in the backing bean for a page in the setter used by the binding attribute on the dvt graph component

public class GraphPageHandler {
    private UIGraph scatterPlot;
    public void setScatterPlot(UIGraph scatterPlot) {
        this.scatterPlot = scatterPlot;
        scatterPlot.setX1TickLabelCallback(new MSToDateFormatLabelCallback());
    } 

Now with the callback in place and the addition of:

  1. Using  the axisMinValue attribute along with axisMinAutoScaled="false" on the <dvt:x1Axis>  to reset the start date for the plot to the year 2000 rather than the default 1970
  2. Some of the currency formatting that we've already looked at to format the Y axis

Here's the result:

Monday Feb 24, 2014

Programmatic UI Scrolling in ADF Faces

I've been dealing with a use case recently where we needed to synchronize two panels in a user interface, where (in the simple version) selecting an object on one side of the screen would also select a corresponding object in a scrolling area on the other side of the screen and scroll in into view.  the scrolling panel in this case being a panelGroupLayout / scroll, not a table.

Declarative  scrolling is really simple, you can drop a UI element such as commandButton onto the page and then nest, within that, the behavior tag <af:scrollComponentIntoViewBehavior>. This tag is great if you have a long page and want to provide the users with a way to navigate to different sections, however, it needs to be wired to a command component and the user needs to physically invoke the operation.

In this case, everything needed to happen in reaction to a series of complex programmatic events and so, the use of the behavior tag did not cut it.  So I did a small amount of testing to see if it was possible to achieve the same result in a hands off fashion from within java code.

It turns out to be pretty simple to do, but there are different approaches depending on your version:

Programatic Scrolling in 11.1.2.n and 12c

In the newer versions of ADF this issue is addressed head on with a new API on the AdfFacesContext -> scrollComponentIntoView(component, focus);  This API takes a UIComponent reference for the component to scroll to, and the second argument is a boolean flag to indicate if the component should take focus as well (for example if it was an input field).  

Programatic Scrolling in 11.1.1.n

 Prior to ADF Faces 11.1.2 the handy  scrollComponentIntoView() api does not exist, however, we can acheive the same effect using some JavaScript. (Note if you are on a version of ADF that does support the scrollComponentIntoView() Java API then use that, not this.)

As with the behavior tag, you need to do two things.

  1. The target component has to have clientComponent="true" as this scrolling operation all takes place on the client
  2. You need the full id of the target component.  This should include any naming containers such as templates and regions.  The safest way to do this is to call getClientId() on the java component itself. 

 Then you can just invoke this little function.  Note that the code does not have any error handling if you get the component ID wrong so feel free to improve it to make it more robust:

import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service; 

private void scrollIntoView(String componentId){
  StringBuilder builder = new StringBuilder();
  builder.append("var c = AdfPage.PAGE.findComponent('");
  builder.append(componentId);
  builder.append("'); c.scrollIntoView(true,null);");
        
  FacesContext fctx = FacesContext.getCurrentInstance();
  ExtendedRenderKitService erks =
    Service.getRenderKitService(fctx, ExtendedRenderKitService.class);
  erks.addScript(fctx, builder.toString());        
} 

If you use this function an it appears not to work you may be hitting a timing issue where the framework is, in turn, resetting the focus after you have set it.  If that seems to be happening in your case, then the solution is to re-write the above JavaScript slightly to execute the scrollIntoView() after a short delay.  You can use a JavaScript  setTimeout() function on the window object to do this.  Delaying the scroll call for about 10ms should be sufficient.

Wednesday Dec 04, 2013

Click History Part 2 - Access from Java

In the previous article on Click History I discussed how you can use WLST to dump or export the Click History. In this follow up I wanted to share the code you can use to actually do the same thing from Java.  This is something that you might want to do from within your applications for example to provide in-app diagnostics or from a harvesting process that goes on to archive the records 

Getting the Log Records

The fundamental task here is to dump the circular memory buffer that contains the Click History.

Setup

FIrst of all, to make the correct classes available you will need to add the "JRF Client" library into the project library list  

Code

The key imports you'll need:

import oracle.core.ojdl.reader.LogRecord;
import oracle.core.ojdl.reader.ODLTextLogReader;
import oracle.dfw.dump.DumpContext;
import oracle.dfw.dump.DumpExecutionException;
import oracle.dfw.dump.DumpManager;
import oracle.dfw.dump.DumpResult;
import oracle.dfw.dump.InvalidDumpContextException;
import oracle.dfw.framework.DiagnosticsFramework;

Here's the basic code to dump the buffer:

  DumpManager dumpMgr;
  DiagnosticsFramework df;
  try {
    df = new DiagnosticsFramework();
    dumpMgr = df.getDumpManager();
    DumpContext ctx = DumpContext.createDumpContext("odl.quicktrace");
    ctx.addArgument("handlerName", "apps-clickhistory-handler");
            //now obtain a dump
    DumpResult result;
    try {
      result = dumpMgr.executeDump(ctx);
      if (result != null) {      
        String dumpFile = result.getDumpContext().getDumpPath() +  
                          File.separator + result.getDumpFiles().get(0);
        List<ClickHistoryEntryWrapper> clicks = parseClickHistory(dumpFile);
        //Now do something with clicks....
      }
      else {
        _logger.warning("Warning: excute dump did not return a result");
      }
    } catch (InvalidDumpContextException eidc) {
      _logger.severe("Error invalid Dump Context " + eidc.getMessage());
    } catch (DumpExecutionException edee) {
      _logger.severe("Dump Execution Error " + edee.getMessage());
    }       
  } catch (Exception e) {
            _logger.severe("Error getting diagnostic framework " + e.getMessage());
  } 

The parsing function which reads the dumped file:

  private List<ClickHistoryEntryWrapper> parseClickHistory(String dumpfilename){
    ODLTextLogReader log = ODLTextLogReader.create(dumpfilename, null);    
    List<ClickHistoryEntryWrapper> clicks = new ArrayList<ClickHistoryEntryWrapper>();    
    while (true){
      LogRecord entry = log.read();
      if (entry == null){
        break;
      }
      clicks.add(new ClickHistoryEntryWrapper(entry));            
    }
        
    return clicks;
  } 

And the ClickHistoryEntryWrapper class that "decodes" the LogRecord into a format that is directly usable: 

import java.util.Date;
import java.util.Map;
import oracle.core.ojdl.reader.LogRecord;
public class ClickHistoryEntryWrapper {
  private LogRecord _entry;

  public ClickHistoryEntryWrapper(LogRecord entry) {
    _entry = entry;
  }

  public String getECID() {
    return (String)_entry.getField("EXEC_CONTEXT_UNIQUE_ID");
  }
    
  public int getECIDSeq() {
    return (Integer)_entry.getField("EXEC_CONTEXT_SEQ");
  }
    
  public String getServer() {
    return (String)_entry.getField("COMPONENT_ID");
  }

  public String getDSID() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("DSID");
  }

  public String getUserId() {
    return (String)_entry.getField("USER_ID");
  }
    
  public int getThreadId() {
    return (Integer)_entry.getField("THREAD_ID");
  }

  public String getApplication() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("APP");
  }

  public String getClickComponentType() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("CLICK_COMPTYPE");
  }

  public String getClickComponentId() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("CLICK_COMPCLIENTID");
  }

  public String getClickViewId() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("CLICK_VIEWID");
  }

  public String getClickRegionViewId() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("CLICK_REGIONVIEWID");
  }

  public Date getClickTime() {
    return new Date(Long.parseLong((String)
              ((Map)_entry.getField("SUPPL_ATTRS")).get("CLICK_STARTTIME")));
  }

  public String getClickType() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("CLICK_TYPE");
  }

  public String getClickDescription() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("CLICK_COMPDISPLAYNAME");
  }

  public String getClickRegionViewName() {
    return (String) ((Map) _entry.getField("SUPPL_ATTRS")).get("CLICK_REGIONVIEWNAME");
  }
}

Tuesday Dec 03, 2013

Click History in ADF 12c

In previous posts (see here) I've covered various aspects of logging in ADF and in conjunction with a presentation delivered at the UK Oracle Users Group, I'm continuing the trend with an article on the new Click History functionality within 12c. 

So What is Click History? 

Basically the feature relates to instrumentation that is built into the ADF framework which, when switched on, continuously reports the actions that users have taken within the UI.  This mostly relates to physical clicks on UI elements such as buttons and tabs, but it also reports scrolling events, graph draws and more.  Why is it useful?  Well the point is that it can help answer that particular question - "What did you do before you got the error?" You can see how you could leverage this in a feedback or error reporting function in a UI where the information is extracted and automatically bundled with other useful information for the Support staff to have a look at. 

Each Click History record provides information about the  component, region and view that reported the event, along with key information about the application,  the user and the DSID (Diagnostic Session ID).

Here's a screen shot from some in-application diagnostics that I've developed as an small example.  In this case the history is being dumped and read into the actual application UI.  This is something I'll cover doing in a follow up article. 

Click History Data

Enabling Click History 

As I mentioned Click History is part of the framework in 12c, however, it's not switched on for every application by default.  There will be a performance overhead for any kind of tracing like this so we want to be explicit about choosing to use it. 

In your application itself you need to make 2 small changes:

  1. Create a new web.xml context parameter with the name oracle.adf.view.faces.context.ENABLE_ADF_EXECUTION_CONTEXT_PROVIDER and set the value to true.
  2. Add an extra library reference to your /WEB-INF/ weblogic.xml file (create this if you don't have one already).  Here's the reference you need:

     <library-ref>
       <library-name>odl.clickhistory.webapp</library-name>
     </library-ref> 

Assuming that you have a correctly configured Weblogic domain that has been extended with JRF this library will already be defined for you.  

So in your application, that's basically it. However, we then need to do a little work on the server to configure the server side parameters for this, specifically how much space should be devoted to the Click History buffer.  To explain, Click History has a logical circular buffer of a specified size. As the buffer fills up then new records will be written from the start again and the older records discarded. When you want to look at the history, then you have to dump this buffer, but we'll come to that later. 

Server Side Configuration of Click History 

Switching Click History On or Off

 Although individual applications enable Click History, the server also has to be told to pay attention.  This is very simple to do as it is controlled by switching a particular logger (oracle.clickhistory.EUM) to the NOTIFICATION level. This can be done from Enterprise Manager, or from the Weblogic scripting tool, WLST. Here, I'll show you how to do it from the command line using WLST, I think you can probably work out how to do it though EM yourself. 

So, with WLST running:

wlst> connect()
wlst> getLogLevel(logger='oracle.clickhistory.EUM')
INCIDENT_ERROR:1

The value of INCIDENT_ERROR indicates that the facility is OFF, so we can set it on with the following command:

wlst> setLogLevel(logger="oracle.clickhistory.EUM",
                  target="AdminServer",level="INFO", 
                  persist="1")

If you've not encountered the setLogLevel command in WLST before, just issue help('setLogLevel') from the WLST command line to view the options and what they mean. 

Viewing the Click History Buffer

Unlike a normal logging message we can't view the Click History output directly in EM, instead we need to explicitly dump the buffer to the console, or more usually a file.  As I mentioned we can also do this in code which is something I'll cover in another article. So for now, let's look at how to do that from WLST:

wlst>executeDump(name="odl.quicktrace", 
                 outputFile="/home/oracle/ch_dump.out")

Again you can issue the help() command in WLST for more information about the options on executeDump(), but in this case I'm just taking the simple option of dumping the data to a file.. Inside the file you'll see the output containing all of the lines of  Click History information like this:

[2013-11-20T09:08:53.332+00:00] [AdminServer] [NOTIFICATION] [] [oracle.clickhistory.EUM] 
[tid: 120][userId: alice] [ecid: 67ea61aa-dde8-4e35-afa2-a75a24c8a820-00001769,0] 
[CLICK_COMPTYPE: oracle.adf.RichShowDetailItem] [APP: SummitProductionDeploy] 
[DSID: 0000K9ohwuNFw000jzwkno1IYTfR00000X] [CLICK_VIEWID: /index] 
[CLICK_REGIONVIEWID: /customer-task-flow-definition/Customers] [CLICK_STARTTIME: 1384938532808] 
[CLICK_RENDERTYPE: oracle.adf.rich.Item] [CLICK_TYPE: disclosure] 
[CLICK_COMPCLIENTID: pt1:r1:0:sdi2] [CLICK_WINDOWID: w0] [CLICK_REGIONVIEWNAME: ] 
[CLICK_COMPDISPLAYNAME: Orders Dashboard] 

 It's pretty easy to parse this by eye...

Configuring the Click History Buffer Size

 You can view the initial size of the circular buffer by issuing the following WLST command:

wlst> listLogHandlers(name="apps-clickhistory-handler")

This will return a chunk of information, including the all important buffer size. (some information omitted here for space reasons):

Handler Name: apps-clickhistory-handler
type:         oracle.core.ojdl.logging.QuickTraceHandlerFactory
useLoggingContext: true
encoding:	    UTF-8
bufferSize:	    880640
…
flushOnDump:	    false

To change the buffer size from the value of 880640bytes it's another WLST command:

wlst> configureLogHandler(name="apps-clickhistory-handler",
                          propertyName="bufferSize",
                          propertyValue="1048576")

So that's the basics of Click History.  Next time I'll look at how to dump the buffer and use it in your application code directly. 

Wednesday Sep 04, 2013

Basic WebLogic Deployment Plans for your ADF Application

In my recent article Dealing with ADF_FACES-30214 I alluded to the fact that you could automate the (re)setting of web.xml parameters using a deployment plan.  This is particularly useful to ensure that all of those development-time switches are toggled off when deploying the application into a UAT or production environment.  At this stage you could head off and just research deployment plans for WebLogic in the main WebLogic server documentation.. However, I wanted to show you how easy it is to create and use a deployment plan from within JDeveloper and to embed it into a deployment profile that can be used either from the IDE or via OJDeploy  in your build system.

To help illustrate this I\ve created a simple application which echos the values of the oracle.adf.view.rich.versionString.HIDDEN and javax.faces.PROJECT_STAGE context params onto the screen using EL. This is a 12c application, however, the same approach can be taken with JDeveloper 11g as well :

So the aim here is to update those values to their production-ready settings ("true" and "Production" respectively) using a deployment plan.  For convenience, I'm going to define it as part of the project contents, although strictly speaking where you put it is up to you, you'll just need to make sure that it's not packaged up inside of your deployment  artifacts (e.g. the WAR file) as it's only needed at deploy time.

Create the file from the New Gallery: Deployment Descriptors > WebLogic Deployment Descriptor and choose plan.xml in the Create WebLogic  Deployment Descriptor wizard - Step 1, as shown here:

On the next step of the wizard you can name the new file, I've called mine dpdProductionModePlan.xml. Step through the rest of the wizard accepting the defaults. Once the wizard has run you'll find the new file in your META-INF directory.  It's not too exciting:

<?xml version = '1.0' encoding = 'US-ASCII'?>
<deployment-plan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://xmlns.oracle.com/weblogic/deployment-plan http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd"
                 xmlns="http://xmlns.oracle.com/weblogic/deployment-plan">
    <application-name>DeploymentPlan</application-name>
</deployment-plan>

The key task that we accomplish in a deployment plan, and the thing we actually need to do in this case, is value substitution and we can do that in a variety of deployment descriptors, including web.xml, the target here. 

Logically here's what happens.  The deployment plan defines one or more overrides for a particular descriptor (e.g. web.xml). It locates an element in the XML file using an XPath expression and replaces the target with the value of a named variable that is defined in the plan.

So let's start off with a variable definition, here's the variable that will be used to define the new value for PROJECT_STAGE

<variable-definition>
  <variable>
    <name>projectStageProduction</name>
    <value>Production</value>
  </variable>
</variable-definition>

So that's easy enough. Now we need to locate what descriptor file to override and the XPath expression that will identify the XML change to make.  Before starting this you will need to look at the deployment profile for your Web project (Project Properties > Deployment > Edit the profile you are using).  Grab the name of the WAR file as defined in the profile. Here's mine for my little demo app - the WAR file is called DeploymentPlanWebapp.war:

Once we have that information we can create the override section in the plan file where we locate the correct parameter in the web.xml, and substitute in the new value:

<module-override>
  <module-name>DeploymentPlanWebapp.war</module-name>
  <module-type>war</module-type>
  <module-descriptor external="false">
    <root-element>web-app</root-element>
    <uri>WEB-INF/web.xml</uri>
    <variable-assignment>
      <name>projectStageProduction</name>
      <xpath>/web-app/context-param/[param-name="javax.faces.PROJECT_STAGE"]/param-value</xpath>
      <operation>replace</operation>
    </variable-assignment>
  </module-descriptor>
</module-override>

You can see  the <variable-assignment> section and the XPath expression within that which is locating the correct context-param element from the collection of parameters based on the param-name value. The param-value is then replaced with the contents of the variable we defined before.

Once the  plan is complete we need to tell JDeveloper (or OJDeploy) to use it (Note that we can also use the same deployment plan when uploading an EAR file from the WebLogic console or using the web logic.Deployer task) .  

To set JDeveloper up, you just need to edit your Application (EAR) deployment profile (Application > Application Properties > Deployment > Edit Your Deployment Profile), then in the general section of the deployment profile property tree you can browse for and select the plan file you've just created:

Now it's simply a matter of deploying that application using the updated profile. Here I've deployed onto my standalone 12c WebLogic installation's managed server.  For completeness I've updated the plan to also replace the value for the oracle.adf.view.rich.versionString.HIDDEN value as well as the PROJECT_STAGE:

And here's the full plan file for reference:

<?xml version = '1.0' encoding = 'US-ASCII'?>
<deployment-plan
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://xmlns.oracle.com/weblogic/deployment-plan
         http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd"
   xmlns="http://xmlns.oracle.com/weblogic/deployment-plan">
  <application-name>DeploymentPlan</application-name>
  <variable-definition>
    <variable>
      <name>projectStageProduction</name>
      <value>Production</value>
    </variable>
    <variable>
      <name>devModeVersionDisplayOff</name>
      <value>true</value>
    </variable>        
  </variable-definition>
  <module-override>
    <module-name>DeploymentPlanWebapp.war</module-name>
    <module-type>war</module-type>
    <module-descriptor external="false">
      <root-element>web-app</root-element>
      <uri>WEB-INF/web.xml</uri>
      <variable-assignment>
        <name>projectStageProduction</name>
        <xpath>
          /web-app/context-param/[param-name="javax.faces.PROJECT_STAGE"]/param-value
        </xpath>
        <operation>replace</operation>
      </variable-assignment>
      <variable-assignment>
        <name>devModeVersionDisplayOff</name>
        <xpath>
          /web-app/context-param/[param-name="oracle.adf.view.rich.versionString.HIDDEN"]/param-value
        </xpath>
        <operation>replace</operation>
      </variable-assignment>
    </module-descriptor>
  </module-override>
</deployment-plan>

Of course there is more that you can do with deployment plans, now you have the basics have a read of the WebLogic documentation on the subject (12c link)

You can download the sample application that I used to illustrate this entry along with the deployment plan from the ADF EMG Samples Repository - download workspace.

Friday Aug 30, 2013

Dealing with ADF_FACES-30214

Whilst doing some work on a separate blog entry using the demo Summit application in 12c I noticed a message repeated again and again in the WebLogic log in the console. Nothing annoys me more that constantly repeating messages that obscure the stuff I really want to see (I've wrapped the message to fit here) :

 <Warning> <oracle.adfinternal.view.faces.renderkit.rich.DocumentRenderer> 
   <ADF_FACES-30214> <The setting to enable version display will be ignored 
   because the application is running in production mode.>

Switching this message off is super easy, as we'll see, but what does it relate to?

Well, this is all to do with the following context parameter defined in your web.xml file

  <context-param>
    <description>Whether the 'Generated by...' comment at the bottom of ADF Faces HTML pages should contain version number information.</description>
    <param-name>oracle.adf.view.rich.versionString.HIDDEN</param-name>
    <param-value>false</param-value>
  </context-param>

This parameter defines if the generated page output contains detailed information about the ADF version. This information is useful in development and QA evironments but is not something that you want in a production environment as it is of potential use to a Hacker. So, to see the version information we need to explicitly include this context paramter and set the value to false so as not to hide the version info.

Now in JSF 2 we have an additional factor come into play to complicate matters, which is the Project Stage (there are several articles on Project Stage that you can find with your favorite search engine, but this one covers the basics). Basically Project Stage is a way that you can define an application as being in Development, Test or Production mode and effect changes accordingly (both in the declarative sense as we have an example of here, but also in terms of code and behaviors). 

Now the problem that all this relates to, the unwanted log message, is a reflection of the fact that if the Project Stage is not explicitly defined then it's going to default to Production mode (hopefully the message begins to make sense now?) . So we kind of have two mutually exclusive settings in this app.  On one hand I'm asking for Development time version information, and on the other hand the app thinks it's in production mode. The result is this message, and, if you look at the actual source of the page in the browser you'll see this:

<!--Created by Oracle ADF (Version information hidden), 
    accessibility (mode:null, contrast:standard, size:medium), 
    skin:skyros-v1.desktop (skyros)-->

The fix?  Well I do want to see the version information so I need to tell the application that it's in Development mode, not Production mode. To do this I just have to add a Project Stage entry into web.xml:

  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>    
    <param-value>Development</param-value>
  </context-param> 

(Note the value is capitalized "Development")  

Now when the application is re-deployed, the pesky message has gone and the page source contains oodles of version information:

<!--Created by Oracle ADF (ADF RichClient API - ABRAMS (custom: 12.1.2.0.40.66.68) 
  / ADF RichClient Impl - ABRAMS (custom: 12.1.2.0.40.66.68) 
  / ADF-Controller{Oracle-BuildSystem=Linux - java - 1.7.0_10-b18, 
  Oracle-Label-JDEVADF=JDEVADF_12.1.2.0.0_GENERIC_130608.2330.6668, 
  Oracle-Version=12.1.2.0.40.66.68, Created-By=23.6-b04 (Oracle Corporation), 
  Oracle-Builder=Official Builder, Oracle-Label=JDEVADF_12.1.2.0.0_GENERIC_130608.2330.6668, 
  Oracle-BuildTimestamp=2013-06-09 02:39:02 -0700} 
  ADF-Model{Oracle-BuildSystem=Linux - java - 1.7.0_10-b18, 
  Oracle-Label-JDEVADF=JDEVADF_12.1.2.0.0_GENERIC_130608.2330.6668, 
  Oracle-Version=12.1.2.0.40.66.68, Created-By=23.6-b04 (Oracle Corporation), 
  Oracle-Builder=Official Builder, Oracle-Label=JDEVADF_12.1.2.0.0_GENERIC_130608.2330.6668, 
  Oracle-BuildTimestamp=2013-06-09 01:58:04 -0700} 
  ADF-Share{Oracle-BuildSystem=Linux - java - 1.7.0_10-b18, 
  Oracle-Label-JDEVADF=JDEVADF_12.1.2.0.0_GENERIC_130608.2330.6668, 
  Oracle-Version=12.1.2.0.40.66.68, Created-By=23.6-b04 (Oracle Corporation), 
  Oracle-Builder=Official Builder, Oracle-Label=JDEVADF_12.1.2.0.0_GENERIC_130608.2330.6668, 
  Oracle-BuildTimestamp=2013-06-09 12:47:43 -0700}  / powered by JavaServer Faces - 1.0.0.0_2-1-20), 
  accessibility (mode:null, contrast:standard, size:medium), skin:skyros-v1.desktop (skyros)--> 

Next time I show how you can use a WebLogic Deployment Plan to update the Project Stage as you deploy your application 

Tuesday Jul 30, 2013

(Adventures in) ADF Logging in 12c

An offline conversation with Andrejus Baranovskis the other day alerted me to a difference in behavior with the ADF Logger in 12.1.2. It seems that the logging output is not being output to the console as it was in 11g, although it's certainly being written to the log file OK.

I did a little testing and digging on this, and the issue it turns out is pretty simple.

The root cause is in the default definition of the console hander in the logging.xml file. In 11g, this handler is configured to let everything through level="ALL".  In 12c the default configuration is only to pass more significant issues through to the console level="WARNING:32".  The reason for this change is, I'm guessing to reduce the amount of general noise that you see when WebLogic is started. 

So it's trivial to switch back to the verboseness of 11g if you'd like to:

  1. From the console choose Actions --> Configure Oracle Diagnostic Logging to open the logging.xml view
  2. Switch to source view and locate the <log_handler name='console-handler' ... entry
  3. Change the WARNING:32 value to ALL

If you are feeling more creative, and want the best of both worlds, you can of course define a second handler for the console with this more permissive value and assign that explicitly to just your loggers - then you can get the verbose output for your logging without everything else.  

For example;

1. Define a custom handler 

<log_handler name='demo-console-handler' 
             class='oracle.core.ojdl.logging.ConsoleHandler' 
             formatter='oracle.core.ojdl.weblogic.ConsoleFormatter'
             level="ALL"/> 

2. Use the custom handler for your root logger:

<logger name="oracle.demo" level="FINEST" useParentHandlers='false'>
  <handler name='odl-handler'/>
  <handler name='wls-domain'/>
  <handler name='demo-console-handler'/>  
</logger> 

Monday Jul 22, 2013

Setting up a standalone WebLogic 12c install for ADF Without a Database

One change that many folks have encountered with the 12c (12.1.2) release of WebLogic and ADF concerns the setup of stand-alone WebLogic instances extended with JRF for ADF Application deployment. 

The main problem comes when creating the JRF extended domain.  On page 6 of the config process screen you are confronted by the following:

Configure Database Screen

Note here that you need to provide information for the database that OPSS should use. 

Now this connection has a real purpose, the Oracle Platform security infrastructure for 12c needs to store it's information within a proper database so as to correctly (and supportably) manage itself across multiple servers in a cluster.  However, if like me, you just wanted to stand up a single unclustered ADF enabled WebLogic for testing deployments etc. This could be a bit of a pain, particularly in my case as the database version I'm currently running is not of the correct version.  So here I'm documenting the steps for avoiding this requirement on a single standalone server, although I stress, as far as I am aware, this is not a supported configuration for production usage.

The Steps to Install Without Needing a Database 

  1. First of all install WebLogic (wls_121200.jar) and the ADF runtime (fmw_infra_121200.jar) into the same 12c Oracle Home using their respective installers. At the end of each of the WebLogic install make sure you uncheck the Configuration Wizard launch option as we'll be doing that manually later. 
  2. Start a shell / command prompt in $MW_HOME/wlserver/common/bin
  3. Set the environment variable QS_TEMPLATES to point to $MW_HOME/wlserver/common/templates/wls/wls_jrf.jar
  4. Run qs_config.sh and define the name of the domain that you want to create (e.g. add_domain) along with the usual WebLogic passwords and ports that you require. Finish the Quick Start wizard without starting the new domain. 
  5. Now run the conventional config.sh and on the first page choose "Update an Existing Domain" rather than "Create a new domain". The new domain (e.g. adf_domain) should now be listed for selection.
  6. On the second screen choose the templates that you wish to apply, e.g. Oracle JRF, Oracle Enterprise Manager etc.  and move on through the wizard. 
  7. On the Database Configuration Type screen this time you will see an extra option where Embedded Database (JavaDB) is offered and pre-selected. Select that and continue with the domain setup as usual with whatever managed servers you need.

Again to re-iterate this is only a setup that I would recommend for a development / testing environment and of course you should not take this approach if you're setting up any kind of clustered environment, even for testing, as the shared database has a real purpose in that case. Also consider that if your application uses MDS then you'll most likely need an database repository anyway, so again, in that instance don't use this technique.  

The fact that the normal install for the ADF Web Runtime does not offer this non-database option should be taken a a strong hint as to how supported you will be running with this configuration in a production environment.  Don't ask me for certification or support guidance, please contact Oracle Support for that information.  

Further Reading

The use of an external RDBMS Security Store for WebLogic (the configuration that  this article bypasses) is discussed in the WebLogic Documentation:

Read that chapter for a better understanding of why the default JRF install asks you to define a database connection for this purpose and why I talk about only using the technique that I've outlined here for development purposes. 

Wednesday Jun 26, 2013

Adaptive Connections For ADFBC

Some time ago I wrote an article on Adaptive Bindings showing how the pageDef for a an ADF UI does not have to be wedded to a fixed data control or collection / View Object. This article has proved pretty popular, so as a follow up I wanted to cover another "Adaptive" feature of your ADF applications, the ability to make multiple different connections from an Application Module, at runtime.
Now, I'm sure you'll be aware that if you define your application to use a data-source rather than a hard-coded JDBC connection string, then you have the ability to change the target of that data-source after deployment to point to a different database. So that's great, but the reality of that is that this single connection is effectively fixed within the application right?  Well no, this it turns out is a common misconception.

To be clear, yes a single instance of an ADF Application Module is associated with a single connection but there is nothing to stop you from creating multiple instances of the same Application Module within the application, all pointing at different connections.  If fact this has been possible for a long time using a custom extension point with code that which extends oracle.jbo.http.HttpSessionCookieFactory. This approach, however, involves writing code and no-one likes to write any more code than they need to, so, is there an easier way? Yes indeed.  It is in fact  a little publicized feature that's available in all versions of 11g, the ELEnvInfoProvider.

What Does it Do? 

The ELEnvInfoProvider  is  a pre-existing class (the full path is  oracle.jbo.client.ELEnvInfoProvider) which you can plug into your ApplicationModule configuration using the jbo.envinfoprovider property. Visuallty you can set this in the editor, or you can also set it directly in the bc4j.xcfg (see below for an example) .
Configuration Editor
Once you have plugged in this envinfoprovider, here's the fun bit, rather than defining the hard-coded name of a datasource instead you can plug in a EL expression for the connection to use.  So what's the benefit of that? Well it allows you to defer the selection of a connection until the point in time that you instantiate the AM.
To define the expression itself you'll need to do a couple of things:
  1. First of all you'll need a managed bean of some sort – e.g. a sessionScoped bean defined in your ViewController project. This will need a getter method that returns the name of the connection. Now this connection itself needs to be defined in your Application Server, and can be managed through Enterprise Manager, WLST or through MBeans. (You may need to read the documentation [http://docs.oracle.com/cd/E28280_01/web.1111/b31974/deployment_topics.htm#CHDJGBDD] here on how to configure connections at runtime if you're not familiar with this) 
  2.  The EL expression (e.g. ${connectionManager.connection} is then defined in the configuration by editing the bc4j.xcfg file (there is a hyperlink directly to this file on the configuration editing screen in the Application Module editor). You simply replace the hardcoded JDBCName value with the expression.  So your cfg file would end up looking something like this (notice the reference to the ELEnvInfoProvider that I talked about earlier)
<BC4JConfig version="11.1" xmlns="http://xmlns.oracle.com/bc4j/configuration">
  <AppModuleConfigBag ApplicationName="oracle.demo.model.TargetAppModule">
    <AppModuleConfig DeployPlatform="LOCAL" 
                     JDBCName="${connectionManager.connection}" 
                     jbo.project="oracle.demo.model.Model" 
                     name="TargetAppModuleLocal"
                     ApplicationName="oracle.demo.model.TargetAppModule">
      <AM-Pooling jbo.doconnectionpooling="true"/>
      <Database jbo.locking.mode="optimistic">
      <Security AppModuleJndiName="oracle.demo.model.TargetAppModule"/>
      <Custom jbo.envinfoprovider="oracle.jbo.client.ELEnvInfoProvider"/>
    </AppModuleConfig>
  </AppModuleConfigBag>
</BC4JConfig>

Still Don't Quite Get It?

So far you might be thinking, well that's fine but what difference does it make if the connection is resolved "just in time" rather than up front and changed as required through Enterprise Manager?
Well a trivial example would be where you have a single application deployed to your application server, but for different users you want to connect to different databases. Because, the evaluation of the connection is deferred until you first reference the AM you have a decision point that can take the user identity into account.

However, think about it for a second.  Under what circumstances does a new AM get instantiated? Well at the first reference of the AM within the application yes, but also whenever a Task Flow is entered -  if the data control scope for the Task Flow is ISOLATED.  So the reality is, that on a single screen you can embed multiple Task Flows, all of which are pointing at different database connections concurrently.

Hopefully you'll find this feature useful, let me know... 


Wednesday May 22, 2013

UKOUG ADF Mobile Demo

Yesterday I participated in a Special Interest Group meeting organised by the UK Oracle User Group on ADF Mobile. 

As part of my session I concentrated on building an ADF Mobile application from the ground up based on grabbing JSON data from Hudson. This demo shows the core techniques for dealing with URL based data and parsing the same. Several folks at the event had asked for the demo code so I've packaged and uploaded onto the ADF EMG Code Samples Repository:

Enjoy!

About

Hawaii, Yes! Duncan has been around Oracle technology way too long but occasionally has interesting things to say. He works in the Development Tools Division at Oracle, but you guessed that right? In his spare time he contributes to the Hudson CI Server Project at Eclipse
Follow DuncanMills on Twitter

Note that comments on this blog are moderated so (1) There may be a delay before it gets published (2) I reserve the right to ignore silly questions and comment spam is not tolerated - it gets deleted so don't even bother, we all have better things to do with our lives.
However, don't be put off, I want to hear what you have to say!

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today