I write solutions to the problems I can't find much about elsewhere on the Web, also some code/script snippets that are absolutely awesome and make my life easier. Will be glad if someone finds these posts interesting and helpful!

Monday, May 3, 2010

Accumulative sorting in ADF 10g tables

Just what the title says.

In an application there were a few tables filled with results of some queries. I haven’t yet figured out why, but it was of a high importance to the client that all the tables had accumulative sorting (like the order by clause in SQL). This requirement took me a while to figure out how to implement, whilst the solution was quite elegant and pretty small to write it here.

So, there’s a table with results of a search (the query really doesn't matter as the entire sorting solution will be implemented in the view layer)
<table value="#{bindings.CustomerSearch.collectionModel}" var="row"
rows="#{bindings.CustomerSearch.rangeSize}"
first="#{bindings.CustomerSearch.rangeStart}" banding="row"
bandinginterval="1"
emptytext="#{bindings.CustomerSearch.viewable ? 'No rows yet.' : 'Access denied.'}"
sortlistener="#{CustomersBean.onSort}">
<column sortproperty="Customerid" sortable="true"
headertext="#{bindings.CustomerSearch.labels.Customerid}">
<commandlink text="#{row.Customerid}"
onclick="return initiateReplacement(#{row.Customerid});"
shortdesc="Click to initiate customer replacement"/>
</column>
<column sortproperty="Surname" sortable="true"
headertext="#{bindings.CustomerSearch.labels.Surname}">
<outputtext value="#{row.Surname}"/>
</column>
<column sortproperty="Name" sortable="true"
headertext="#{bindings.CustomerSearch.labels.Name}">
<outputtext value="#{row.Name}"/>
</column>
<column sortproperty="Fathername" sortable="true"
headertext="#{bindings.CustomerSearch.labels.Fathername}">
<outputtext value="#{row.Fathername}"/>
</column>
<column sortproperty="Address" sortable="true"
headertext="#{bindings.CustomerSearch.labels.Address}">
<outputtext value="#{row.Address}"/>
</column>
</table>
view raw gistfile1.xml hosted with ❤ by GitHub

The whole trick is to set sortListener=”#{CustomersBean.onSort}” for the table. The backing code for this sortListener is found in the backing bean named CustomerBean.
import oracle.adf.view.faces.event.SortEvent;
import oracle.adf.view.faces.model.SortCriterion;
import oracle.adf.view.faces.component.core.data.CoreTable;
import java.util.List;
import java.util.ArrayList;
public class Customers {
public void onSort(SortEvent se) {
// get selected criterion
List sortList = se.getSortCriteria();
SortCriterion currentSortCriterion = (SortCriterion)sortList.get(0);
// get previously selected criteria
ArrayList persistentSortList = getCustomerTableSortCriteria();
// the rest of the code expands the sort criteria, applies the new criteria list to the table and saves the criteria list for the future
ArrayList updatedSortList = new ArrayList();
updatedSortList.add(0, currentSortCriterion);
for(Object o : persistentSortList) {
updatedSortList.add((SortCriterion)o);
}
CoreTable ct = (CoreTable)se.getComponent();
ct.setSortCriteria(updatedSortList);
setCustomerTableSortCriteria(updatedSortList);
}
public static final String CUSTOMER_TABLE_SORT_CRITERIA = "customerTableSortCriteria";
/* As the sort criteria returned at any call to the onSort method have request scope,
* I use a session scoped bean that I call viewScope (I clean it up every time the viewId is changed, meaning that user has entered another page) which in fact is a java.util.HashMap.
* The method getCustomerTableSortCriteria() returns the Object under key CUSTOMER_TABLE_SORT_CRITERIA stored in viewScope.
* Naturally instead of viewScope one can use any other session scoped bean to store the ArrayList of SortCriterion objects.
*/
private ArrayList getCustomerTableSortCriteria() {
ArrayList sortCriteria = (ArrayList)viewScope.get(CUSTOMER_TABLE_SORT_CRITERIA);
if(sortCriteria == null) {
sortCriteria = new ArrayList();
setCustomerTableSortCriteria(sortCriteria);
}
return sortCriteria;
}
private void setCustomerTableSortCriteria(ArrayList sortCriteria) {
viewScope.put(CUSTOMER_TABLE_SORT_CRITERIA, sortCriteria);
}
}
view raw gistfile1.java hosted with ❤ by GitHub

And that’s pretty much it. The table is now sortable accumulatively: you can hit customerid first, then name, then surname etc. the resulting table will look similarly to what produces SQL clause order by customerid, name, surname, etc.
Hope this helps.

“javax.faces.convert.ConverterException: Unsupported Model Type” in ADF 10g for selectManyCheckbox

There’s a mess with the selectManyCheckbox bindings: for both initial list items and selected items. ADF itself doesn’t support direct binding to a ViewObject, and that’s why, as Frank Nimphius said,
…you would need to use a managed bean to dispatch between the ADF binding layer and the multi select component.
Maybe it’s just me, but it took me a while to figure out how exactly to do it.


First off, the managed bean. It should contain fields with accessors for two lists: the initial options and the selected items.
ArrayList<javax.faces.model.SelectItem> initialListItems;
public ArrayList getInitialListItems() {
if(initialListItems == null) {
initialListItems = new ArrayList();
JUIteratorBinding paramIter =
(JUIteratorBinding)JSFUtils.resolveExpression("#{bindings.ParamView1Iterator}");
for(int i = 0; i < paramIter.getEstimatedRowCount(); i++) {
ParamViewRowImpl paramRow = (ParamViewRowImpl)paramIter.getRowAtRangeIndex(i);
SelectItem si = new SelectItem();
si.setValue(paramRow.getParamkey());
si.setLabel(paramRow.getParamvalue());
initialListItems.add(si);
}
}
return initialListItems;
}
java.util.List selectedItems;
public void setSelectedItems(List selectedItems) {
this.selectedItems = selectedItems;
}
public List getSelectedItems() {
return selectedItems;
}
view raw gistfile1.java hosted with ❤ by GitHub

The initialListItems will be bound to the multi-selection items element. Whilst the selectedItems list will contain the selected items from the initial list. The important thing is the datatype for the latter – a java.util.List! In fact it’s what provokes the “Unsupported Model Type” exception. This datatype hassle was actually the part that took me a real while to get it, I tried primitive array, ArrayList of String’s, Object’s and what not!
Now the only thing left is to bind both lists to the multi-selection component.
<af:selectManyCheckbox value="#{ManagedBean.selectedItems}">
<f:selectItems value="#{ManagedBean.initialListItems}"/>
</af:selectManyCheckbox>
view raw gistfile1.xml hosted with ❤ by GitHub

No more exceptions. That's it, hope it helps!

How to add showPopupBehavior to an ADF Rich Faces table column

I didn’t see the answer to that anywhere in the manual, so I had to experiment myself. Ofc I sort of figured out how to do it pretty easily. Still this tutorial can save someone’s time, I hope.
So, basically, what one can do to pop a dialog with ADF Rich Faces, is 2 simple steps:
1. Define a popup with a dialog in it
2. Set showPopupBehavior of the link or button to point to that popup.
<af:panelGroupLayout halign="center" layout="horizontal">
<af:panelFormLayout rows="1">
<af:commandButton text="#{msg.ADD}" id="add"
actionListener="#{CorpinfoBean.onAddAction}"
disabled="#{not empty CorpinfoBean.dirtyRows}"/>
<af:commandButton text="#{msg.DELETE}" id="delete">
<af:showPopupBehavior popupId="confirmDelete"
triggerType="action" align="afterStart"/>
</af:commandButton>
<af:commandButton text="#{msg.COMMIT}" id="commit"
actionListener="#{CorpinfoBean.onCommitAction}"/>
<af:popup id="confirmDelete">
<af:dialog title="#{msg.CONFIRM_DELETE_POPUP_TITLE}"
dialogListener="#{CorpinfoBean.dialogListener}">
<af:activeOutputText value="#{msg.CONFIRM_DELETE_POPUP_MESSAGE}"/>
</af:dialog>
</af:popup>
</af:panelFormLayout>
</af:panelGroupLayout>
view raw gistfile1.xml hosted with ❤ by GitHub

Pretty simple, isn’t it? Now the question is how to achieve the same behavior per row in a table. If we set the same showPopupBehavior popupId to a command object found in a column, it won’t work and JDeveloper will mark it as an erroneous syntax (“Reference Id confirmDelete not found”). Well, the trick is that the popup should be defined inside the column, and that’s actually it. Here’s a code snippet.
<table varstatus="rowStat" value="#{SchedulerBean.localCollectionModel}"
fetchsize="25" contentdelivery="immediate"
rows="#{SchedulerBean.localCollectionModel.rowCount}"
emptytext="#{msg.SCHED_EMPTY_TABLE}" rowselection="single" width="100%"
var="row" rowbandinginterval="0" binding="#{SchedulerBean.schedTable}"
id="schedTable">
<column headertext="#{msg.ACTIONS}" sortable="false"
inlinestyle="background-color:#{(not empty row[SchedulerBean.columns[6].label] and row[SchedulerBean.columns[6].label] eq 'RUNNING'? '#00ff00' :'')};"
width="#{set.ACTIONS_COLUMN_WIDTH}">
<panelformlayout rows="1">
<commandlink actionlistener="#{SchedulerBean.toggleStatus}"
id="toggleStatus">
<image source="/images/start.png"
rendered="#{empty row[SchedulerBean.columns[6].label] or row[SchedulerBean.columns[6].label] eq 'KILLED'}"/>
<image source="/images/stop.png"
rendered="#{not empty row[SchedulerBean.columns[6].label] and row[SchedulerBean.columns[6].label] eq 'RUNNING' or row[SchedulerBean.columns[6].label] eq 'ACTIVE'}"/>
</commandlink>
<commandlink>
<image source="/images/delete.png"/>
<showpopupbehavior popupid="deleteRow" triggertype="action"
align="startAfter"/>
</commandlink>
<popup id="deleteRow">
<dialog title="#{msg.CONFIRM_DELETE_POPUP_TITLE}"
dialoglistener="#{SchedulerBean.onDeleteConfirmation}">
<activeoutputtext value="#{msg.CONFIRM_DELETE_POPUP_MESSAGE}"/>
</dialog>
</popup>
</panelformlayout>
</column>
</table>
view raw gistfile1.xml hosted with ❤ by GitHub

The method in the backing bean looks like:
public void onDeleteConfirmation(oracle.adf.view.rich.event.DialogEvent de) {
if(de.getOutcome().equals(oracle.adf.view.rich.event.DialogEvent.Outcome.ok)) {
deleteSelectedRow();
FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(FacesContext.getCurrentInstance(), "", "scheduler");
}
}
view raw gistfile1.java hosted with ❤ by GitHub

The navigation part is intended to refresh the page (there’s a global rule in the faces-config.xml for outcome “scheduler” to navigate to this page), because dialog handler doesn’t do it automatically with JSF controller.
Hope this helps!

How to make the Oracle Application Server 10g run as a Windows service

A problem that recently has been bugging the project I’m currently working on, was to make the Oracle Application Server 10g run as a service, so that it would be available without a user logging in and starting it up manually. We solved it with the use of an open source middleware project called JavaService, a pretty neat tool. Find below how.

Requirements:
Windows machine (the guide was tested with a vanilla Windows 2003 server installation)
Oracle Application Server 10g
The latest version (today 2.0.10) of JavaService

Actions:

Let’s say JavaService is unzipped to C:\JavaService-2.0.10
Then from the JavaService-2.0.10 directory execute:
rem To install the service with name OAS:
set ORACLE_HOME=C:\product\10.1.3.1\OracleAS_1
set CP=%ORACLE_HOME%\j2ee\home\oc4j.jar;%ORACLE_HOME%\jlib\startupconsole.jar;%ORACLE_HOME%\opmn\lib\optic.jar;%ORACLE_HOME%\lib\xmlparserv2.jar
javaservice -install OAS %ORACLE_HOME%\jdk\jre\bin\client\jvm.dll -Djava.class.path=%CP% -DORACLE_HOME=%ORACLE_HOME% -start oracle.appserver.startupconsole.view.Runner -params start -stop oracle.appserver.startupconsole.view.Runner -params stop -current %ORACLE_HOME%\j2ee\home\ -description "Oracle Application Server Service"
(in case the memory settings are not met (they are supposed to be taken from %ORACLE_HOME%\opmn\config\opmn.xml) add these 2 parameters
-XX:MaxPermSize=256m -Xmx256m right after the Java dll path)
rem To uninstall the service with name OAS:
javaservice -uninstall OAS
rem To view the configuration for installed Java service with name OAS:
javaservice -queryconfig OAS

Now the service OAS can be found among other services.

Should be also mentioned, that the Application Server better be stopped by the time you attempt to start the service.

Hope this helps.

Global validation for a Date input field in ADF 10

Say you have a table with a date column and you have created a view accessing it. Now try to insert an abnormal date into the dateInput field on the page (something like 1/1/111111111), commit the changes, check the value in the DB and you’ll get what I’m talking about here. I had queried a question on the OTN forum regarding this and the only answer I got was from Frank Nimphius to
configure a value change listener on the date field and verify that the provided value is valid, If not call FacesContext.renderResponse to ignore the request
this is a solution, but well, it requires more or less to apply it to each dateInput field on every single page. The other way around that I finally used, was to override the EntityImpl setAttributeInternal method like the following:
protected void setAttributeInternal(int attributeNumber, Object attributeValue) {
if (attributeValue instanceof oracle.jbo.domain. Date) {
java.util.Date value = ((oracle.jbo.domain.Date)attributeValue).getValue();
GregorianCalendar gc = new GregorianCalendar();
java.util.Date inAHundreedYears = new java.util.Date(new GregorianCalendar(gc.get(Calendar.YEAR) + 100, gc.get(Calendar.MONTH), gc.get(Calendar.DAY_OF_MONTH)).getTimeInMillis());
if (value.after(inAHundreedYears)) {
System.err.println("Invalid date provided (" + value + ")");
}
else {
super.setAttributeInternal(attributeNumber, attributeValue);
System.out.println("Set date " + value);
}
}
else {
super.setAttributeInternal(attributeNumber, attributeValue);
}
}
view raw gistfile1.java hosted with ❤ by GitHub

which actually just checks that the Date attribute provided is of a logical range.


This method has to be part of a class extending oracle.jbo.server.EntityImpl and obviously the entity implementation for that table with a date column must extend the new EntityImpl class.


That's it. Hope this helps!

Oracle database schema data removal

In my previous post I mentioned a script I used to drop everything from the schema, and I thought it could come handy to someone, hence here it is:
SET SERVEROUTPUT ON SIZE 1000000
BEGIN
FOR cur_rec IN
(SELECT object_name,
object_type
FROM user_objects
WHERE object_type IN ('TABLE', 'VIEW', 'PACKAGE', 'PROCEDURE', 'FUNCTION', 'SEQUENCE', 'TRIGGER')
)
LOOP
BEGIN
IF cur_rec.object_type = 'TABLE' THEN
EXECUTE IMMEDIATE 'DROP ' || cur_rec.object_type || ' "' || cur_rec.object_name || '" CASCADE CONSTRAINTS';
ELSE
EXECUTE IMMEDIATE 'DROP ' || cur_rec.object_type || ' "' || cur_rec.object_name || '"';
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('FAILED: DROP ' || cur_rec.object_type || ' "' || cur_rec.object_name || '"');
END;
END LOOP;
END;
/
PURGE RECYCLEBIN;
view raw gistfile1.sql hosted with ❤ by GitHub

This script has been extensively tested on Oracle 10g for all kind of editions and seems to work awesomely!
Beware, this WILL REMOVE EVERYTHING FROM THE DATABASE SCHEMA FOR USER YOU HAVE CONNECTED AS; that is if you connect as sysdba, it will most likely make the database useless.

Clear an iterator in ADF 10g

The mission is to empty out an iterator without setting its Refresh attribute to “never” and consequentially executing it manually. What I ended up doing, when I needed this once, was adding a parameter to the query WHERE clause (something like “and 1 = nvl(:param, 1)”) and invoking it with a zero when I needed it empty. Apparently there is an easier way.
Say the iterator to clear is defined in the pageDef as:
<iterator id="City1Iterator" RangeSize="-1"
Binds="City1" Refresh="always"
DataControl="AppModuleDataControl"/>
view raw gistfile1.xml hosted with ❤ by GitHub

Then the code in the backing bean would look like:
javax.faces.context.FacesContext ctx = javax.faces.context.FacesContext.getCurrentInstance();
javax.faces.el.ValueBinding bind = ctx.getApplication().createValueBinding("#{data}");
oracle.adf.model.BindingContext bindingContext = (oracle.adf.model.BindingContext) bind.getValue(ctx); //resolve binding context
oracle.adf.model.binding.DCDataControl dataControl = bindingContext.findDataControl("AppModuleDataControl");//find data control by name (defined in DataBindings.cpx) from BindingContext
/*
* finally get the View Object instance which the iterator is bound to (see the attribute Binds in the iterator definition in the pageDef)
* then invoke the magic method executeEmptyRowSet on it
*/
((AppModuleImpl) dataControl.getDataProvider()).getCity1().executeEmptyRowSet();
view raw gistfile1.java hosted with ❤ by GitHub

That’s it. Hope this helps!

Service management in Windows

Go to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services in Registy Editor (regedit.exe under WINDOWS\system32), there lookup the service by name. Properties appear on the right of the tree.
One property that can be surely needed at some point is the location of the executable of a service. That’s the property ImagePath.
Whoever has set up an Oracle Database instance knows what I’m talking about here The story behind that, is that I made the mistake to run the script which drops everything from my scheme, whilst I had connected as sysdba Yeah, I know.. stupid. Well, not the first time Anyways, the thing is that during second set up, Oracle DB seems to highly dislike the previous instance and does not alter all the services correctly (oh, I just hope that it wasn’t left intentionally, because that would mean, the developers aren’t human beings). E.g. the OracleCSService (something to do with clustering) doesn’t shut down correctly and consequentially during the second set up the path doesn’t change. Here I had to change the ImagePath for it at the location HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\OracleCSService. As simple as that.
The truth be told, I still haven’t figured out how to properly set up a fresh instance after removing the previous. Sounds like a story for another post.

P.S. After quite a research I found a great post about a complete removal of the previous installation. Find it here. I tried the method described like a hundred times already and it seems to work perfectly.

Silently print Jasper reports

I’ve recently come across an issue with the project I’m working on regarding what the title says. The thing is that the users didn’t want to see any kind of dialogs prompting to print the report. Well, it was pretty easy after all and the class I wrote follows.
import net.sf.jasperreports.engine.JRExporter;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.JRPrintServiceExporter;
import net.sf.jasperreports.engine.export.JRPrintServiceExporterParameter;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
public class SilentPrint {
public void printReport(Vector jasperPrintList, String selectedPrinter) {
try {
//first make sure that requested printService exists
PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
PrintService printService = null;
for(PrintService ps : printServices) {
if(ps.getName().equals(selectedPrinter)) {
printService = ps;
break;
}
}
if(printService != null) {
JRExporter jrExporter = new JRPrintServiceExporter();
jrExporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST,
jasperPrintList); //tell jasper what reports to export
jrExporter.setParameter(JRPrintServiceExporterParameter.PRINT_SERVICE,
printService); //tell jasper to export the report to selected printService
jrExporter.setParameter(JRPrintServiceExporterParameter.PRINT_SERVICE_ATTRIBUTE_SET,
printService.getAttributes());
jrExporter.setParameter(JRPrintServiceExporterParameter.DISPLAY_PAGE_DIALOG,
Boolean.FALSE); //who doesn't like self-explanatory attributes!
jrExporter.setParameter(JRPrintServiceExporterParameter.DISPLAY_PRINT_DIALOG,
Boolean.FALSE);
jrExporter.exportReport();
}
else {
System.err.println("Printer " + selectedPrinter + " not found");
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}

And here’s a usage sample:
public void createReport(String[] jasperFileUrls, Map parameters, JRDataSource ds, String type,
String selectedPrinter) {
Vector jasperPrintList = new Vector(0);
for(String s : jasperFileUrls) {
try {
jasperPrintList.add(JasperFillManager.fillReport((JasperReport)JRLoader.loadObject(s), parameters, ds));
System.out.println("Added report: " + s + " to the printlist");
}
catch(JRException jre) {
System.err.println("Failed to add the report '" + s + "' due to exception: " +
jre.getMessage());
}
}
try {
printReport(jasperPrintList, selectedPrinter);
}
catch(Exception e) {
e.printStackTrace();
}
}
view raw gistfile1.java hosted with ❤ by GitHub

Hope this helps anyone.