ADF Related Useful Code Snippets
ADF Related Useful Code Snippets
Oracle ADF Code Corner OTN Harvest is a monthly blog series that publishes how-to tips and information around Oracle JDeveloper and Oracle ADF. Disclaimer: ADF Code Corner OTN Harvest is a blogging effort according to the Oracle blogging policies. It is not an official Oracle publication. All samples and code snippets are provided "as is" with no guarantee for future upgrades or error correction. No support can be given through Oracle customer support. If you have questions, please post them to the Oracle OTN JDeveloper forum: http://forums.oracle.com/forums/forum.jspa?forumID=83
Table of Contents
OTN Harvest ...................................................................................... 4 Oracle JDeveloper 10.1.3 documents are back on OTN ................. 4 How-to get help for OAF framework ............................................... 4 Oracle Fusion Reference Architecture (OFRA) ............................... 4 Dynamically Adding a where-clause to ADF BC ............................. 5 How-to add ADF binding based auto suggest behavior .................. 5 How-to access a hidden attribute from a table row ......................... 6 How-to add JavaScript code to an ADF Faces pages..................... 6 How-to launch a popup programmatically from Java ...................... 6 Best option for accessing an attribute value of an iterator binding .. 7 Strange component refresh behavior using ADF auto-ppr .............. 8 How-to keep selected row after rollback ......................................... 8 How-to programmatically queue a selection event........................ 10 How to configure a project to use Trinidad components ............... 11 How-to enable the commit button on ADF BC based input form ... 11 PartialTriggers on command item confusion ................................. 11 Why isn't my custom skin showing for the login pages? ............... 12 How-to hide the slider bar of the af:carousel component .............. 13 How-to filter tables case insensitive .............................................. 13 ADF Faces page doesn't open in inline frame .............................. 13
OTN Harvest
Below is a summary or call it a premium selection of OTN topics posted to the OTN forum this month. The answers provided in this document may not be the only solution possible to a problem; however, they give you some ideas and code snippets to play with.
As a best practice, no matter which approach you choose, make sure bind variables are used to define the dynamic part of the where clause condition instead of re-constructing and re-applying the whole where clause for each new condition. This reason for this recommendation is performance as the database does not need to parse the query string again for each where clause condition.
Note: To add JavaScript to ADF Faces page fragments exposed by an ADF region, you use the trh:script tag of the Apache MyFaces Trinidad component set. Because page fragments dont have an af:document tag, they cannot use the af:resource tag.
The popup hints can be used to set the aligned, the component the popup is opened next to, and the alignment type. To close a popup, just call
The recommendation as you would expect depends on what information else you need to get access to. For example, if access is only for a single attribute value and if optionally this also needs to be EL accessible (for example to reference from a task flow input parameter or method binding) then creating an attribute binding is the better solution to use. Still however, I would recommend avoiding the use of EL in managed bean Java code because of the unnecessary overhead and assumption it makes about the binding layer structure. So code like shown below will do
BindingContainer vBindingContainer = BindingContext.getCurrent().getCurrentBindingsEntry(); ControlBinding vControlBinding = vBindingContainer.getControlBinding("attributeBindingName"); AttributeBinding vAttributeBinding = (AttributeBinding)vControlBinding; Object value = vAttributeBinding.getInputValue();
If however the use case is to access multiple attribute values, then reading the iterator directly seems to be a better solution as it only requires a single binding layer access.
BindingContainer vBindingContainer = BindingContext.getCurrent().getCurrentBindingsEntry(); DCBindingContainer vDCBindingContainer = (DCBindingContainer)vBindingContainer; DCIteratorBinding vDCIteratorBinding = vDCBindingContainer.findIteratorBinding("theIterator"); Row vRow = vDCIteratorBinding.getCurrentRow(); Object attr1Value1 = vRow.getAttribute("attrName1"); Object attr1Value2 = vRow.getAttribute("attrName2");
Option 2 and 3 are the solutions to recommend to developers who want to access the ADF binding layer from Java. Note: if you use ChangeEventPolicy with a value of "ppr" to refresh ADF Faces components in response to data changes on the binding layer, please read on.
txt1 bound to managed bean method1 that accesses an ADF attribute binding using EL in Java to return the value of an attribute txt2 bound to managed bean method2 that accesses an ADF attribute binding from Java to return the value of an attribute txt3 bound to managed bean method3 that accesses an iterator in ADF to read from the current row's attribute
The iterator has its changeEventPolicy set to PPR (to enable auto-ppr). However, navigating the data with command buttons bound to ADF binding operations ("partialSubmit" set to true) refresh only txt1 and txt2 - not txt3. The reason for this behavior is to explain with the way the auto-ppr functionality works. When the change event policy is set to "ppr" on the ADF binding, then all changes in the binding layer like row currency changes are reported to the client component by adding the component reference to the list of partial targets to refresh in response to a request, which is pressing the navigation button. Iterators are bound to UI components through control bindings, like attribute, list or tree bindings. Any component that directly or indirectly through a managed bean method referenced from the component value property accesses a control binding is added to the list of partial targets to refresh. Components that access a managed bean method that accesses the iterator binding directly are not added to this list and therefore not refreshed. A component that binds to the ADF iterator through a managed bean method bypasses the ADF Faces binding layer. This means that registration with the active model (auto-ppr) does not happen. If you need a component to refresh though it accesses the iterator binding directly through a managed bean method, then set up its PartialTriggers property to point to the component that starts the change (the navigation button in the example). As a rule of thumb: auto-ppr refresh with ADF works for components that directly or indirectly reference a control binding in the "Bindings" section of the PageDef file. Note: A thank you goes to John Stegeman and Jan Vervecken for their contributions to OTN thread http://forums.oracle.com/forums/thread.jspa?messageID=7423464
Drag the Rollback operation from the Data Controls panel and drop it as a button The "Rollback" button has its "disabled" property pointing to the action binding in the PageDef file. Either remove this reference or ensure the rollback button is refreshed when the submit button is pressed.
Double click onto the "Rollback" button, which opens a dialog for you to create a managed bean and a method in this bean. Optional the action method can be created in an existing bean. The action method in the bean gets created as shown below, where "onRollback" is the name for the action that we chose for the new method
public String onRollback() { BindingContainer bindings = getBindings(); OperationBinding operationBinding = bindings.getOperationBinding("Rollback"); Object result = operationBinding.execute(); if (!operationBinding.getErrors().isEmpty()) { return null; } return null; }
The behavior of the method is the same as before. The difference is that instead of EL, which is used after the drag and drop of the Rollback operation, Java is used to perform the rollback action.
Change the generated method above as shown below to read the row key for a master and detail relation
public String onRollback() { BindingContainer bindings = getBindings(); //get rowKey for parent and child DCIteratorBinding parentIter = (DCIteratorBinding)bindings.get("DepartmentsView1Iterator"); DCIteratorBinding childIter = (DCIteratorBinding) bindings.get("EmployeesView3Iterator"); Key parentKey = parentIter.getCurrentRow().getKey(); Key childKey = childIter.getCurrentRow().getKey(); // perform rollback operation OperationBinding operationBinding = bindings.getOperationBinding("Rollback"); Object result = operationBinding.execute(); if (!operationBinding.getErrors().isEmpty()) { return null; } //user row key back to preserve row selection parentIter.setCurrentRowWithKey(parentKey.toStringFormat(true)); childIter.setCurrentRowWithKey(childKey.toStringFormat(true)); return null; }
Note:
In JDeveloper 11.1.1.3 chances are that the Rollback action binding is deleted when you create the managed bean reference. In this case the rollback action needs to be added manually in
Below is a sample code, assuming the row delete operation is performed using a button created by dragging the "Delete" operation of a View Object from the Data Control panel. To create the managed bean method to do the same in Java, just double click on the button and then follow the dialog that opens. The action method code below highlights the generated code lines in bold.
public String onDelete() { //we deleted the column, so the old value doesn't exist anymore RowKeySet oldSet = new RowKeySetImpl(); //create a new set RowKeySet newSet = new RowKeySetImpl(); //delete row the ADF way BindingContainer bindings = getBindings(); OperationBinding operationBinding = bindings.getOperationBinding("Delete"); Object result = operationBinding.execute(); if (!operationBinding.getErrors().isEmpty()) { return null; } //get a reference to the table component either through a JSF //component binding or by looking up the component in the UI //view root. From the table you get a generic access to the //DCIteratorBinding CollectionModel model = (CollectionModel) table1.getValue(); JUCtrlHierBinding tableBinding = (JUCtrlHierBinding) model.getWrappedData(); DCIteratorBinding dciter = tableBinding.getDCIteratorBinding(); //get the current row key
10
Note: This of course also works for other events, like ValueChange etc.
Create a new "Generic Project" and choose JSF as the Technology Scope. Don't use any ADF Faces or PageFlow scope. Create a JSPX page which is the easiest option to open the component palette, which gives you a quick access to configuring the Trinidad tag libraries Select the Component Palette and right mouse click into it Choose "Edit Tag Libraries" and select the Trinidad components. Move them to the "Selected Libraries" section and Ok the dialog. The first time you drag a Trinidad component to a page, the web.xml file is updated with the required filters
11
What's confusing with this is that you would expect the table to have a PartialTriggers property entry pointing to the command button. Instead the PartialTriggers property of the button points to the table. Why is this? 1. The command button is set to partialSubmit="false" by default. This means that pressing the command button performs a full page refresh, which also leads to the table getting refreshed. Only if the partialSubmit property of the command item is set to "true" you need to set the PartialTriggers property on the table to refresh when the button is pressed The command button has the "disabled" property pointing to the ADF binding layer to determine whether or not it is enabled: #{!bindings.CreateInsert.enabled}. Chances are that an operation on the table changes the button state, which is why the button has its PartialTriggers property pointing to the table. Keep in mind that table actions are performed by partial submits issued by the component itself.
2.
So the confusion actually comes from the IDE being smart, doing the right thing without telling. In general, of course, PartialTriggers only need to be set if a component requires refresh in response to a partial submit performed on another component.
The above constraint in web.xml protects all resources not only pages of an application, making it accessible for authenticated users only. If such an application uses form based authentication, in which the form is contained in a JSF page, then this page comes without any style applied to it and most likely without images too because of the root level protection. To avoid this problem, either use ADF Security based authorization, store page content in a sub folder under the public_html directory to the use /pages/* as the authorization pattern or use an explicit mention of the file extension to protect (e.g. *.jspx). However note that if the page is served as a controller reference, then it is accessed by its viewActivity name, which does not use any file extension at all. Applying web based security to JSF applications, which implies ADF Faces and MyFaces Trinidad, is
12
13
Valid values are always: The page will show an error and redirect whenever it attempts to run in a frame. differentDomain: The page will show an error and redirect only when it attempts to run in a frame on a page that originates in a different domain (the default). never: The page can run in any frame on any originating domain. (the default)
See: http://download.oracle.com/docs/cd/E15523_01/web.1111/b31973/ap_config.htm#BABDHGEJ
If you want avoid table execution on the initial page rendering, then you can check for a memory attribute, for example ${viewScope.refreshTable == true? false : true}. This returns false if the attribute is not set or set to false, in which case the iterator is not refreshed and the table is empty. Using a command item action, or an af:setPropertyListener, the memory attribute can be set to true. The next time the table refreshes, the data then is shown.
<af:commandButton text="Submit" id="cb1" partialSubmit="true"> <af:setPropertyListener from="#{true}" to="#{viewScope.refreshTable}" type="action"/> </af:commandButton>
14
Note that using the viewScope memory scope, which is an ADF Controller specific scope, ensures the memory attribute is cleaned up when users navigate off the page. It's all about scope: So chances are that storing the attribute in a viewScope does not 100% meet your needs, in which case you may want to reach out to pageFlowScope or sessionScope. Another use case is in which you don't want the table to refresh at all until you said so. In this case, set the iterator refresh option to "never". This then however requires you to refresh the table from Java in a managed bean.
String iterator = "myIteratorName"; BindingContext bctx = BindingContext.getCurrent(); BindingContainer bindings = bctx.getCurrentBindingsEntry(); DCIteratorBinding dciter = (DCIteratorBinding)bindings.get(iterator); dciter.executeQuery(); dciter.refresh(DCIteratorBinding.RANGESIZE_UNLIMITED);
Note: If JPA is used, you need to re-execute the finder method exposed in the binding
15
2.
3.
4.
5.
To ensure ADF Security works well with this configuration, configure the custom servlet to the JpsFilter. Note that the JpsFilter mapping must come before the adfBinding filter mapping shown next in step 6.
<filter-mapping> <filter-name>JpsFilter</filter-name> <servlet-name>MyServlet</servlet-name> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>REQUEST</dispatcher> </filter-mapping>
6.
16
remove.anonymous.role
false
Other properties that haven't changed their default value are: application.name The deployed application name, an application ID that must be the same in jazn-data.xml and the JpsFilter if configured. In general this property is not needed to be set. If this property is not set, the deployed application name will be used instead. This property only needs to be set if multiple applications should share the same application ID defined in the policy store. add.application.roles Adds application roles to the authenticated Subject. By default both, application roles and enterprise roles are added to the authenticated subject from where they can be checked e.g. using the ADFSecurity security expression. The default setting is "true". add.authenticated.role Adds the "authenticated" role to the user Subject. The default setting is "true" and the authenticated role is added to the Subject for all users that successfully authenticated to OPSS. oracle.security.jps.jaas.mode By default, this is set to "doAsPrivileged" . Other options are "doAs", which however is a rare use case for an ADF application.
17
In the example above,the View Object instance is defined as "EmployeesView1", whereas the interface definition is defined as the DefName attribute: The presence of the DefName="adf.sample.model.EmployeesView" parameter supports polymorphic View Object. A tree binding can have node definitions for different types and subtypes for which the ADF model binding can automatically make a different set of attributes available for subtypes. If you have a View Object with just a single type of rows, the DefName attribute is effectively optional. The DefName property is also defined for other DataControls, describing the type of object being rendered by a tree node. In a tree form samples posted to ADF Code Corner (see sample #038), the selected node type is determined using EL referencing the hierTypeBinding. The example below shows the af:switcher component used in the sample to display an input-form dependent on the selected tree node. <af:switcher facetName="#{node.hierTypeBinding.structureDefName}" defaultFacet="adf.pojo.entities.Locations"> <f:facet name="adf.pojo.entities.Locations"> </f:facet> <f:facet name="adf.pojo.entities.Employees"> ... </f:facet> </af:switcher> In summary, the tree binding defName attribute is optional up to when you actively start using it or in the case of ADF BC use polymorphic View Objects.
18
As said, the code to delete the rows looks correct and it not only looks so. The problem is cause by the rowKeySet only having a single entry, which happens when the selection listener, which by default is added when creating a single row select table, still exists.
<af:table value="#{bindings.allEmployees.collectionModel}" var="row" rows="#{bindings.allEmployees.rangeSize}" ... selectionListener= "#{bindings.allEmployees.collectionModel.makeCurrent}" rowSelection="multiple" ... >
Removing the line highlighted in bold fixes the problem so that all selected rows are deleted. Note that the ADF binding layer only supports a single row to be current, which is why there is no equivalent listener setting for the multi row select case.
When the application user clicks or hits enter on this command link, a JavaScript function is called before the event is passed to the server to execute associated actions and action listeners. The JavaScript function signature is shown below
Function onLinkPressed(evt){ }
The "evt" argument is the ADF Faces event object that is passed to the function. It allows developers to cancel the event thus to suppress server side execution of logic associated with the action or action listener property know about and access the event origin (the link component) and more. In an OTN post, the question was how to invoke the clientListener event on a component from JavaScript. Because the clientListener only listens for a component event, the solution to this is to raise the event, which can be done using JavaScript as shown below:
var link = AdfPage.PAGE.findComponentByAbsoluteId( 'cl1'); AdfActionEvent.queue(link, link.getPartialSubmit());
19
The AdfPage.PAGE access to an ADF Faces view also becomes handy for integration between 3 rd party UI rendering technologies like Flash or JQuery and ADF Faces.
Note The port number is 7101 (not 7001), which is an Oracle JDeveloper specific configuration. The WebLogic console, for example, is accessible from http://localhost:7101/console
20
The security context is also accessible from Groovy in ADF Business Components, for example to define a bind variable used in a View Criteria that queries View Object data in the context of the authenticated user
adf.context.securityContext.userName
The SecurityContext is not available in Oracle JDeveloper releases before 11g. In older releases, the authenticated username is accessible from a call to getUserPrincipalName() on the Application Module Impl class. This "old" API is still supported but discouraged in favor of the SecurityContext accessed from the ADF context.
Drag the operation binding for example DELETE from the View Object's operations node in the Data Controls panel and drop it as a command button onto your page. When pressing the button at runtime, the ADF binding is called to remove the current row (#{bindings.Delete.execute}) from the collection
21
Double click the command button in the visual editor to open the dialog for creating a managed bean method for the command action
After creating the class, give a meaningful name to the action method that is getting created and OK the dialog Select the "Bindings" tab at the bottom of the visual editor of the page In the "Bindings" section, press the green plus icon to create a new "action" Select the Application Module entry (root entry) and choose the "Commit" operation. This creates an action binding "Commit" when you OK the dialog Open the managed bean you created before and Change the action method from
public String deleteAction() { BindingContainer bindings = getBindings(); OperationBinding operationBinding = bindings.getOperationBinding("Delete");
22
Next time the button is pressed, it deletes the current row and if this could be done without errors commits the transaction.
23
Once open, use the right mouse context menu option "Add to Diagram" to browse the class path (or source path) for the classes you are interested seeing in their relation to each other. As shown in the image below, the class overview also makes sure you get the classes from the correct packages, thus avoiding accidental use of internally packaged classes. No source code is required for building your own class diagrams of the ADF framework classes. Just make sure the project you create the diagram in is configured for ADF so it has the libraries set. The image below shows classes from the "binding" package, which contains the interfaces. Concrete implementation classes, like DCBindingContainer, DCIterator etc. are locatedin the adf | model | binding package. To see the classes in a compact view, select them all using ctrl+A or the mouse and choose "View As" from the context menu to select the "compact" option. Open the context menu again and choose "Optimize Shape Size | height and width" to reduce the diagram entries in their size.
24
2.
25
Note: There exists a second very elegant option using the framework internal MetadataService class. However, the use of internal classes is not recommended and therefore an enhancement request was filed to provide a public API for developers to analyze bounded task flow metadata.
26
2.
27
28
The listener declaration in web.xml needs to be created between the filter-mapping and the servlet section. If you are unsure of where this is, double click the web.xml file entry in the Application Navigator and use the "Web Application Listeners" section in the "Application" category.
RELATED DOCOMENTATION
29