Conditional display of menu items with xml menu in adf

In one of the application, we have used xml menu model to display menu items/ tabs.

We have a requirement to display/hide menu items based on the users and their roles, which is a generic kind of requirement in most of the applications.

For example if the logged in user has the manager role, we need to display Approvals tab, which should be hidden for other users who don’t have manager role.

But generally , when we got this kind of requirement, we first think how to do this in jsff/ managed bean using some code. But ADF supports an easy manner to achieve this, even without writing any java code for this kind of requirements. When we use xml menu model, we will get all the tabs info from menu.xml file. Here ADF supports to specify render conditions in the same xml file as part of tab information.

Here is the sample code to achieve this in menu xml file.

<itemNode id=”Approvals” label=”Approvals” action=”adfMenu_Approval” focusViewId=”/Approval”
rendered=”#{sessionScope.roles.contains(‘MGR’)}”/>

As part of the itemNode tag , I have specified the corresponding rendered condition with the role.

In the template/ jsff page where the menu items are referred, we need to specify the rendered property from the menu like below :

<af:navigationPane hint=”bar” id=”pt_np2″ value=”#{test_menu}” var=”menu”>
<f:facet name=”nodeStamp”>
<af:commandNavigationItem text=”#{menu.label}” id=”it” action=”#{menu.doAction}”  rendered=”#{menu.rendered}”
/>
</f:facet>
</af:navigationPane>

Thats all!!!

 

 

ADF 12C resolve component not registered for Active data issue

I came to strange error when I was working with Jdeveloper/ADF 12C.

Below are the details :

In a page, I have a search panel (af:query) and the results table to display for support employee records and Add new Employee button. In one column I need to display Employee Number as link. When we click on Add new Employee button or employee number link in table column, page should redirect to the Employee details page.

For the 1st selection, everything is working fine and page is navigating to the employee details page and corresponding record details are displayed.

When we come back to the search page and click on any employee number link or Add new employee button, in this case 12C is throwing a strange error saying Component is not registered for Active data , as shown below :

 

error2

I have tried many options to resolve this issue, but it seems the issue is with  framework in 12C version.

Though its showing the error message, its not impacting the application functionality anywhere. So,one option I tried to escape from this issue is to suppress this exception and not to display in the front end screen. As its not displayed in the front end,  we can continue with our navigation to the details page.

Now how to suppress this framework exception in 12C :

  1. create a folder named ‘services’ under <Application folder>\.adf\META-INF.
  2. Create a java class which extends ExceptionHandler and override the method handleException to catch that exception. I have done this way : 

    import javax.faces.context.FacesContext;
    import javax.faces.event.PhaseId;

    import oracle.adf.share.logging.ADFLogger;
    import oracle.adf.view.rich.context.ExceptionHandler;

    public class TestExceptionHandler extends ExceptionHandler {

    @Override
    public void handleException(FacesContext facesContext, Throwable throwable, PhaseId phaseId) throws Throwable {
    if (throwable != null) {
    String errorMessage = throwable.getMessage();
    // Temporarily handle ADF_FACES-60003:Component not registered for Active Data.
    if (errorMessage != null && errorMessage.startsWith(“ADF_FACES-60003”)) {
    logger.fine(errorMessage);
    } else {
    throw throwable;
    }
    }
    }
    }

  3. Go to services folder, create a document and enter ‘<package_name>.TestExceptionHandler’.  Now save that document as type “All Types” and in File name give’org.iaea.aips.portal.view.handlers.ExceptionHandler’. So the document should be saved with type EXCEPTIONHANDLER File like below:handler

Now we can see this services and document in JDeveloper also like below :

jdevs

Thats all. Now run the page, as the exception is caught, we can proceed with our navigation.

 

 

ADF Integration with BI publisher reports

In one of my applications, I came through a requirement where I need to download some report and display it as pdf/doc file.

Generally, If that file is available in the database table as blob , in our adf application we can direct map it to view object attribute and with little amount of java code we can download and display directly.

But in our scenario, we need to get the data dynamically from the database based on the given conditions and based on that data , report should be generated dynamically. To achieve this requirement, we have used BI publisher integration with ADF.

