ADF Tips and Tricks
ADF Tips and Tricks
conventions ADF Client: Establish good packaging conventions Dynamic/API Use of ADF Components Getting the application module of a panel Binding to Swing components Binding dynamically created ViewObjects to ADF Swing Components 'Unexplained' problems do have causes Dynamic ADF: Create a View Object from a SQL Statement Setting Custom Properties and UI Hints on Dynamically Created View Objects ADF-SM Blog: ADF: JClient Login Dialog JDeveloper ADF Components and Refactoring Rebuidling Class Folder Renaming a JDeveloper Project DataBindings.cpx Connections.xml Rename a page definition Create a data control from a CSV file or a URL ADF JClient: Showing a splash screen when starting ADF JClient applications How to get the row index of a JTable row with a checked checkbox in it Set a checkbox in a ADF JTable ADF JPanels Working with Multiple Panels that bind the same Page Definition Working with Embedded ADF Swing Panels ADF JPanel Dialogs Framework for adding ADF Dialog panels ADF Method Dialog Workaround #1 ADF Method Dialog Workaround #2 Respond to Errors in Method Calls ADF Check Boxes Check Box in a Table Check Box for marking table selections ADF Master-Detail Forms Customizing Auto-generated M-D Forms
ADF JUNavigationBar Set which buttons to show ADF LOV Popups setHelpAction Listening to Navigation Events in the LOV Dialog ADF Comboboxes Selections from ComboBox in ADF Tables ADF Tables Selections from ComboBox in ADF Tables (see example below) ADF Trees Record Navigation using ADF Jtree Recreating ADF Tree Bindings for a new Swing tree Renaming ADF Tree Root nodes Saving and restoring a Hierarchical Tree Restoring the Last Selected Node Detecting the type of the Current Node Hierarchical ADF Trees Additional hierarchy levels Tree Node Binding: View link Accessor Rowsets ADF Trees are not bound to ViewObject Instances but to their Definitions Manual Editing of Tree Node Binding Folder Labels for ADF Tree Nodes. Expanding an ADF filtered tree node Filtering an ADF Hierarchical Tree Search a Rendered ADF Bound Tree Searching an ADF Hierarchical Tree How to use a partial branch of ADF View Tree Refreshing ADF Tree Objects How to add a context menu on an ADF Tree PanelBinding View Objects How to programmatically populate a newly added view object row? Programmatically iterating over the RowSet of a view ViewObjects Come Up Blank View Object Bind Variables and Bind Variable Default Values Find a ViewObject Row by Key Use a ViewLink to Access the Details of a Nested Collection or Nested Row
Performing In-Memory Sorting and Filtering of Row Sets Recovery from Error Saving ViewObject XML file after Updates View Object Based on * queries View Criteria ViewCriteria UsageNotes Create and Apply ViewCriteria Remove ViewCriteria Should new ViewCriteria appended to the list of already applied view criteria? Is a view Object Filtered by some View Criteria? How to use Named View Criteria How to use Dynamic Named View Criteria Application Module Working with multiple ApplicationModules Manual way to bind an AppModule Custom Method How to Access an Application Module Client Interface Connecting to a second database using a database link Connecting to a second database using another project's application module Connecting to a different database by adjusting application module properties Login and Authentication Using database tables for authentication in ADF applications ADF Eye Candy Include Images in Swing Forms QUESTIONS UNRESOLVED ISSUES Creating Shuttle Component in Oracle ADF Tree Binding fails after opening in Dialog more than once Workarounds to Some JBO Exceptions JBO-35001 JBO-27022 JBO-2900 JBO-25001
Example: All view used for the MASSterMine client UI are indicated as V_NAME
See also: 'An Introduction to ADF Best Practices' an Oracle white paper (July 2006)
To create the application 1. Create a Business Components project with an ApplicationModule and at least one ViewObject 2. Create an ADF Swing project 3. Create an empty ADF Swing panel and make it runnable 4. To create all required entries in the DataBindings.dcx file, drop an existing ViewObject
from the data control palette and choose Table from the ADF menu. Delete the table immediately from the Swing panel. You can also manually create the DataControl binding to the ApplicationModule, but this takes longer to finish ;-)
5. Add a JScrollPane from the Component Palette and place a JTable component into it 6. Add a JUNavigationBar Component from the "ADF Swing" selection of the Component
Palette
7. Add a text field for query input and a button to execute the query 8. In the example code the following names are used for the components JTable: myQueryTable JUnavigationBar: myQueryTableNavigationBar JTextField: qryText Create a method that looks as follows: 9.
private void populateADFSwingComponentsFromQuery(String qstmt) { //get ApplicationModule using ADF data provider API ApplicationModule am = (ApplicationModule) panelBinding.getApplication().getDataProvider(); //if dynamic view object exists, delete it try{ am.findViewObject("myView").remove(); } catch (Exception e){} ViewObject myView= am.createViewObjectFromQueryStmt("myView",qstmt); DCJboDataControl jbodc = new DCJboDataControl(am); JUIteratorBinding iterBinding = new JUIteratorBinding(jbodc,myView ); JUIteratorBinding testIter = (JUIteratorBinding) panelBinding.findIteratorBinding("dynamicSql"); try{ panelBinding.removeIteratorBinding("dynamicSql"); } catch (Exception e){} panelBinding.addIterBinding("dynamicSql", iterBinding); JUTableBinding tblBinding = new JUTableBinding(myQueryTable, iterBinding, null); myQueryTable.setModel((TableModel)tblBinding); myQueryTableNavigationBar.setModel(iterBinding); }
Thanks to Sathish and Shailesh from the development team for looking into this. Frank Posted by Frank.Nimphius at March 7, 2006 01:34 AM My Usage Notes: In one case I had somewhat unpredictable behavior trying to get this to work. The added JTabbedPane did not show the dynamic table. I ended up embedding the same panel with a design time ViewObject binding so that I could first see it, and then removed the binding and added a call to the populateADFSwingComponentsFromQuery . This
appeared to work Help Using Navbar Find mode with dynamic viewobjects, Posted: Aug 23, 2006 6:22 AM What needs to be added to my coded to make the JUNavigatorBar QBE function in the "ADF Swing: Binding dynamically created ViewObjects to ADF Swing" example posted by Frank Nimphius on March 07, 2006. My table loads from the dynamic viewobject and the Navbar moves the cursor over each row in the table but when I go into Find mode ,the table row for entering the search criteria is not editable. I must be missing something basic here, but I am at a lost for the solution.
Ok, I found this solution to make the dynamic view objects example 'searchable' from the NavigatorBar 'Find' button. I had to extend and replace the JUTableBinding class when creating a TableBinding which is then set as the model for my dynamic JTable using .setModel(); The rest of the example code works without modification. class MyJUTableBinding extends JUTableBinding{ public MyJUTableBinding(javax.swing.JTable control, JUIteratorBinding iterBinding, java.lang.String[] attrNames){ super(control, iterBinding,attrNames); } public boolean isCellEditable(int row, int col) { return true; } }
New Java WebStart Wizard Fails to ID DataModel Definition Also trying to continue by defining a <New..> does not proceed past the first step
It seems that I had commended out (but did not remove) an <BC4JDataControl> element in the DataBinding.cpx file. I needed to physically delete this element (and not just commend it out) before the wizard would proceed.
[March 6, 2010] Receiving a bunch of non-informative JBO-2900 errors. Check for remnants of deleted pages that exist in the DataBinding.cpx file. The <pageDefinitionUsages> elements are not cleaned appropriately when a page is deleted. Occasionally these errors re-appear. I have not been able to find what really causes them. However, if you open the DataBindings.cpx file and reformat the code (right click on source and reformat) the errors frequently resolve! (go figure!)
In this example, the resulting view object will have two attributes, named EmpName and EmpMgr. Internally, this method create a temporary view definition with no entity object base and maps database columns to attribute definitions. Then, it uses that view definition to create the view object.
ADF-SM Blog:
71.Dropdown Lists in Table with Different Choices per Row [10.1.3, HR schema]
1. Open the Application workspace node and select the Project to be renamed. From the 2. 3. 4. 5.
JDev menu select File->Rename The project is renamed, BUT not the project directory. Exit JDev. Rename the project directory manually Re-Open JDEv.The new project directory does no immediately appear in the application workspace Go on the applications node and <Add to applications>. Navigate to the renamed directory and add the renamed project.
TODO: identify where the directory information is persisted on the file system DONE: index.xml in folder C:\DEVTOOLS\ORACLE\JDEV\jdev\system\oracle.ide.10.1.3.40.66\projects So this is where we can rename the folder as well but it appears that the process above is the right way to do this. Of course there might still be dependencies in files like the ant build file (ctbuild.xml) that will need to be updated
DataBindings.cpx
Oracle Ref Oracle JDeveloper 11.1.1.0.2 Note I had this file update from the SVN repository using the build-in SVN plugin. When I attempted to run the ADF application it complained about a missing PageDef that was added during the file update. So it was there but not detected. I had to shut down JDev and restart it to make this change visible.
Connections.xml
This file maintains connection properties. Can be found in .adf/META-INF directory (on the source side). It is copied to the META-INF directory on the class folder. There ate two similarly named files one maintaining the jdbc connections and the other those for URL/CSV files used in data controls (see below)
The created DataControl is added to the list of data controls and can be used in an ADF Swing component (drag and drop)
ADF JClient: Showing a splash screen when starting ADF JClient applications
[copied comments] Performance is not only a figure that can be physically measured for each application, it also describes end-user perception. If the end-users perception of a starting application is that it runs slow, no matter how good your arguments are to justify the time needed to launch an application, you can't win this fight. However, if you can't deal with them, trick them! One way of making your Java applications appear starting fast is to use a splash screen up front. This kind of 'end-user entertainment' is commonly used by many kinds of applications and also easy to implement with ADF JClient, using the splash screen solution published on www.javapractices.com. I tried it with ADF JClient in Oracle JDeveloper 10g and it just works great. To implement it, I created a private method as follows to instantiate the Splash screen and called it first thing in the try/catch block of the ADF JClient application classes' main method:
private static void showSplashScreen(){ fSplashScreen = new SplashScreen("/Images/splash.gif"); fSplashScreen.splash(); } The image file "splash.gif" is the code above is expected to be in the directory "Images", which is a subdirectory of the project's "src" directory. Make sure that the "splash.gif" image gets added the JDeveloper project so that it gets deployed with the application source files during compilation. To close the Splash screen in a thread safe manner, create an inner class: private static final class SplashScreenCloser implements Runnable { public void run() { fSplashScreen.dispose(); } }
and call it at the end of the try block and the catch block, or using the finally block of the try/catch block construct. EventQueue.invokeLater( new SplashScreenCloser() ); Frank Posted by Frank.Nimphius at 01:53 AM
How to get the row index of a JTable row with a checked checkbox in it
Code by Frank Nymphius TableColumnModel tcm = jTable1.getColumnModel(); JUTableLOVEditor jTableLOVEditor = (JUTableLOVEditor) tcm.getColumn(0).getCellEditor(); JComboBox component = (JComboBox) jTableLOVEditor.getComponent(); component.addFocusListener(new FocusListener(){ public void focusGained(FocusEvent e) { DCIteratorBinding dciter = (DCIteratorBinding)panelBinding.get("EmployeesView1Iterator"); System.out.println(dciter.getCurrentRowIndexInRange()); } public void focusLost(FocusEvent e) { } });
1. Return an array of
the selected rows
2. These need to be
mapped back to the JTable model rows (using the convertRowIndex ToModel(i) ) since the user could have sorted the table 3. Then we set the value (in this case we set the attribute to a boolean 1 so that a checkbox displays checked 4. Finally repaint the JTable to refresh the modified cells
ADF JPanels
Working with Multiple Panels that bind the same Page Definition
November 26, 2008
References to this issue http://www.oracle.com/technology/products/jdev/htdocs/9.0.5.2/readme.html http://forums.oracle.com/forums/thread.jspa?messageID=1179594� oracle.jbo.JboException:35001 Initial attempts to resolve this issue following the provided instructions did not work. So, I took a somewhat simpler approach (that works with my current requirements but may not scale well). Since my problem arose from the use of TWO panels binding the same page definition, I've copied and renamed the page definition and used the new copy for one of the two panels. This resolved the oracle.jbo.JboException:35001 being raised when adding multiple instance of an existing JClient panel to another JClient panel or when launching multiple instances of a panel from the same binding context. To do this: 1. Open the PageDef file and Save As with a new name 2. Then Manually add the required XML element for the new page definition in the Databindings.cpx file 3. Edit the corresponding panel class to use the new PageDef name private JUPanelBinding panelBinding = new JUPanelBinding("NewNamePageDef");
The bindNestedContainer method exists for all default ADF panels and it must be called by an embedding panel during initialization (or later as needed, see below).
Reusing Databound ADF Swing Panels with Different View Object Instances Using Model Parameters [10.1.3] http://blogs.oracle.com/smuenchadf/examples/ 91.
embeddedPanel.bindNestedContainer(panelBinding.findNestedPanelBinding("com_nibr _bdm_EmbeddedPanelPageDef"));
So for example ,
...and instead use similar code to what is shown below to fire off the method:
basically we manually create and execute a method binding. This ALWAYS works Here is the code fragment private void jBGenerateBifArtifact_actionPerformed(ActionEvent e) { OperationBinding opB=(OperationBinding) panelBinding.getOperationBinding("gen_bif_artifact"); opB.getParamsMap().put("art_name",jTName.getText()); opB.getParamsMap().put("art_doc",jTDocumentation.getText()); opB.getParamsMap().put("genActitivtyId",jTActivityId.getText()); opB.execute(); JOptionPane.showMessageDialog(this, "Output: " + jTName.getText() + "\nSuccessfully Generated"); }
opB.execute(); /* Note how we check for errors and present appropriate dialog */ if (opB.getErrors().isEmpty()){ JOptionPane.showMessageDialog(this, "Object: " + objNameHint.getText() + "\nSuccessfully Created "); }else{ JOptionPane.showMessageDialog(this, "Creation of Object: " + objNameHint.getText() + "\nFAILED due to errors! ", Error Title JOptionPane.ERROR_MESSAGE); }
ADF JUNavigationBar
Set which buttons to show
Use the JUNavigationBar API as shown below to set which buttons are shown setHasDeleteButton(boolean b)
Tells the toolbar whether to include the delete button or not. setHasExecuteButton(boolean b) Tells the toolbar whether to include the 'Execute' button or not. setHasFindButton(boolean b) Tells the toolbar whether to include the 'Find' button or not. setHasInsertButton(boolean b) Tells the toolbar whether to include the insert button or not. setHasNavigationButtons(boolean b) Tells the toolbar whether to include the navigation buttons or not. setHasTransactionButtons(boolean b) Tells the toolbar whether to include the commit/rollback buttons or not.
An action listener can be assigned to the button that fires the event. Note: The action is executed upon exiting the LOV dialog.
setHelpAction
public void setHelpAction(java.awt.event.ActionListener al)
If the default LOV dialog is being used to display LOV Data, this method should be used by applications to set the ActionListener that is triggered on activating the help button in the LOV Dialog. This allows applications to display custom help for the LOV dialog.
We get the LOV RowSetIterator and we activLOVButtonBinding.getLOVRowSetIterator().a add a RowSetListener. ddListener(new RowSetListener(){ This interface has several methods that need to be implemented but since I'm public void only interested in the navigation events rangeRefreshed(RangeRefreshEvent I left everything else blank. rangeRefreshEvent) { } The navigated method implements the action. public void rangeScrolled(ScrollEvent The example is from the biofiler class scrollEvent) { InsertWrkFlActivityConfigurator } using two custom methods I set a public void rowInserted(InsertEvent visual cue and show the activity insertEvent) { parameters in response to navigating } the list in the LOV dialog. No need to exit the dialog, so this acts public void rowDeleted(DeleteEvent as a dynamic way to review the deleteEvent) { activities before selecting the } desired one. public void rowUpdated(UpdateEvent updateEvent) { } public void navigated(NavigationEvent navigationEvent) { // System.out.println("navigationEvent firing!"+navigationEvent.getSource().toString()); Object activityID=((JULovButtonBinding) panelBinding.getControlBinding("Name") ).getLOVRowSetIterator(). getCurrentRow().getAttribute("Activityid"); Object imageID=((JULovButtonBinding) panelBinding.getControlBinding("Name") ).getLOVRowSetIterator(). getCurrentRow().getAttribute("ImageId"); setVisualCue(imageID); setPropertyView(activityID); } });
ADF Comboboxes
ADF Tables
Selections from ComboBox in ADF Tables (see example below)
ADF Trees
Record Navigation using ADF Jtree
MAY 17, 2010 This is a technique I have been using for a while. It consists of the following 3 steps: 1. Make the Swing JPanel implement the TreeSelectionListener interface 2. Implement the valueChanged(TreeSeelctionEvent) method 3. Add a tree selection listener on the tree. The Panel Listens for Tree Selection events Ways to implement the valueChanged method One helpful suggestion was derived from the following blog. In summary we obtain the row of the tree node and then set any iterators we need using the FindByKey method. /** * This method responds to TreeSelection events generated by the embeddedArtifactNavTree . * [MAY-17-2010] Of interest is the code that creates a Key from the tree node and * synchronizes the currentRow of the BpmnArtifactView1Iterator accordingly using this Key. * This seems to be a better approach than filtering the Viewobject to get back a single row * [AUG-25-2010] By checking for path.getPathCount()>1 we ensure * that no errors are thrown when user clicks on root node * @param e */ public void valueChanged(TreeSelectionEvent e) { TreePath path=((JTree)e.getSource()).getSelectionPath(); JUTreeNodeBinding tb=null; /* * Note how we verify the source of the TreeSelectionEvent * by checking that it equals the JTree object in this class only */ if (path != null&&e.getSource().equals(embeddedArtifactNavTree) &&path.getPathCount()>1) { ApplicationModule am = (ApplicationModule) panelBinding.getApplication().getDataProvider(); DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent(); tb = (JUTreeNodeBinding)node.getUserObject(); DCBindingContainer bc=(DCBindingContainer)getPanelBinding(); DCIteratorBinding iter=(DCIteratorBinding) bc.findIteratorBinding("BifxImageView1Iterator"); Key key=new Key(new Object[] {tb.getRow().getAttribute("ImageId")}
); RowSetIterator rsi=iter.getRowSetIterator(); Row row=rsi.findByKey(key,1)[0]; rsi.setCurrentRow(row); } } path.getPathCount()>1 This ensures that we do not respond to click event on the root node! Notes on Adding the Tree Selection Listener //class declarations private JTree embeddedArtifactNavTree = new JTree(); //in the init method...
embeddedArtifactNavTree=artifactBranchNavTree1.getArtifacTempltNavTree(); embeddedArtifactNavTree.addTreeSelectionListener(this); The above code fragment demonstrates a common scenario. The JTree emitting the click events may not reside in the same JPanel that is registered as the listener (and implements the valueChanged method). In the example above it resides in the artifactBranchNavTree1 embedded JPanel. We need to implement a getter method and provide a local variable for the returned JTree
Another important consideration is the ability to listen to the 'right' tree event. Remember that the source of the event can be discovered from the event itself. NOTE: I have been unsuccessful implementing (in Swing) a method that has been described by Steve Muench in the July/August 2009 Oracle Magazine. The article describes targeting another iterator in tree selection. Although it sounds rather easy to implement in several attempts I was not able to get it to work with a Swing JTree as the navigator.
From the bindings tab, select the iterator, right click: Refactor->Rename. Enter a new Name (no spaces allowed)
private static RowKeySet savedExpanded; private static RowKeySet savedSelected; public String saveButton_action() { // Add event code here... savedSelected = tree1.getSelectedRowKeys().clone(); savedExpanded= tree1.getDisclosedRowKeys().clone(); return null; } public String loadButton_action() { // Add event code here... tree1.setSelectedRowKeys(savedSelected.clone()); tree1.setDisclosedRowKeys(savedExpanded.clone()); return null; } Restoring the Last Selected Node
[March 19, 2011] In several cases after the indirect addition of a tree node (ie. via a stored procedure) we need to refresh the tree by executing a query on the root iterator. This collapses the tree and potentially confuses and frustrates the user that now has to go back and re-expand the nodes of interest. Although it would preferable too simply find a solution that fully restores the expansion of the tree, a satisfactory workaround is to simply restore the last node that the user was working on. In the last couple of days I took a detour to examine how this an be accomplished. Things to watch out for while working with ADF tree node expansion! 1. There exist typically TreeSelectionListeners with a valueChanged(TreeSelectionEvent e) method that will also automatically respond to the collapsing tree event. As a result various node and selection assignments right before the query are reset or changed immediately after (that had me scratching my head for a while). A solution to this is to set a flag immediately before the query that will cause the valueChnaged method to not fire if we are in the middle of restoring the expansion of the tree.
OFINT: Examining the code above I observed the use of the object clone method. Presumambly this caches the objects so they do not get changed after the tree collapses and is getting restored TODO: Examine this method further. APR-15-2011: It does not appear that TreePath implements the clone-able interface 2. If your are assigning variables based on the tree node, model, selection model etc these will be changing even if the valueChanged method does not fire since they are simply members of the corresponding JTree. So store in a cached model (or simply strings) and use this cached values to restore the tree.
So, now on the technique to restore the expansion state of the last selected node.
1. In this example, the last selected node path was stored during the valueChanged
event in a map and it is retrieved. 2. Then I build a String array that maintains the names of each of the nodes in the path 1. TreePath lastAssayPath=(TreePath)model.getPropertiesMap().get("assayPath"); 2. String[] lastAssayPathObjects=new String[lastAssayPath.getPath().length];// lastAssayPath.getPath(); for(int i=0;i<lastAssayPath.getPath().length;i++){ System.out.println("Added: "+lastAssayPath.getPathComponent(i).toString() ); lastAssayPathObjects[i]=lastAssayPath.getPathComponent(i).toString(); }
1. We then set the flag (in the shared model) that will prevent the valueChanged
method from firing
Finally the expansion of each of the ancestor nodes of the last node happens in a loop. Of interest is the use of the getNextMatch method that matches the name from the cached path node names. for(int i=0;i<lastAssayPathObjects.length;i++){ System.out.println("Expanding: "+lastAssayPathObjects[i].toString()); navTree.setSelectionPath(navTree.getNextMatch(lastAssayPathObjects[i],0,Position.Bias.F orward)); navTree.expandPath(navTree.getSelectionPath()); } The effect of this targeted restoration is quite useful, as it focuses the user by collapsing all other nodes and expanding the last node that an action was fired from The examples are taken from the following Biofiler classes 1. public class ActionNewProcess extends AbstractAction 2. public class PNavBifExperiment extends JPanel implements JURegionPanel,
TreeSelectionListener
voA:vcN1 |_voA
How to construct the tree rules Start by creating a tree with the parent voA:vcN1 (LEVEL 1 Nodes) ViewInstance. Then edit the tree binding to define the branch rules. Select the root node viewObject and add children nodes from the voA view (step 4 above ViewLink). Interestingly, although you are allowed to add this rule IT WILL NOT APPEAR as a new rule in the tree binding configuration wizard UI. However, it will appear once the tree is displayed in the UI. This will allow you to navigate and expand from the root Level 1 nodes to leaf node, adding as many node levels as required to display the entire hierarchy
public void valueChanged(TreeSelectionEvent e) { TreePath path=((JTree)e.getSource()).getSelectionPath(); JUTreeNodeBinding tb=null; DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent(); tb = (JUTreeNodeBinding)node.getUserObject();
From there we can get various row attributes such as: tb.getRow().getAttribute("Assayid").toString()
NOTE: I have not been able to successfully query the ViewObject (as it seems to require a BIND VARIABLE that I have not figured out how to set). However, I have been able to successfully query the Iterator. I have done this in an attempt to refresh the tree node display after the addition of a new node (via a stored procedure). This does not seem to work. Its only after refreshing the Root node that the display gets refreshed (which also collapse the tree, which was what I was trying to avoid!)
ADF Trees are not bound to ViewObject Instances but to their Definitions
[FEB-08-2011]
I finally understood better that ADF trees are not bound to ViewObject instances but rather to the ViewObject structure and ViewLinks.
What this means is that what you view in the visual binding editor is not specific instances of ViewObjects, but Viewobjects that are accesible via the ViewLinks that are part of the ViewObject definition. In fact ADF Trees can be constructed in th eabsence of any view objects as long as the correct viewLinks exist.
Make sure that the iterator RangeSize="-1" when the RangeSize (by default at 25) would filter parent nodes out. Otherwise you get an error trying to expand the parent nodes to the child level
1. Create a view to support the hierarchical tree. 2. Ensure that the view works with a BINDING variable that encapsulates the search
term. When the view is executed with the search term only level 1 nodes should be returned 3. Create a two-part panel so that you can place the search panel on the top and the tree at the bottom 4. Add the root node view operation ExecuteWithParams as a method to the top panel. 5. Add the tree to the bottom and you are ready to go!
The Level 1 nodes of the tree can be filtered by entering the search term and pushing the ExecuteWithParams button.
http://www.oracle.com/technetwork/developer-tools/adf/learnmore/61search-in-rendered-trees177577.pdf
This method is now (10-17-2011) encapsulated in the JTreeUtil.findMatchingTreeNodes(DefaultMutableTreeNode rootNode, String nodeStr) static method This method returns an array list that can be iterated to guide a successive matching node traversal. An example action performed by a search button is shown below (taken from ArtifactTemplateTreeNavigator class)
private void searchButton_actionPerformed(ActionEvent e) { if (!navIsExpanded){ navIsExpanded=JTreeUtil.expandJTree(artifacTempltNavTree,JTreeUtil.EXPAND_FULLY); } String findTerm=searchTermField.getText(); /* new way for finding based on array list of terms */ if (findTerm.equalsIgnoreCase(currentSearchTerm)&&matchingTreeNodes.size()>0){ if (currentNodeIndex>=matchingTreeNodes.size()) { //reset node index so search starts from top currentNodeIndex=0; } String nextMatchingNode=matchingTreeNodes.get(currentNodeIndex).toString(); int currentTreeRow=artifacTempltNavTree.getRowForPath((artifacTempltNavTree.getSelectio nPath())); artifacTempltNavTree.setSelectionPath(artifacTempltNavTree.getNextMatch(nextMatching Node,currentTreeRow+1,Position.Bias.Forward)); artifacTempltNavTree.scrollPathToVisible(artifacTempltNavTree.getSelectionPath()); currentNodeIndex++; }else{ currentNodeIndex=0; currentSearchTerm=findTerm; matchingTreeNodes=JTreeUtil.findMatchingTreeNodes(rootNode,findTerm); if (matchingTreeNodes.size()>0){ String nextMatchingNode=matchingTreeNodes.get(currentNodeIndex).toString(); int currentTreeRow=artifacTempltNavTree.getRowForPath((artifacTempltNavTree.getSelectio nPath())); artifacTempltNavTree.setSelectionPath(artifacTempltNavTree.getNextMatch(nextMatching Node,currentTreeRow+1,Position.Bias.Forward)); currentNodeIndex++; }else{ System.out.println("Node with string " + findTerm + " not found"); } } }
December 10, 2009 I've tried to use a partial branch of ADF view tree
For example given the View Tree above, I have a panel where only the ActivityInputArtifactView1 and BifvArtifactIngroupView3 are used. However, the row filtering on the leaf view BifvArtifactIngroupView3 depends on setting a BIND VARIABLE in the root View node that is not included on this panel. THIS DID NOT WORK as expected. Setting the bind variable on the root node did not return any rows!
The trick that made this work was to add the BifvActivityInputView1.Activityid as a text field and then delete it. When I was asked to remove the unused iterator I replied NO. The remnant of this iterator NOW MAKE the filtering work as expected! Apparently we need the iterator in the page definition
I have observed that panelBinding refreshing does not appear to be effective if new root level nodes have been added. A slightly more drastic approach is to refresh the ViewObject that the root level nodes are bound to. This is accomplished by executing the query on the ViewObject with code similar to the one shown below:
TO-TRY this IDEA (JULY 26, 2011) An alternate way to refresh an ADF tree after some action that modifies its model in the database (such as the exectuiton of a stored procedure) is to get the internally generated view object that ADF trees generate and execute after the action has been completed. Here is some example code
TreePath path = ((JTree)e.getSource()).getSelectionPath(); JUTreeNodeBinding tb = null; ApplicationModule am = (ApplicationModule)panelBinding.getApplication().getDataProvider(); DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); tb = (JUTreeNodeBinding)node.getUserObject(); System.out.println("VO_Name: "+tb.getIteratorBinding().getViewObject().getName ());
The name printed out is the internally generated name for the VO bound to the selected tree node. An example of how you may proceeed is shown below with the internally generated name shown in bold.
If the VO requires a BIND VARIABLE use code similar to the one shown in blue. If left out you may get a helpful diagnostic message that will allow you to identity the required BIND VARIABLE. The re-execution of the view should refresh the tree.
Listener action
PanelBinding
Some useful info that can be collected from the panelBinding varaible, ubiquitous in ADF panels.
Method panelBinding.getFullName ()
panelBinding.getName()
View Objects
How to programmatically populate a newly added view object row?
March 17, 2008 In one panel I have a button that I want to use to INSERT a new ROW in a ViewObject displayed as a table in another panel. How can we insert this new row and populate it with data from the action of the button? CODE FRAGMENT //create new row Row newRow=VO.createRow(); //Populate attributes of new row row.setAttribute("AttrName1","ValueAttr1"); row.setAttribute("AttrName2","ValueAttr2"); //insert new row VO.insertRow(newRow); The specific example below uses a button to open a JFileChooser that allows the user to select a file. Metadata from this file are then stored in a new row of the View Object named AnxDatafileview2
2.
3. Then Define the BIND variable: You can either supply a default value or leave it null (in which case by default you don't get any results which in some
case is what you want) In some cases its is better to use queries with a LIKE where clause. For example LIKE :BIND_VARIABLE. Then the default value of the bind variable can be set to % where it will select all the records. I have found this last approach to be more reliable.
1. Subsequently to set the bind variable use code like the one shown below:
The code above gets the Activityid from a tree node and uses it to set the BIND Variable in the corresponding view.
model = (ContextMenuModel)getValue("contextModel");//this is from a Biofiler class //next is the View object we need to synchronize with the contextModel ViewObject expVo = panelBinding.getBindingContext().findDataControl("BifAssayModuleDataControl").getApplic ationModule().findViewObject("BifAssay1View1"); oracle.jbo.Row[] r = expVo.findByKey(model.getSelectedComponentItem().getKey(),1); expVo.setCurrentRow(r[0]); //this is the trick! since the returned rowset is an array we need to select one!
The view object's SQL mode controls the source used to retrieve rows to populate its row set. The setQueryMode() allows you to control which mode, or combination of modes, are used: ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES This is the default mode that retrieves results from the database. ViewObject.QUERY_MODE_SCAN_VIEW_ROWS This mode uses rows already in the row set as the source, allowing you to progressively refine the row set's contents through in-memory filtering.
ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS This mode, valid only for entity-based view objects, uses the entity rows presently in the entity cache as the source to produce results based on the contents of the cache. You can use the modes individually, or combine them using Java's logical OR operator (X | Y). For example, to create a view object that queries the entity cache for unposted new entity rows, as well as the database for existing rows, you could write code like: setQueryMode(ViewObject.QUERY_MODE_SCAN_DATABASE_TABLES | ViewObject.QUERY_MODE_SCAN_ENTITY_ROWS) I've tried setRowMatch(RowMatch) but this seems to remove the rows from the collection! Rows can come from three different sources. If the row set currently has a collection of rows (View rows), QUERY_MODE_SCAN_VIEW_ROWS will indicate that the row match (see setRowMatch(RowMatch)) should be applied to further filter view rows. Unqualifying rows are removed from the collection
[FEB-14-2011] ViewObjects based on custom * queries (SELECT * FROM...) are not recommended. If the schema of the target table changes the ViewObjects break. Apparently they depend on a particular sequence of returned columns Instead always enumerate the columns that a query returns as this is resistant to changes in the underlying table schema. If you receive errors like: (oracle.jbo.AttributeLoadException) JBO-27021: Failed to load custom data type value at index 28 with java object of type oracle.jbo.domain.Number due to java.sql.SQLException.
its probably due to the use of star queries. Identify the offending VO definition and replace the star query with a named column query. Required column names can be easily obtained
View Criteria
ViewCriteria UsageNotes
March 16-18 2009 What does it do: Creates Viewcriteria for filtering a result set with The use of ViewCriteria using JDev11G has been unusually problematic. Previous 10G code stopped working. More specifically it appears that for the dynamic views although I'm still able to create ViewCriteria in Find mode the UI bound values are NOT saved. Instead the returned attribute values are null. Since it was critical for me to re-establish lost filtering functionality, I was able to generate ViewCriteriaRows manually by grammatically reading the rows of the table in FindMode. This worked rather well although it still suffers from the inability to enclose attribute names in quotation marks. This had been a problem before and lead me to try to create custom WHERE clause.
// 1. Create a view criteria rowset for this view object ViewCriteria vc = procView.createViewCriteria(); vc.setName("processVcrit"); // 2. Use the view criteria to create one or more view criteria rows ViewCriteriaRow vcr1 = vc.createViewCriteriaRow(); // 3. Set attribute values to filter on in appropriate view criteria rows
vcr1.setAttribute("Processid", "= " + propView.getCurrentRow().getAttribute("Processid")); // 4. Add the view criteria rows to the view critera rowset vc.add(vcr1); // 5. Apply the view criteria to the view object procView.applyViewCriteria(vc); System.out.println("Applied View Criteria"); procView.executeQuery(); System.out.println("ProcessID Filter applied");
Remove ViewCriteria SEP-03-2010 Having problems clearing and removing ViewCriteria. Trying the following variations do not observe any effect // // // // ViewCriteria vc = procView.getViewCriteria("criteria_name"); vo.removeViewCriteria("criteria_name") vc.resetCriteria(); procView.applyViewCriteria(vc);
// ViewCriteria vc =procView.getViewCriteriaManager().getViewCriteria("processVcrit"); // vc.removeCurrentRow(); // procView.applyViewCriteria(vc); // procView.getViewCriteriaManager().removeApplyViewCriteriaName("processVcrit"); What worked was the following:
viewObject.applyViewCriteria(null)
Should new ViewCriteria appended to the list of already applied view criteria? Note the API paraemters of the ViewObject.applyViewCriteria method
Applied the view criteria to this view object. If bAppend is true view criteria is appended to the list of already applied view criteria. If bAppend is false the applied view criteria list is cleared prior to applying the passed view
criteria.
Parameters: criteria - view criteria to be applied bAppend - flag to indicate if the applied view criteria list needs to be cleared before applying the view criteria.
NOTE: This has effects on adding as well as removing criteria. By simply applying ViewObject.applyViewCriteria(null) all view criteria is reset. This may not be the desired effect. You may want to apply/reapply a new critrerion in addition to an existing one. the the recommended usage is ViewObject.applyViewCriteria(null, true) meaning that the criteria will be appended to an existing list Is a view Object Filtered by some View Criteria? FEB-07-2011 Goal: Discover if a view object is filtered or not. This may be useful if you want to update a UI indicator that informs the user that what is displayed is either filtered or not.
Comments This is an example of a method that checks the View Criteria status of a View Object and updates the text of a label. Of Interest is the use of the getViewCriteriaClause to check whether its null or has been set to something and therefore the ViewObject is filtered.
private void updateFilterStatusLabel(){ if (artifactOfTypeVO.getViewCriteriaManager( ).getViewCriteriaClause(true)!=null) { filterStatusLabel.setText("Rows :" + artifactOfTypeVO.getRowCount() + "(Filtered)"); System.out.println(artifactOfTypeVO.getView CriteriaManager().getViewCriteriaClause(true )); } else { //the view is not filtered filterStatusLabel.setText("Rows :" + artifactOfTypeVO.getEstimatedRowCount()); } } private void updateFilterStatusLabel() { if (artifactOfTypeVO.getViewCriteriaManager( ).getViewCriteria("MatchPropertyViewCriteria ").getClauses().getClauseForQuery()!= null) { rowCountLabel.setText("Rows :" + artifactOfTypeVO.getRowCount()); filterStatusLabel.setText("(Filtered)"); } else { //the view is not filtered rowCountLabel.setText("Rows :" + artifactOfTypeVO.getRowCount()); filterStatusLabel.setText(""); } }
What has not worked is the count of getAllRowsInRange (see rem-ed code). Even when the ViewCriteriaClause is null (not filtered a far as I'm concerned) the count does not appear to be zero. So this did not work if (artifactOfTypeVO.getViewCriteria().getAllRo wsInRange().length != 0) NOTE: FEB-14-2011: The reason this is not working as expected is that we apply multiple named criteria. Which ones does getAllRowsInRange retrieve?
We can be more specific about the ViewCriteria we are inspecting. This seems to be the most reliable method to detect the application of the specific Viewcriteria. On the left is an example of a method that drills down into the specific critetion to determine if its the one whose clause is null.
objView.applyViewCriteria(null); Reset any previous view criteria and get vco = the named view criteria from the Criteria objView.getViewCriteriaManager().getV Manager iewCriteria("UI20101015A1ArtifactProp ery"); objView.applyViewCriteria(vco); objViewexecuteQuery(); Then apply the UI20101015A1ArtifactPropery view criteria and refresh the view object
[APR-13-2011] An example of how to use a Named View Criteria clause with a BIND variable private void setPropertyView(Object This code has been encapsulated in a simple bpmnActivityId){ method ApplicationModule Note how we: am = (ApplicationModule) 1. First apply the Named View Criteria panelBinding.getApplication().getDataProvide and r(); 2. Then we set the BIND variable ViewCriteria vco; ViewObject propView = am.findViewObject("UI20110413BpmnProper tyViewRO"); propView.applyViewCriteria(null); vco = propView.getViewCriteriaManager().getView Criteria("BpmnPropertyViewROCriteria"); propView.applyViewCriteria(vco); propView.setNamedWhereClauseParam("BIN D_BPMNACTIVITYID",bpmnActivityId); propView.executeQuery(); }
artifactOfTypeVO.applyViewCriteria(null); ViewCriteria matchByprop= artifactOfTypeVO.getViewCriteriaManager ().getViewCriteria("MatchPropertyViewCrit eria"); ViewCriteriaItem vci=(ViewCriteriaItem) matchByprop.findElementWithRelativeNa me("MatchPropertyViewCriteria.vcrow184.Ar tifactid"); vci.setValue(sqlCriterion); Use View Criteria manager to get the named view criterion. Then use the findElementWithRelativeNamemethod to retrieve the specific ViewCriteriaItem whose value we will dynamically update. Note that we need to cast this to a ViewCriteriaItem as the method returns an object Update the criterion value with a new sql fragment in this case
Apply the updated named view criterion and artifactOfTypeVO.applyViewCriteria(match refresh the view object Byprop); artifactOfTypeVO.executeQuery();
Application Module
Working with multiple ApplicationModules
November 26, 2008 Finding a ViewObject while working with multiple ApplicationModules is more reliable if the AppModule name is used as follows: ViewObject abcVo = panelBinding.getBindingContext().findDataControl("AppModuleDataControl2") .getApplicationModule().findViewObject("AnxDatamanagerView1"); I have run into NullPointer exceptions when I used an alternate syntax that worked fine when I was dealing with a single AppModule ViewObject expVo = panelBinding.getBindingContext().getDefaultDataControl() .getApplicationModule().findViewObject("AniExperimentstepView1");
Comment
Note how the NamedData elements are named from the method parameters. Once this MethodAction element is introduced we can accecss the operation via its panel binding as follows:
OperationBinding opB=(OperationBinding) DataControl="BpmnStoredProcsDataControl" panelBinding.getOperationBinding("update _bifartifact_prop"); InstanceName="BpmnStoredProcsDataContr ol.dataProvider"> opB.getParamsMap().put("new_value",prope <NamedData NDName="new_value" rtyValue); NDType="java.lang.String" opB.getParamsMap().put("bpmn_propertyId" NDValue="${bindings.update_bifartifact_pro ,propertyId); p_new_value}"/> <NamedData opB.getParamsMap().put("bif_artifactId",artI NDName="bpmn_propertyId" d); NDType="int" opB.execute(); NDValue="${bindings.update_bifartifact_pro p_bpmn_propertyId}"/> <NamedData NDName="bif_artifactId" NDType="int" NDValue="${bindings.update_bifartifact_pro p_bif_artifactId}"/> </methodAction>
One way to get around this limitation is to create a private link in the database for the
schema that the ADF application is already connecting to. In turn a Entity Objects can be created on the remote link using the @REMOTE_LINK syntax (shown below)
In the example shown above the View Project has dependencies on both the Model and ModelOMIXD projects allowing This allows you to access the application module of the new project and thus have access to more than one database connections. As an example, I've build a simple form that contains and displays bindings to two different databases simultaneously.
Different connection for different application Modules Open the configuration of a particular application module and adjust the connection
This connection is saved in the corresponding bc4j.xcfg file and is used for the connection of that application module. (Note: there has been some discussion that nested application modules will only use the connection of the root application module)
QUESTIONS
1. Can NamedViewCriteria be used at run time programmatically? [7/23/2009]. So far I
have not found a way to decalre them and then use them programmatically 2. Can Tooltips be generated from an attribute binding? This would be useful in cases where due to space limitations a Swing component (such as JLabel or JText) can not display the full text of an attribute. In that case it would be useful to display the full text as a tooltip. Currently it seems that tooltips can be generated either form a static attribute OR the record that is displayed first when the component is initialized in the jbinit method jULabel5.setToolTipText(jULabel5.getModel().getCurrentRow().getAttribute("Name").toStri ng());
UNRESOLVED ISSUES
Creating Shuttle Component in Oracle ADF Tree Binding fails after opening in Dialog more than once
August 31, 2009 Symptom: A Tree bound to an ADF view opens in a modal dialog from a button event. The first time the tree opens in the dialog it is bound to its view object but the second time it fails and nodes appear as $place_holder$ It turns out that this is caused by the release of the DATACONTROL in the automatically generated code for launching the dialog
SOLUTION: Once I've commented these two lines out I was able to re-open the dialog with the ADF bound tree showing correctly! SOLUTION2: Rather than using the auto-generated code use the following: exitButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { artifactManager.getPanelBinding().release(DCDataControl.REL_ALL_REFS);/ / rather than REL_VIEW_REFS); dialog.dispose(); } }); This does appear to resolve the issue of the tree becoming unbound when it is launched from an action that can be found in several panels and/or the application menu. Currently it's applied to the ActionBrowseBifArtifacts (Biofiler Object Manager GUI Dialog). Now this does not get us completely out of the weeds. It turns out that modal dialogs that get opened from within the original dialog are indeed also uncoupled. So I will need to find a way to probably release their REL_ALL_REFS upon exiting the main dialog. If you do it before they become immediately unbound PREFERRED SOLUTION It seems that DCDataControl.REL_ALL_REFS together with an adaptation of the multipleform instructions resolves many of the problems. See MDI ADF Forms
[SEP-2-2009] Additional complications arise once the same form is opened repeatedly. It seems to be a design issue on how MDI forms are bound in ADF. See this article for more details. At any rate, even after applying some of the recommendations I still have trouble with the tree loosing its 'binding context(?)'. NEW ISSUE: Using the approach described in the article, selecting nodes on the tree appears to generate multiple copies after the form is opened several times (One for each time the form is opened!)
Finally if the form is launched from yet another form (such as the navigation tree) a JBO exception is thrown and the tree becomes un-bound!
JBO-35001
Some solution to a somewhat similar problem (but not exactly). It could be that this is due to the fact that we have two separate action objects accessing the same view object. In some case it might be possible to share a single action object. Also see this thread from OTN Forum (JBOException 35001). Also see some ADF recommendations
JBO-27022 [OCT-08-2010] JBO-27022: Failed to load value at index n with java object of type X In several cases I have found this to be a JDev bug, where even though an attribute is added to a ViewObject (lets say from the entity object), the addition is not propagated correctly to the VO source code. Check the SelectList attribute of the <ViewObject> definition to check whether the offending attribute is missing. If it is add it to the source code manually! Also check any VO extensions that may exist. In one case although the VO was updated (By JDev) correctly the extension was not!
JBO-2900 Trying to save ViewObject xml after edit receive JBO-2900 errors with the message 'cannot add an ancestor as a child node'. See workaround here. Recovery from Error Saving ViewObject XML file after Updates
JBO-25001
oracle.jbo.NameClashException: JBO-25001: Name _LOCAL_VIEW_USAGE_com_nibr_bdm_model_bpmn_BpmnPropertyView_BifOntologyByIsa 1 of object type View Object already exists Identify the offending view object. in BpmnPropertyView one of the View Accessors is BifOntologyByIsa1: Right click and <Find Usages> The search pointed to two instances used in the same view to provide LOV values To Resolve: Add a copy of the iterator with a differnt name and bound second property LOV to this.