To achieve this, first we have to create xml template (document outline) and data definition template (the query to get the actual data to replace the outline). Once the templates are ready, we need to register them as part of XML Publisher Administrator responsibility (EBS applicaiton). xmlPublisher

While registering we need to give the code for template and data definition template to identify. Once they are registered we can check in the EBS application.

Now , its turn of our adf code to access/use that template/data template using the template codes. Here is the sample adf code we used to download BI publisher report as pdf file in ADF application.

Asusual in adf I have added fileDownloadListener tag like below :

<af:commandToolbarButton text=”Export to PDF” id=”cil8″>
<af:fileDownloadActionListener method=”#{backingBeanScope.bean.downloadReport}”
filename=”#{bindings.Report.inputValue}.pdf” />
</af:commandToolbarButton>

Code in the bean is like below :

public void downloadReport(FacesContext facesContext,
OutputStream outputStream) {

String template =”TEMPLATECODE”;

ByteArrayInputStream queryOutput = runDataTemplate(template,empId,null,”summary”); // based on empId the query in the data definition template will be executed and result will be considered as part of report

ByteArrayInputStream xslTemplate = readTemplate(template); // document outline will be generated based on the tempalte code.

// FOProcessor api is used to integrate the data coming from the query with the template document. This is also like predefined api we can use the code as is like our adf code.

FOProcessor processor = new FOProcessor();
processor.setData(queryOutput);
processor.setTemplate(xslTemplate);
ByteArrayOutputStream pdfotptstream = new ByteArrayOutputStream();
processor.setOutput(pdfotptstream);
processor.setOutputFormat(FOProcessor.FORMAT_PDF);
processor.generate();

// Once we have output stream with the generated pdf , we can use our normal download file code like below :

HttpServletResponse response = (HttpServletResponse)facesContext.getExternalContext().getResponse();
response.setContentType(“application/pdf”);
response.setHeader(“Content-disposition”, “attachment; filename=”+Report+”.pdf”);
byte[] resultOut = pdfotptstream.toByteArray();
outputStream.write(resultOut);
outputStream.flush();
facesContext.responseComplete();

}

// In this method data definition template will be read from the database and query will be executed based on empId and both will be merged as document.

public ByteArrayInputStream runDataTemplate(String dataSourceCode, String eventId, String partyId,String source) throws SQLException {
EmployAMImpl am = this.getEmployAMImpl();
ByteArrayInputStream resultOut = null;
ByteArrayInputStream xmlDataTemplate = readDataTemplate(dataSourceCode);
Connection conn = am.getDBTransaction().createCallableStatement(“select 1 from dual”, 1).getConnection();
DataProcessor _dataProcessor = new DataProcessor();
ByteArrayOutputStream resultOutStream = new ByteArrayOutputStream();
try {
_dataProcessor.setDataTemplate(xmlDataTemplate);
Hashtable parameters = new Hashtable();
parameters.put(“p_emp_id”, empId);
_dataProcessor.setParameters(parameters);
_dataProcessor.setConnection(conn);
_dataProcessor.setOutput(resultOutStream);
_dataProcessor.processData();
resultOut = new ByteArrayInputStream(resultOutStream.toByteArray());

} catch (Exception e) {
throw new JboException(e);
}

return resultOut;
}

// As the data definition template is stored in the database as blob, we can normal adf view object to read it from database like below: 

public ByteArrayInputStream readDataTemplate(String dataSourceCode) {
EmployAMImpl am = this.getEmployAMImpl();

GenericBlob blobDataTemplate = null;
ByteArrayInputStream resultOut = null;

try {

ViewObjectImpl vo = am.getDataTemplateVO();
vo.setNamedWhereClauseParam(“pDataSourceCode”, dataSourceCode);
vo.executeQuery();
Row row = vo.first();
if(row!=null) {
blobDataTemplate = (GenericBlob)row.getAttribute(“FileData”);
}

resultOut = new ByteArrayInputStream(blobDataTemplate.toByteArray());

} catch (Exception e) {
throw new JboException(e);
}
return resultOut;
}

// As the template is stored in the database as blob, we can normal adf view object to read it from database like below: 

public ByteArrayInputStream readTemplate(String templateCode) {
EmployAMImpl am = this.getEmployAMImpl();

GenericBlob blobDataTemplate = null;
ByteArrayInputStream resultOut = null;

try {
ViewObjectImpl vo = am.getTemplateVO();
vo.setNamedWhereClauseParam(“pTemplateCode”, templateCode);
vo.executeQuery();
Row r = vo.first();
if(r!=null) {
blobDataTemplate = (GenericBlob)r.getAttribute(“FileData”);
}
resultOut = new ByteArrayInputStream(blobDataTemplate.toByteArray());
// here we need to save in database

} catch (Exception e) {
throw new JboException(e);
}
return resultOut;
}

Thats all!!!!

 

 

Customize ADF query programmatically

By default ADF query panel takes all the parameters values and builds the criteria/ where clause. In some scenarios we need to override the criteria with some more conditions which are not possible from front end. Query panel itself is the single component which does not allow to customize.

In such cases we can get the view criteria and override the criteria programmatically  in 2 ways :

  1. By overriding the executeQueryForCollection method in the ViewObjectImpl class.
  2. By overriding the query listener method.

Here is the sample code for both the approaches :

  1. Overriding executeQueryForCollection :

In my sample i have input lov for locations , where user selects the location and click on search. For Input lov in view criteria, by default equals only will be allowed. But here requirement is to do with contains. For that I need to customize using the below code :

ViewCriteria[] vcs =getApplyViewCriterias(ViewCriteria.CRITERIA_MODE_QUERY);
if (vcs != null & vcs.length > 0) {
for (ViewCriteria v : vcs) {
ViewCriteriaRow vcr = (ViewCriteriaRow)v.first();
if (vcr != null) {
ViewCriteriaItem[] vcis = vcr.getCriteriaItemArray();
if (vcis != null && vcis.length > 0) {
for (int j = 0; j < vcis.length; j++) {
ViewCriteriaItem vci = vcis[j];
if(vci!=null) {
if(vci!=null && vci.getName()!=null && vci.getValue() !=null) {
if(“Itinerary”.equals(vci.getName())) {
vci.setOperator(JboCompOper.OPER_CONTAINS);
}}}}}}}

As we are overriding the view object impl method, it will be invoked automatically.So we no need write any coding to invoke this.

2. From bean query listener metthod :

Bu default when we drag n drop the view criteria as search panel (af:query tag will be created with default query listener and query operations listener).

public void filterSearchResults(QueryEvent queryEvent) {
DCIteratorBinding iter = (DCIteratorBinding)ADFUtils.findIterator(“EmpVOIterator”);
ViewObjectImpl vo = (ViewObjectImpl)iter.getViewObject();
QueryDescriptor qd = queryEvent.getDescriptor();
ConjunctionCriterion cc= qd.getConjunctionCriterion();

String id=null;
//access the list of search fields
List<Criterion> list= cc.getCriterionList();
//iterate over the attributes to find the required attributes
for (Criterion criterion : list) {
AttributeDescriptor attrDescriptor = ((AttributeCriterion)criterion).getAttribute();
if (attrDescriptor.getName().equalsIgnoreCase(“EmpName”) || attrDescriptor.getName().equalsIgnoreCase(“empNumber”)) {
if(id== null)
id= (String)((AttributeCriterion)criterion).getValues().get(0);
else
id+=”,”+(String)((AttributeCriterion)criterion).getValues().get(0);
}
}
vo.setNamedWhereClauseParam(“bindEmp”, id);
ADFUtils.invokeEL(“#{bindings.EmpVOCriteriaQuery.processQuery}”, new Class[] { QueryEvent.class },
new Object[] { queryEvent });
}

In this case I need to get selected value (emp ids ) from the empName lov or empNumber lov and need to filter the search results based on emp ids (which is passed as 1,2,3) as bind variable.

These are the sample approaches we followed in our scenarios.

Hope this will be helpful to some one having the same kind of requirement.

Using JSTL String functions in ADF EL expressions

Some times we may come through the requirement to validate the UI values conditionally. Generally we go for managed bean method and we will write java code to validate that conditions and data and return true/false values. We have one more simple approach to achieve this using JSTL functions in our ADF el expressions.

For this we need to use  xmlns:fn=”http://java.sun.com/jsp/jstl/functions&#8221; in our jsff/jspx page like below :

fnJSTL

When we use this for the first time in our application, it asks to add JSTL libraries to the application. When we click on warning icon , it will automatically add the libraries  (JSTL1.2)required.

We can use the functions in EL expressions like below :

rendered=”#{!fn:startsWith(bindings.EmployName.inputValue,’ABC’)}”

rendered=”#{fn:contains(bindings.EmployName.inputValue,’XY’)}”

rendered=”#{!fn:endsWith(row.Tag,’Y’) }”

styleClass=”#{fn:length(bindings.EmployName.attributeValue) gt 100 ?….

<af:outputText value=”#{fn:trim(fn:replace(row.TrxType,’RMA’,”))}” />

like this, we can use all the string functions like substring..etc using EL expressions in this way instead of going for managed bean java code.

Thats All!!!!!!

 

ADF: SSL Secure webservice invocation

In one of my applications, I came through a requirement where I need to invoke a web service which is secured through SSL  (implemented in .Net). This web service is from some external team and they have their own internal security.

To invoke this service from our adf application ((Jdev 11.1.1.7) and weblogic 11g server), we need to do the following steps :

I need to use JAVA SSL logic as below :

try {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}

public void checkClientTrusted(X509Certificate[] certs, String authType) { }

public void checkServerTrusted(X509Certificate[] certs, String authType) { }

}
};

SSLContext sc = SSLContext.getInstance(“SSL”);
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
/*
* They have 2 -way secure mechanism, 1st we need to retrieve token to access the service using the code below
*/

URL url = new URL(“https://TEST.org/connect/token&#8221;);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();

conn.setDoOutput(true);
conn.setRequestMethod(“POST”);
conn.setRequestProperty( “Content-Type”, “application/x-www-form-urlencoded” );
conn.setConnectTimeout(1000000);

String urlParameters = “grant_type=test_params&scope=testapi&client_id=test_param&client_secret=test”;
byte[] postData = urlParameters.getBytes(“UTF-8”);
conn.getOutputStream().write(postData);

BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer resp = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
resp.append(inputLine);
}
in.close();

JSONObject obj = (JSONObject)new JSONParser().parse(resp.toString());

we get the security token in obj.

Again below code to access the actual web service by sending the token as parameter :

try {

TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}

public void checkClientTrusted(X509Certificate[] certs, String authType) { }

public void checkServerTrusted(X509Certificate[] certs, String authType) { }

}
};

SSLContext sc = SSLContext.getInstance(“SSL”);
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
/*
* end of the fix
*/

URL url = new URL(“https://test/api/Request/GetByNumber/1700594&#8221;);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod(“GET”);
conn.setRequestProperty(“authorization”, “Bearer ” + token);
conn.setRequestProperty(“accept”, “application/json”);
conn.setRequestProperty(“cache-control”, “no-cache”);
int responseCode = conn.getResponseCode();
System.out.println(“\nSending ‘GET’ request to URL : ” + url);
System.out.println(“Response Code : ” + responseCode);

BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();

JSONObject obj = (JSONObject)new JSONParser().parse(response.toString());
String status = (String)obj.get(“Status”);
System.out.println(“status “+status);
JSONArray rooms = (JSONArray)obj.get(“Rooms”);

Iterator iterator = rooms.iterator();
while (iterator.hasNext()) {
JSONObject roomInfo = (JSONObject)iterator.next();

This JSON array is the sample code we have used in our application to get the data from the web service and to create rows and display in our adf table.

For this we need to set the proxy settings in Jdeveloper to bypass the security while running in jdev.

Preferences  –> Web Browser and Proxy –>

Check Http Proxy server and specify the proxy host details and exceptions.

This setting is fine while running in local jdev.

When we deploy this adf application on web logic, we got another issue related to weblogic security.

javax.net.ssl.SSLKeyException: FATAL Alert:BAD CERTIFICATE – A Corrupt and unuseable certificate was received.

To resolve this we need to do some configuration in weblogic admin console :

Environment –> Servers –> Integration/Admin server –> SSL –> Advanced –>

Hostname Verification : Custom Hostname verifier

Custom Hostname Verifier: weblogic.security.utils.SSLWLSWildcardHostnameVerifier

Like below :

SSLconfinWeblogic

Thats enough. We could invoke SSL secured webservice from ADF web application.

 

 

 

 

ADF: case insensitive filtering in af:table

We have default sorting and filtering options in adf table. But table filters are case sensitive by default. If we need to make the filters as case insensitive, we have simple option with properties. We no need to write any custom code to achieve this.

In properties we have filterFeatures attribute. For that we need to declare “caseInsensitive” like below :

 

Thats all. We got the case insensitive for adf table filters .

Search field as read only with ADF query panel

This blog is about how to make a search field as read only in adf query panel.

If we use custom search form, it will be easy to make a field as read-only from UI. But in my scenario I need to use view criteria only. Even though we have created a read only view object, in search panel fields will be displayed as updatable only. But it wont be easy to customize view criteria/query panel programmatically.

We can achieve this thru the follwoing code in CSS file:

af|query

{

-tr-criterion-field-readOnly-type:readonly;

}

querycss

And along with this we need to make the corresponding search attrbutes as read only in view object.

queryResult

Thats all!!!

Remove the Error message popup for af:inputDate

Sometimes, we may have the requirement from the client like: we should not show the error message popup for the inputDate and we should not show the predefined adf faces error message for the invalid dates.

How to deal such scenarios in simple way :

Just create one css file and add the following :

.AFNoteWindowConeBorder{
display: none
}

.AFNoteWindowConeBL{
display: none
}

.AFNoteWindow{
display: none
}

af|inputDate:error::content{
border-color:#ADB6C4 #E5E8EE #E5E8EE #ADB6C4;
border-width: 1px;
}
af|message::detail{
color:Orange;
}
af|message img
{
display:none;
}
af|message::type
{
display:none;
}

JSPX Code :

<af:inputDate label=”Label 1″ id=”id1″ >
<af:convertDateTime pattern=”dd/MM/yyyy” dateStyle=”short” type=”date”
messageDetailConvertDate=”Please give correct date”/>
</af:inputDate>
<af:message id=”m1″ for=”id1″/>

We need to configure css as part of trinidad-skins…and follow allt he configuration required for adf skinning.

Run now:Image

Hope this helps…

ADF tree table toggling

we had requirement of toggling adf tree .
while googling i found one link(https://community.oracle.com/thread/1005268?tstart=0
) for this.
this is working for 2 level tree table(DepartmentstoEmployees)
but in my use case,I have three level tree(LocationtodepartmenttoEmployees).
in fact we were not using ADF BC.rather we were using POJO clasess.
so i thought i would share this sollution through my blog.

I have created aviewlinks —>EmployeesForDepartments and DepartmentsForLocation
i dragged DepartmentsForLocation as tree and attached Employees to Departments Iterator
so this will form 3 level Tree in UI.
for example
Location
Departments
employees

to achieve tree table toggling..i have added rowdisclosure method as below
public void toggle(RowDisclosureEvent event) {

UIXTree tree = (UIXTree)event.getSource();

int dept = tree.getDepth();
// click on root node only; if click on child node -> do nothing

if (dept > 0) {

return;

}
RowKeySet discloseRowKeySet = tree.getDisclosedRowKeys();

RowKeySet addedRowKeySet = event.getAddedSet();

Iterator addedRowKeySetIter = addedRowKeySet.iterator();
discloseRowKeySet.clear();

if (addedRowKeySetIter.hasNext()) {

Object firstKey = addedRowKeySetIter.next();

discloseRowKeySet.add(firstKey);

tree.setSelectedRowKeys(addedRowKeySet);

tree.setRowKey(firstKey);

FacesCtrlHierNodeBinding node = (FacesCtrlHierNodeBinding)tree.getRowData();

if(node !=null && node.getParent()!=null)  {

Key key = node.getParent().getRowKey();

java.util.List list = new ArrayList();

list.add(key);
discloseRowKeySet.add(list)   ;

}

AdfFacesContext adfFacesContext = null;

adfFacesContext = AdfFacesContext.getCurrentInstance();

adfFacesContext.addPartialTarget(treetable.getParent());

}

System.out.println(discloseRowKeySet);

System.out.println(tree.getDisclosedRowKeys());

}