Wednesday Mar 26, 2014

File Upload Using WSDC for an MTOM WebService

Use Case:

In this usecase I will explain about how to upload file in WSDC to the target WebService from the Backing Bean 

What is MTOM ?

MTOM or  Message Transmission Optimization Mechanism is the W3C Message Transmission Optimization Mechanism, a method of efficiently sending binary data to and from Web services.

Implementation:

Lets start by creating a Java WebService with MTOM Enabled to allow transfer of files as shown below.

In the service provide a location where the files have to be saved else create a folder called mytemp in C drive if planning to use the code below. 

package project1;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import javax.activation.DataHandler;

import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;

import javax.jws.WebService;

import javax.xml.bind.annotation.XmlMimeType;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.MTOM;

@WebService(serviceName = "UploadService")
@MTOM
public class UploadService {
    public UploadService() {
        super();
    }

    @WebMethod
    @Oneway
    public void fileUpload(@WebParam(name = "arg0") String fileName, @XmlMimeType("application/octet-stream") @WebParam(name = "arg1") DataHandler data) {
    try {
       
 OutputStream os = new FileOutputStream("C:\\mytemp\\"+fileName);
        InputStream is = data.getInputStream();          
                 byte[] buffer = new byte[1024];
                 int bytesRead;
                 //read from is to buffer
                 while((bytesRead = is.read(buffer)) !=-1){
                     os.write(buffer, 0, bytesRead);
                 }
                 is.close();
                 //flush OutputStream to write any buffered data to file
                 os.flush();
                 os.close();
        
    }
    catch (Exception e)
    {
            throw new WebServiceException(e);
    }
    }
}

Now deploy the Service to Integrated WLS.

Create an ADF Fusion Application and create WebService Data Control from the New Gallery using the WSDL URL of the service deployed. After creating the DC , expand the DC palette where we can observe that the FileUpload method takes 2 arguments , the first argument is the file Name which String type and the Second Argument is Object type 

DCPalette

Now in the ViewController, create a jspx page with Backing Bean or we can create the backing bean separately and bind to the page.

In the Backing Bean add the following Two Methods

// This Method is used to read the file provided by the user and pass it to the DC
public void fileUploaded(ValueChangeEvent event)
     {
       UploadedFile file = (UploadedFile) event.getNewValue();
       if (file != null)
       {
        FacesContext context = FacesContext.getCurrentInstance();
        FacesMessage message = new FacesMessage("Successfully uploaded file " + file.getFilename()+" (" + file.getLength()+" bytes)");
        context.addMessage(event.getComponent().getClientId(context), message);
        DCBindingContainer bc = (DCBindingContainer) BindingContext.getCurrent().getCurrentBindingsEntry();
        OperationBinding op = bc.getOperationBinding("fileUpload");
            try {
                //Convert the file Data into Byte Array and then pass this to the DC
                byte[] buffer = readFully(file.getInputStream());
                if(op!=null){
                   Map param = op.getParamsMap();
                   param.put("arg0",file.getFilename());
                   param.put("arg1",buffer);
                    op.execute();
                }
            } catch (IOException e) {
            }
            
       }
     }
    public byte[] readFully(InputStream input) throws IOException
    {
        byte[] buffer = new byte[8192];
        int bytesRead;
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        while ((bytesRead = input.read(buffer)) != -1)
        {
            output.write(buffer, 0, bytesRead);
        }
        return output.toByteArray();
    }

In jspx page, Drag and drop the ADF Input File Component  and bind the Value Change Listener component to the fileUploaded() Method given above

pageSource

Go to the page definitions of the page, in  the bindings add the Method Action as shown below


Once after adding the method action, drag &drop a button onto the page for submit.Now when the jspx page is rendered , browse and select a file then click on Submit button 

createREST

Now we can observe that file gets transferred to the location mentioned in the Service



Thursday Feb 27, 2014

Consuming Async SOA in a WebService Proxy

Consider a scenario where an application is built using SOA Async processes and needs to be consumed in a WebService Proxy. In this blog, we will be demonstrating how to implement this use case. To achieve this, we will follow a two step process:

  1. Create an Async SOA BPEL process.
  2. Consume it in a WebService Proxy.

Pre-requisite:

 Jdeveloper with SOA extension installed.

Steps:

To begin with step 1, create a SOA Application and name it SOA_AsyncApp.

This invokes Create SOA Application wizard. In the wizard, choose composite with BPEL process in Step 3.


As we are going to build an async processes, in Create BPEL Process dialog, choose Asynchronous BPEL Process from Template drop-down. We will be using BPEL 1.1 Specification for our process. Also, make sure the process is exposed as a SOAP service.


We can see the two one-way operations listed in the process wsdl as it is an async process:

Drag and drop assign activity from Components onto the SOA Composite.


Edit the assign activity to insert a new rule from input variable payload to output variable payload. Click on the Plus symbol to add the rule.

Now, the Async service setup is complete and the project containing Async process can be deployed to a SOA server. With this, we are done with Step 1 of our use case.

To create a proxy for this service, create a custom Application SOA_AsyncProxy. Invoke Create Web Service Proxy wizard and in Step 2 of wizard, give WSDL of the service developed in Part1 of this blog.  In Step 3, check ‘Generate As Async ’ checkbox and give Callback Package name CallBackPackage.

In step 5, make sure generate async method radio button is selected and finish the wizard.

After proxy is generated, run the Callback Service from under the package CallBackPackage. This service will listen to the response from the asynchronous service. Give target URL of Callback Service in proxy class and call the process with appropriate input value.


Run the proxy. Following sequence of actions will take place after proxy is run:

1) The proxy will invoke the SOA process asynchronously and continue with other tasks.

2) SOA async service will store the request and send receipt confirmation (202) to the proxy.

3) The service processes the request and acting as a proxy for Callback Service, sends the response back to the Callback Service.

      4) Callback service, deployed on the proxy side receives the response and sends a confirmation to the async SOA service (202).

After the callback service receives the response, it has to map the response to the request. This is achieved using WS-Addressing Headers. The proxy must set two fields to establish the correlation: 1) ReplyTo address (address of the Callback Service) and 2) A unique Id to identify the request.

Async behavior can be verified by logging in to EM console and examining the flow instance of BPEL Process.


The correct payload in the callbackProxy confirms that async operation has been executed successfully.

Thursday Feb 13, 2014

ADF Desktop Integration Server Logging

About

In my last blog, I have discussed on how to enable logging on ADFdi client. Here let us see on how to enable server-side logging to obtain detailed error stack.

Adding log-handler and logger for desktop integration

In <JDEV_HOME>\jdeveloper\system<version>\DefaultDomain\config\fmwconfig\servers\DefaultServer\logging.xml file, add log-handler for desktop integration by copying below code inside <log-handlers>.

<!-- The log handler for ADFdi -->
<log_handler name="oracle-adf-desktopintegration-logging-handler" class="oracle.core.ojdl.logging.ODLHandlerFactory">
<property name="path" value="../log/adfdi/msglogging"/>
<property name="maxFileSize" value="10485760"/>
<property name="maxLogSize" value="104857600"/>
<property name="encoding" value="UTF-8"/>
</log_handler>

Note: Provide valid value for path property.

Loggers can be added for below packages

  • All ADF Desktop Integration server logic: name = oracle.adf.desktopintegration
  • ADF Desktop Integration HTTP filter: name = oracle.adf.desktopintegration.filter

oracle.adf.desktopintegration: This package generates logs for all Servlet logic(DIRemoteServlet) and Download filter logic(DIExcelDownloadFilter) of ADFdi.

In logging.xml, inside <loggers>, comment the existing logger for oracle.adf.desktop.integration.
Eg: <!--logger name='oracle.adf.desktopintegration' useParentHandlers='true'/-->

Add new logger by copying below code inside <loggers> tag.

<!-- The logger for ADFdi -->
<logger name="oracle.adf.desktopintegration" level="INFO">
<handler name="oracle-adf-desktopintegration-logging-handler"/>
</logger>

Different Levels:

INFO : Information, warning and error messages will be logged into the log file present in the specified path.
WARNING: Only warning and error messages will be logged.
ERROR: Only error messages will be logged.
FINE/FINER/FINEST: More detailed log messages can be obtained by setting these levels.

oracle.adf.desktopintegration.filter: This package generates logs only for Download filter logic(DIExcelDownloadFilter) of ADFdi.

In logging.xml, inside <loggers>, comment the existing logger for oracle.adf.desktop.integration.
Eg: <!--logger name='oracle.adf.desktopintegration' useParentHandlers='true'/-->

Add new logger by copying below code inside <loggers> tag.

<!-- The logger for ADFdi -->
<logger name="oracle.adf.desktopintegration.filter" level="FINE">
<handler name="oracle-adf-desktopintegration-logging-handler"/>
</logger>

Different Levels:

INFO: Logs only two messages- one at startup and one at termination of Excel Download Filter.
FINE/FINER/FINEST: Detailed logs can be obtained by setting these levels.

After adding loggers or changing levels for loggers, weblogic server has to be restarted for changes to get effected.
To configure loggers while the application is running in Integrated Weblogic server, see the "Using the ADF Logger" section in Developing Fusion Web Applications with Oracle Application Development Framework.

Tuesday Feb 04, 2014

HTTP Basic Auth in REST Client

When a web service is secured using basic authentication, the client can access the web service only after providing the appropriate credentials.

In this blog, I take you through the code which will enable the client to access such a service.

Pre-requisites:

Use JDeveloper 12c  version or above.

Creating the Service:

Let us start by creating a REST service and securing it with basic authentication. 

For creating the service, please refer the section "Creating the service" in blog.

Basic Authentication setup:

Once the REST service has been created, in order to secure it using basic authentication, follow the below steps:

Open the web.xml file. At the end of the file, add the lines:

<login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>myrealm</realm-name>
    </login-config>
    <security-constraint>
        <web-resource-collection> 
           <web-resource-name>MyResource</web-resource-name> 
           <url-pattern>/*</url-pattern> 
        </web-resource-collection> 
        <auth-constraint> 
           <role-name>MyRole</role-name> 
        </auth-constraint> 
    </security-constraint>
    <security-role> 
        <role-name>MyRole</role-name> 
    </security-role> 
</web-app>

Next, you need to create a weblogic.xml file. For this, go to New Gallery -> General -> Weblogic Deployment Descriptor.

weblogic xml file

On clicking Ok, another popup appears. Under Select Descriptor, select weblogic.xml and click Next. Select the deployment descriptor version in accordance with your weblogic version and then click Finish. This will generate a weblogic.xml file under WEB-INF. Open weblogic.xml file and select 'Security' from LHS. Under 'Security Role Assignments', add the Role Name 'MyRole', and add the Principals 'Administrators' as shown.

Configuring weblogic.xml file

At this point, your web service has been secured using basic authentication mechanism. Run the service and copy the generated WADL URL. This will be used to create the proxy for the service.

Accessing the service through REST Client:

First, to create the REST Client, create a new Custom Project.Let us name it ClientProj. Right click on the Project and invoke the New Gallery window. Under Business Tier -> Web services category, select REST Client and Proxy. Provide the generated WADL URL and click Next.

A prompt will appear asking for username/password.

Credential pop up

Provide the Administrator username and password that you would use to log into the WLS Console.On providing the correct credentials and clicking on Finish, the client class gets generated with some in-built code. Within the class 'Localhost_EmployeeServiceEmployeeProjContextRootResourcesClient.java' auto generated for you, write the following lines:


public class Localhost_EmployeeServiceEmployeeProjContextRootResourcesClient {
    public static void main(String[] args) {
        Client client = Localhost_EmployeeServiceEmployeeProjContextRootResources.createClient();

        Localhost_EmployeeServiceEmployeeProjContextRootResources.Employeeproj localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj =
            Localhost_EmployeeServiceEmployeeProjContextRootResources.employeeproj(client);
        
        // add your code here
        client.addFilter(new HTTPBasicAuthFilter("weblogic","weblogic1"));
        System.out.println(localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.getAsXml(String.class));
    } 

Use the import as below:

import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;

We use the HTTPBasicAuthFilter provided by Jersey to pass the basic auth credentials. Refer link for more details.

Alternate method:

Alternately, within the public class Localhost_EmployeeServiceEmployeeProjContextRootResourcesClient.java auto generated for you, write the following lines:

public class Localhost_EmployeeServiceEmployeeProjContextRootResourcesClient {
    public static void main(String[] args) {
        Client client = Localhost_EmployeeServiceEmployeeProjContextRootResources.createClient();

        Localhost_EmployeeServiceEmployeeProjContextRootResources.Employeeproj localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj =
            Localhost_EmployeeServiceEmployeeProjContextRootResources.employeeproj(client);
        
        // add your code here
        Authenticator.setDefault(new MyAuthenticator());
        System.out.println(localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.getAsXml(String.class));
    }
    
    static class MyAuthenticator extends Authenticator {    
        public PasswordAuthentication getPasswordAuthentication() {
                
        return (new PasswordAuthentication("weblogic", "weblogic1".toCharArray()));
            }
        } 

Use the imports:

import java.net.Authenticator;

import java.net.PasswordAuthentication;

Here, in the code block above, we basically extend the Authenticator class, and override the getPasswordAuthentication method, in which we pass the username and password. (Replace username and password above with the credentials you provided to create the client class)

In the main method, we then invoke the setDefault method of the Authenticator class and pass our Authenticator class name. This method will set the credentials whenever a proxy or HTTP server asks for authentication. The next two lines are for invoking a method of the service.

If you run the client code by supplying incorrect credentials, you will get a 401 Unauthorized error.

Wednesday Jan 15, 2014

Consuming Query and Path Parameter in REST DC

Use Case:

In this blog I will explain on how to configure the REST DC when there are Query and Path Parameters present in the Service that is to be consumed.
The Query and Path parameters in the DC are exposed as method Input Parameters in the DC palette allowing the user for easy Drag and Drop.

JDeveloper Version : 12.1.2

Implementation:

Configuring Query Parameters

Service API Uri:

http://<Server>/SampleApp/resources/rest-deleteservice?name=’Abby’

Scenario Details:
In this Uri ‘name’ is a Query Parameter which will be used to delete a specific record, to expose this follow the below steps

1.    Invoke the Rest DC  and create the connection

2. In the Resources tab, enter the  remaining resource uri without providing the ‘?name=’  as shown below

3. In the Method Details tab select the delete method, and in the URL Parameters panel click on ‘+’ and enter the parameter details.
Provide an Default value which will be invoked whenever the service is loaded

NOTE:
Providing a Valid Default Value becomes crucial when there is a GET with Query Param for which you would like the XSD to be generated by the Jdeveloper itself. 

Configuring Path Parameters


Service Uri:
http://<ServerDetails>/sqlrest/CUSTOMER/7/


Scenario Details:
In the above case ‘7’ is an customer id value based on which the specific customer record will be fetched, so this customer id is dynamic so we will be discussing on how to expose this as an dynamic field in the REST DC


1.    Invoke the Rest DC and create the Connection with Server details as mentioned in the  Step 1 for the Query Parameter
2.    In the Resources Tab, enter the resource url  and select the Method type , provide an valid name for the method

Here we can observe that Instead of the Value 7 we have provided that field as ##CustomerId##, now click on Next.

3. In the Method Details page, the ##CustomerId## is exposed in the URL Attributes panel, provide the default value of the Customer Id  , Click Next 

4. Now In this case , I have not provided the Response XSD which will be auto generated by the Jdeveloper and the DC palette will look as shown below after completing the wizard

Wednesday Dec 18, 2013

Client Proxy Generation for REST style services

This blog mainly talks about generating a client for REST style web services. It begins by creating a REST style service and then consuming that service using some client code.

Pre-requisites:

Use JDeveloper 12c  version.

Creating the Service:

To start with, invoke the New Application wizard, and create a Custom application. Let us call the application EmployeeService and the Project, EmployeeProj. Next invoke the New Gallery window and create a Java class. Lets name the class Employee.java. [Refer Employee.java ].

Similarly, create another Java class and name it EmpService.java. [Refer EmpService.java]

Right click on EmpService.java and select Create RESTful Service. Under Configure HTTP methods, set the following:

Name

Type

Consumes

Produces

getEmpList

GET

Application/xml

addEmployee

PUT

Application/xml

Application/xml

deleteEmployee

DELETE

Under Configure Parameter: For name, select  Annotation - PathParam; Parameter - name

updateEmployee

POST

Application/json

Under Configure Parameter: For name, select  Annotation - PathParam; Parameter - name

Leave getEmp and addEmp methods. They will not be exposed.

createREST

Click on Finish. EmpService.java should now look like [FinalEmpService.java]. Now the web service is ready to use.

Run the web service and test the methods in HTTP Analyzer. Copy the generated WADL URL.

WADL URL

Creating the REST Proxy:

Next, to create the REST proxy, follow the steps mentioned below:

Create a new Custom Project. Let us name it ClientProj.  Right click on the Project and invoke the New Gallery window. Under Business Tier -> Web services category, select RESTful Client and Proxy.

In the Create REST Proxy Client dialog that appears, enter the WADL URL generated on running the service and click on Finish. 

The generated client will consist of some in-built code.

The auto generated code consists of 2 important public classes -

Localhost_EmployeeServiceEmployeeProjContextRootResourcesClient.java and 

Localhost_EmployeeServiceEmployeeProjContextRootResources.java.

The former class basically consists of a main() method using which we can readily invoke the REST service methods. The latter, i.e., Localhost_EmployeeServiceEmployeeProjContextRootResources.java consists of all the internal logic involved in invoking the methods and handling data.

This class has various methods which allow access to the web service methods. 

For example, to access the getEmpList() method of the service, normally, the code would look like:

Client client = Client.create();

WebResource service = client.resource(UriBuilder.fromUri("http://localhost:7101/REST_Sanity-Project1-context-root/resources/employeeproj").build());

System.out.println(service.accept(MediaType.APPLICATION_XML).get(String.class));

But, now we can use the auto generated class and methods to invoke the GET method as follows:

Open Localhost_EmployeeServiceEmployeeProjContextRootResourcesClient.java and add the following lines where it is written "//add your code here".

System.out.println(localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.getAsXml(String.class));

The put method invocation would look like:

localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.putXml("<?xml version = '1.0' encoding = 'UTF-8'?> <employee><id>2</id><name>Simon</name></employee>",String.class);

The delete method uses @PathParam in the service. So its invocation would be:

localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.name("Simon").deleteAs(String.class);

Here localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.name(..) returns an instance of the static Name class present within the Localhost_EmployeeServiceEmployeeProjContextRootResources class. Using that, you can then access the methods within the Name class.

Similarly, the post method can be invoked as:

localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.name("Test").postAsJson(String.class);

Below is the complete main method:

public class Localhost_EmployeeServiceEmployeeProjContextRootResourcesClient {

public static void main(String[] args) {

Client client = Localhost_EmployeeServiceEmployeeProjContextRootResources.createClient();   Localhost_EmployeeServiceEmployeeProjContextRootResources.Employeeproj localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj = Localhost_EmployeeServiceEmployeeProjContextRootResources.employeeproj(client);

// add your code here

System.out.println("Put a new employee:" + localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.putXml("<?xml version = '1.0' encoding = 'UTF-8'?> <employee><id>2</id><name>Simon</name></employee>", String.class)); localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.name("Simon").deleteAs(String.class); System.out.println("Delete new employee:"+localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.getAsXml(String.class)); System.out.println("After Post:" + localhost_employeeserviceemployeeprojcontextrootresourcesemployeeproj.name("Test").postAsJson(String.class));

}

[Refer Localhost_EmployeeServiceEmployeeProjContextRootResourcesClient.java ].

On running the client, the following output will be seen in the log:

ClientOutput

The first line of the output corresponds to Put operation. In the put operation, we enter a new employee, Simon's details and the XML data after this operation is shown in the output.

The next line shows the result of delete operation after deleting the employee with name "Simon".

This is followed by the result of post operation which is displayed in Json format. 

Monday Dec 16, 2013

XSLT Transformation in REST DC

Use Case:
This blog explains about applying XSLT transformation on a REST Service while creating the Rest DataControl. Here I will be explaining on how the data is getting manipulated and is rendered by the REST DC when XSL is applied on it.

JDeveloper Version used : 12.1.2

Steps:
1. Consider an Scenario where an REST Service API is retuning an XML response as mentioned below

<?xml version = '1.0' encoding = 'UTF-8' standalone = 'yes'?>
<departments>
   <department>
      <deptId>1001</deptId>
      <deptName>HumanResource</deptName>
   </department>
   <department>
      <deptId>2002</deptId>
      <deptName>Finance</deptName>
   </department>
   <department>
      <deptId>3003</deptId>
      <deptName>Research</deptName>
   </department>
   <department>
      <deptId>5005</deptId>
      <deptName>Sales</deptName>
   </department>
</departments>


2. Lets create an XSD structure to contain two more new attributes e.g CUSTOMNAME of type String and UNIQUEID of type integer

<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xsd:element name="departments">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="department" maxOccurs="unbounded">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="deptId" type="xsd:integer"/>
              <xsd:element name="deptName" type="xsd:string"/>
              <xsd:element name="customUniqueId" type="xsd:integer"/>
              <xsd:element name="customName" type="xsd:string"/>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

3. Now we have to create an XSL File to transform data , so that the values can be populated for the new attributes.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:ns2="http://project1/">
    <xsl:template match="departments">
        <departments>
          <xsl:for-each select="department">
              <department>
                  <deptName><xsl:value-of select="deptName"/></deptName>
                  <deptId><xsl:value-of select="deptId"/></deptId>
                  <customName><xsl:value-of select="concat('CUST_',deptName)"/></customName>
                  <customUniqueId><xsl:value-of select="deptId * 2"/></customUniqueId>
              </department>      
          </xsl:for-each> 
        </departments>
    </xsl:template>
</xsl:stylesheet>

4. In Jdeveloper, invoke the WSDC wizard, select the type as REST, in the wizard provide the name for the DC and create the connection.



5. In the Next Step of the wizard, provide the resource path and the method name for the operation



6. In the Method Details Step of the wizard provide the Cutsom XSD and  XSL file for the method as shown below



7.  Click Next and Complete the Wizard, then Expand the DC palette to find the Structure as shown below



8. Drag and Drop the departmentNode onto the page as an ADF table and Run the page


9. Here we can observe the value being populated for the customUniqueId and customName as per the definition given in the XSL file

Monday Nov 25, 2013

ADF Faces Layout Basics - New Whitepaper Released

As the title suggests, basics of layout components available in ADF Faces is covered in this whitepaper.

This paper can be accessed from this location : http://www.oracle.com/technetwork/developer-tools/adf/learnmore/adffaceslayoutbasics-2046652.pdf

In a nutshell, this paper talks about

  • different layout components available in ADF
  • their geometry management
  • example situations during which a particular component can be used
  • reverse engineering some of the popular websites and
  • solutions to few frequent layout scenarios

This paper will be very helpful for understanding different layout components offered by ADF Faces and also to use them effectively to build your website.

You can also browse through ADF Architecture Square Homepage to find this and other related whitepapers : http://bit.ly/adfarchsquare

Thursday Oct 17, 2013

Simple GET operation with JSON data in ADF Mobile

Usecase:

This sample uses a RESTful service which contains a GET method that fetches employee details for an employee with given employee ID along with other methods. The data is fetched in JSON format.

This RESTful service is then invoked via ADF Mobile and the JSON data thus obtained is parsed and rendered in mobile in a table.

Prerequisite:

Download JDev build JDEVADF_11.1.2.4.0_GENERIC_130421.1600.6436.1 or higher with mobile support. 

Steps:

Run EmployeeService.java in JSONService.zip. This is a simple service with a method, getEmpById(id) that takes employee ID as parameter and produces employee details in JSON format. 

Copy the target URL generated on running this service. The target URL will be as shown below:

http://127.0.0.1:7101/JSONService-Project1-context-root/jersey/project1

Now, let us invoke this service in our mobile application. For this, create an ADF Mobile application. 

Gallery

Name the application JSON_SearchByEmpID and finish the wizard.

appName

Now, let us create a connection to our service. To do this, we create a URL Connection. Invoke new gallery wizard on ApplicationController project. 

newGallery

Select URL Connection option.

urlConn

In the Create URL Connection window, enter connection name as ‘conn’. For URL endpoint, supply the URL you copied earlier on running the service. Remember to use your system IP instead of localhost. Test the connection and click OK.

conn

At this point, a connection to the REST service has been created.

Since JSON data is not supported directly in WSDC wizard, we need to invoke the operation through Java code using RestServiceAdapter. For this, in the ApplicationController project, create a Java class called ‘EmployeeDC’. We will be creating DC from this class.

javaclass

Add the following code to the newly created class to invoke the getEmpById method.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public Employee fetchEmpDetails(){

RestServiceAdapter restServiceAdapter = Model.createRestServiceAdapter();

restServiceAdapter.clearRequestProperties();

restServiceAdapter.setConnectionName("conn"); //URL connection created with this name

restServiceAdapter.setRequestType(RestServiceAdapter.REQUEST_TYPE_GET);

restServiceAdapter.addRequestProperty("Content-Type", "application/json");

restServiceAdapter.addRequestProperty("Accept", "application/json; charset=UTF-8");

restServiceAdapter.setRetryLimit(0);

restServiceAdapter.setRequestURI("/getById/"+inputEmpID);

String response = "";

JSONBeanSerializationHelper jsonHelper = new JSONBeanSerializationHelper();

try {

response = restServiceAdapter.send(""); //Invoke the GET operation

System.out.println("Response received!");

Employee responseObject = (Employee) jsonHelper.fromJSON(Employee.class, response);

return responseObject;

} catch (Exception e) {

}

return null;

}

Here, in lines 2 to 9, we create the RestServiceAdapter and set various properties required to invoke the web service. At line 4, we are pointing to the connection ‘conn’ created previously.

Since we want to invoke getEmpById method of the service, which is defined by the URL

http://IP:7101/REST_Sanity_JSON-Project1-context-root/resources/project1/getById/{id}

we are updating the request URI to point to this URI at line 9. inputEmpID is a variable that will hold the value input by the user for employee ID. This we will be creating in a while.

As the method we are invoking is a GET operation and consumes json data, these properties are being set in lines 5 through 7. Finally, we are sending the request in line 13.

In line 15, we use jsonHelper.fromJSON to convert received JSON data to a Java object. The required Java objects' structure is defined in class Employee.java whose structure is provided later. Since the response from our service is a simple response consisting of attributes like employee Id, name, design etc, we will just return this parsed response (line 16) and use it to create DC.

As mentioned previously, we would like the user to input the employee ID for which he/she wants to perform search. So, in the same class, define a variable inputEmpID which will hold the value input by the user. Generate accessors for this variable.

accessor

Lastly, we need to create Employee class. Employee class will define how we want to structure the JSON object received from the service. To design the Employee class, run the services’ method in the browser or via analyzer using path parameter as 1. This will give you the output JSON structure.

jsonOutput

Ours is a simple service that returns a JSONObject with a set of data. Hence, Employee class will just contain this set of data defined with the proper data types.

Create Employee.java in the same project as EmployeeDC.java and write the below code:

package application;

import oracle.adfmf.java.beans.PropertyChangeListener;

import oracle.adfmf.java.beans.PropertyChangeSupport;

public class Employee {

private String dept;

private String desig;

private int id;

private String name;

private int salary;

private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

public void setDept(String dept) {

        String oldDept = this.dept;

this.dept = dept;

propertyChangeSupport.firePropertyChange("dept", oldDept, dept);

}

public String getDept() {

return dept;

}

public void setDesig(String desig) {

String oldDesig = this.desig;

this.desig = desig;

propertyChangeSupport.firePropertyChange("desig", oldDesig, desig);

}

public String getDesig() {

return desig;

}

public void setId(int id) {

int oldId = this.id;

this.id = id;

propertyChangeSupport.firePropertyChange("id", oldId, id);

}

public int getId() {

return id;

}

public void setName(String name) {

String oldName = this.name;

this.name = name;

propertyChangeSupport.firePropertyChange("name", oldName, name);

}

public String getName() {

return name;

}

public void setSalary(int salary) {

int oldSalary = this.salary;

this.salary = salary;

propertyChangeSupport.firePropertyChange("salary", oldSalary, salary);

}

public int getSalary() {

return salary;

}

public void addPropertyChangeListener(PropertyChangeListener l) {

propertyChangeSupport.addPropertyChangeListener(l);

}

public void removePropertyChangeListener(PropertyChangeListener l) {

propertyChangeSupport.removePropertyChangeListener(l);

    }

}

Now, let us create a DC out of EmployeeDC.java.

createDC

 DC as shown below is created.

DC

Now, you can design the mobile page as usual and invoke the operation of the service. To design the page, go to ViewController project and locate adfmf-feature.xml. Create a new feature called ‘SearchFeature’ by clicking the plus icon.

feature

Go the content tab and add an amx page. Call it SearchPage.amx.

createPage

Call it SearchPage.amx. Remove primary and secondary buttons as we don’t need them and rename the header.

preview

Drag and drop inputEmpID from the DC palette onto Panel Page in the structure pane as input text with label.

inputText

Next, drop fetchEmpDetails method as an ADF button.

fetchButton

For a change, let us display the output in a table component instead of the usual form. However, you will notice that if you drag and drop Employee onto the structure pane, there is no option for ADF Mobile Table. Hence, we will need to create the table on our own.

To do this, let us first drop Employee as an ADF Read -Only form. This step is needed to get the required bindings. We will be deleting this form in a while.

form

finalPage

Now, from the Component palette, search for ‘Table Layout’. Drag and drop this below the command button. 

table tableStructure

Within the tablelayout, insert ‘Row Layout’ and ‘Cell Format’ components. Final table structure should be as shown below. Here, we have also defined some inline styling to render the UI in a nice manner.

<amx:tableLayout id="tl1" borderWidth="2" halign="center" inlineStyle="vertical-align:middle;"

width="100%" cellPadding="10">

<amx:rowLayout id="rl1" >

<amx:cellFormat id="cf1" width="30%">

<amx:outputText value="#{bindings.dept.hints.label}" id="ot7" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf2">

<amx:outputText value="#{bindings.dept.inputValue}" id="ot8" />

</amx:cellFormat>

</amx:rowLayout>

<amx:rowLayout id="rl2">

<amx:cellFormat id="cf3" width="30%">

<amx:outputText value="#{bindings.desig.hints.label}" id="ot9" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf4" >

<amx:outputText value="#{bindings.desig.inputValue}" id="ot10"/>

</amx:cellFormat>

</amx:rowLayout>

<amx:rowLayout id="rl3">

<amx:cellFormat id="cf5" width="30%">

<amx:outputText value="#{bindings.id.hints.label}" id="ot11" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf6" >

<amx:outputText value="#{bindings.id.inputValue}" id="ot12"/>

</amx:cellFormat>

</amx:rowLayout>

<amx:rowLayout id="rl4">

<amx:cellFormat id="cf7" width="30%">

<amx:outputText value="#{bindings.name.hints.label}" id="ot13" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf8">

<amx:outputText value="#{bindings.name.inputValue}" id="ot14"/>

</amx:cellFormat>

</amx:rowLayout>

<amx:rowLayout id="rl5">

<amx:cellFormat id="cf9" width="30%">

<amx:outputText value="#{bindings.salary.hints.label}" id="ot15" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf10">

<amx:outputText value="#{bindings.salary.inputValue}" id="ot16"/>

</amx:cellFormat>

</amx:rowLayout>

    </amx:tableLayout>

The values used in the output text of the table come from the bindings obtained from the ADF Form created earlier. As we have used the bindings and don’t need the form anymore, let us delete the form. 

tableImg

One last thing before we deploy. When user changes employee ID, we want to clear the table contents. For this we associate a value change listener with the input text box.

valChange

Click New in the resulting dialog to create a managed bean.

valChangeDialog managedBean

Next, we create a method within the managed bean. For this, click on the New button associated with method. Call the method ‘empIDChange’.

newMethod empIDChange

Open myClass.java and write the below code in empIDChange().

public void empIDChange(ValueChangeEvent valueChangeEvent) {

// Add event code here...

//Resetting the values to blank values when employee id changes

AdfELContext adfELContext = AdfmfJavaUtilities.getAdfELContext();

ValueExpression ve = AdfmfJavaUtilities.getValueExpression("#{bindings.dept.inputValue}", String.class);

ve.setValue(adfELContext, "");

ve = AdfmfJavaUtilities.getValueExpression("#{bindings.desig.inputValue}", String.class);

ve.setValue(adfELContext, "");

ve = AdfmfJavaUtilities.getValueExpression("#{bindings.id.inputValue}", int.class);

ve.setValue(adfELContext, "");

ve = AdfmfJavaUtilities.getValueExpression("#{bindings.name.inputValue}", String.class);

ve.setValue(adfELContext, "");

ve = AdfmfJavaUtilities.getValueExpression("#{bindings.salary.inputValue}", int.class);

ve.setValue(adfELContext, "");

}

That’s it. Deploy the application to android emulator or device. Some snippets from the app.

Snapshot1 Snapshot2

Monday Oct 07, 2013

XML Parsing in ADF Mobile

Usecase:

In this usecase, we have a web service that returns employee details(like name, designation, salary etc.) for an employee with given ID. In the mobile app, we will be calculating tax on the returned salary and displaying it to the user in a pop-up.

To do this, we will be parsing the returned XML to fetch the current salary and add a tax of 5% on it.

Here, we are assuming that tax is 5% of the current salary.

Steps:

Run EmployeeService.java in RESTApp.zip. Copy the target URL. 

Create a new ADF Mobile application. 

Name the application ‘XmlParsingSample’ and finish the wizard.

Create a URL Connection in the ApplicationController project.

Name the connection ‘conn’ and suppy the target URL copied earlier. Remember to use IP instead of localhost. Test the connection and click OK to close the wizard.

Since the service returns XML data, also create a Data control. This will be useful for designing the UI page. To create DC, invoke URL Service data control wizard in the ApplicationController project.

Test the URL connection in the last step and finish the wizard.

Now, let us create the AMX pages. Open adfmf-feature.xml present in ViewController project. Add a new feature ‘EmpFeature’ by clicking the plus icon as shown.

Go to the content tab and add an AMX page.

Call it EmpPage.amx. We don’t need the primary and secondary actions. So, uncheck them.

Drag and drop ‘id’ parameter from the DC palette onto the Panel Page of the Structure pane for this page.

Drag and drop a button from the component palette onto the page. Change the text of the button to ‘Get salary’.

As we want to invoke the service on click of this button, let us associate an ActionListener with this. Focus on the button and from the PI, select the arrow button for Action Listener propery and click Edit to bring up the below dialog. Click New to create a new managed bean.

Name the manged bean as  myBean and class as myClass.

Next, create a new method called calculateTax.

Go to myClass.java. In calculateTax method, write the code provided in the below table. Use the imports:

import oracle.adfmf.amx.event.ActionEvent;

import oracle.adfmf.dc.ws.rest.RestServiceAdapter;

import oracle.adfmf.framework.api.AdfmfContainerUtilities;

import oracle.adfmf.framework.api.Model;

import org.xmlpull.v1.XmlPullParser;

import org.kxml2.io.KXmlParser;

import java.io.Reader;

import java.io.StringReader;

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

28.

29.

30.

31.

32.

33.

34.

35.

36.

37.

38.

39.

40.

41.

42.

43.

44.

45.

46.

47.

48.

49.

50.

public void calculateTax(ActionEvent actionEvent) {

// Add event code here...

RestServiceAdapter restServiceAdapter = Model.createRestServiceAdapter();

restServiceAdapter.clearRequestProperties();

restServiceAdapter.setConnectionName("conn");

restServiceAdapter.setRequestType(RestServiceAdapter.REQUEST_TYPE_GET);

restServiceAdapter.addRequestProperty("Content-Type", "application/xml");

restServiceAdapter.addRequestProperty("Accept", "application/xml; charset=UTF-8");

restServiceAdapter.setRetryLimit(0);

Object emppIDObj = AdfmfJavaUtilities.evaluateELExpression("#{bindings.id.inputValue}");

String empIDString = emppIDObj.toString();

restServiceAdapter.setRequestURI("/byID?id="+empIDString);

String response = "";

try {

response = restServiceAdapter.send("");

System.out.println("Response received!");

String name = "";

int sal = 0;

int salAfterTax = 0;

KXmlParser parser = new KXmlParser(); //create a parser instance

Reader stream = new StringReader(response);

parser.setInput(stream);

parser.nextTag();

parser.require(XmlPullParser.START_TAG, null, "employee");

while (parser.nextTag() != XmlPullParser.END_TAG) { //loop through until you encounter an end tag

parser.require(XmlPullParser.START_TAG, null, null); //go to start tag

String tagName = parser.getName(); //get the name of the current tag

String tagValue = parser.nextText(); //get the value within the current tag

if (tagName.equals("name")) { //if current tag is ‘name’, assign its value to variable ‘name’

name = tagValue;

}

if (tagName.equals("salary")) { //if current tag is ‘salary’, calculate salary after tax

System.out.println("Salary:" + tagValue);

sal = Integer.parseInt(tagValue);

salAfterTax = (int)(sal - sal * 0.05);

}

parser.require(XmlPullParser.END_TAG, null, tagName); //go to end tag of current tag

//parse through all the tags

}

parser.require(XmlPullParser.END_TAG, null, "employee");

parser.next();

parser.require(XmlPullParser.END_DOCUMENT, null, null);

AdfmfContainerUtilities.invokeContainerJavaScriptFunction("EmpFeature", "navigator.notification.alert",

new Object[] { "Salary for employee " + name +

" is " + sal +

". After tax deduction, salary is:" +

salAfterTax, "", "Note", "OK!" });

} catch (Exception e) {

System.out.println("Exception is:" + e);

}

}

}

 Lines 3 to 15 set various properties required to invoke the web service. In lines 10 and 11, we are getting the value input by the user and converting that to String inorder to pass the parameter to the service. In line 20, we are creating a kXml parser instance. kXML is one of the core ADF Mobile libraries that provides API that you can use to parse XML. As the parser needs a Reader object, we are converting our services’ response into Reader in line 21 and passing it in line 22.

 The response returned by our service is of the form

<?xml version="1.0" encoding="UTF-8"?>
<employee>
<dept>HR</dept>
<desig>Officer</desig>
<id>2</id>
<name>Myra</name>
<salary>70000</salary>
</employee>

To parse this response, we are setting the parser to the first tag ‘employee’ in line 24. In lines 25 through 42, we are going through the XML elements. The comments are in-line for these lines. Finally, lines 43 through 47 are for displaying the pop-up. To display the pop-up, we are utilizing AdfmfContainerUtilities.invokeContainerJavaScriptFunction. For more information, see link.

As mentioned in the article, we make use of the javascript function which comes from phonegap-1.0.0.js. Hence, we need to include an entry to this javascript file in our AMX page. Put the below entry in EmpPage.amx above the panelPage.

<script type = "text/javascript" charset="utf-8" src="../../../www/js/phonegap-1.0.0.js"></script>

That’s it. Deploy the app to a device or an emulator. Some snippets from the final app. 

Enjoy coding! 

Thursday Aug 29, 2013

ADF Desktop Integration Client Logging

About
In general, when we encounter errors, we often tend to look at logs to get detailed information. Users of ADF Desktop Integration can also enable logging in both client-side and server-side. In this blog we will have a look on how to enable client-side logging.

Assumption
Let us assume an ADF application is created with Employees table using simple HR schema and ADFdi workbook is created and configured to use EmployeesView1 as ADF Table.

simple table-new.png

How to enable logging for all workbooks opened with installed client:

For Design time client, copy 'adfdi-excel-addin.dll.config' from "<JDEV_HOME>\jdeveloper\adfdi\bin\excel\samples to the directory mentioned under About ADF Desktop Integration dialog in Oracle ADF tab -> Properties tab -> Configuration property.

For Runtime client, copy 'adfdi-excel-addin.dll.config' from "<JDEV_HOME>\jdeveloper\adfdi\bin\excel\samples to the directory mentioned under About ADF Desktop Integration dialog in MyWorkbook tab -> Properties tab -> Configuration property and rename it as 'adfdi-excel-addin-runtime.dll.config'.

We will continue with the assumption that we have installed Design time client and hence will use adfdi-excel-addin.dll.config file.

In adfdi-excel-addin.dll.config file, we have an <add> tag inside <listeners> tag where we can set type, name, location and trace options of log file.
<add type="System.Diagnostics.DelimitedListTraceListener" name="adfdi-common-text-listener" initializeData="C:\temp\adfdi-common-excel.txt" delimiter="|" traceOutputOptions="ThreadId, ProcessId, DateTime"/>

type - determines type of logs. Logs can be obtained in 3 different formats:

  • System.Diagnostics.DelimitedListTraceListener - Default format. Logs will be obtained with the specified delimiter.
  • System.Diagnostics.XmlWriterTraceListener - Logs will be obtained in xml format.
  • System.Diagnostics.TextWriterTraceListener - Logs will be obtained in Text format.

name - name of the trace listener. Can be set to any value

initializeData - determines the path where the log file has to be generated. Valid path has to be mentioned.

delimiter - specified character is used as a delimiter when type is set to 'System.Diagnostics.DelimitedListTraceListener'. It is not required to be set for other types.

traceOutputOptions - determines the optional content of trace output. If no extra details are required, it can be set to "None"

Different levels of logging can be set by chaning the switchValue property inside <source> tag. By default it is set to Information and logging can be turned off by setting switchValue = Off

Once after setting switchValue save the config file. Open the adfdi workbook and click on Refresh Config button in Logging section of Oracle ADF ribbbon tab.

  • With switchValue = Information - Information, warning and error messages will be logged in the log file.
    Eg: In excel workbook, add an ADF Input Text into cell J5. Select the same cell (J5) and insert ADF Button Component. A warning will be displayed.
    In C:\temp\adfdi-commom-excel.txt log file, logs similar to below will be seen:
    ~~~~~~~~~~~~~~~~~~~~~~
    "adfdi-common"|Warning|3|"A component already exists at origin J5."||7284||"1"|"2013-08-23T10:28:15.2851735Z"||
    ~~~~~~~~~~~~~~~~~~~~~~
  • With switchValue = Warning - Warning and error messages will be logged into log file.
    Eg: Edit ADF Table properties by double-clicking any cell in table range, and open Edit Columns by clicking on button on RHS of Columns property. Select FirstName column and set the value of DynaminColumn property to True. Close all dialogs by giving OK and run the workbook.
    In C:\temp\adfdi-commom-excel.txt log file, logs similar to below will be seen:
    ~~~~~~~~~~~~~~~~~~~~~~
    "adfdi-common"|Warning|3|"ADFDI-07520: Unable to evaluate the expression '#{bindings.EmployeesView1.hints.FirstName.label}' in 'Sheet1.TAB429819356.FirstName.HeaderLabel'. Detail: "||6352||"1"|"2013-08-23T10:40:16.0642489Z"||
    ~~~~~~~~~~~~~~~~~~~~~~
  • With switchValue = Error - only error/exception messages will be logged into log file.
    Eg: Run the workbook. Click on Edit Options in MyWorkbook tab and enter an invalid webapproot (say add 1 at end). Click on OK and click Yes in Web App Root confirmation dialog. ConnectionFailedException will be seen.

    In C:\temp\adfdi-commom-excel.txt log file, logs similar to below will be seen:
    ~~~~~~~~~~~~~~~~~~~~~~
    "adfdi-common"|Error|2|"ADFDI-05530: unable to initialize worksheet: Sheet1
    ADFDI-00134: An attempt to connect to the web application has failed.
    ADFDI-00501: An unexpected status: 404 (NotFound) was returned from the server while requesting the URL: http://127.0.0.1:7101/BlogApplication-ViewController-context-root1/adfdiRemoteServlet

    ~~~~~~~~~~
    ConnectionFailedException: ADFDI-00134: An attempt to connect to the web application has failed.
    Source: adfdi-datamanager
    Stack:
    at oracle.adf.client.windows.datamanager.LoginHandler.GetAuthenticationMode()
    at oracle.adf.client.windows.datamanager.LoginHandler.Login()
    at oracle.adf.client.windows.datamanager.ADFBindingContext.LoginHandlerProxy.Login()
    at oracle.adf.client.windows.datamanager.ADFBindingContext.AttemptLogin(Boolean maybeHadSession)
    at oracle.adf.client.windows.datamanager.ADFBindingContext.SyncModel(BindingContainer bc, String contentType)
    at oracle.adf.client.windows.datamanager.BindingContainer.ReloadMetadata()
    at oracle.adf.client.windows.excel.runtime.DIWorksheet.InitializeComponents(Boolean isFirstLoad)
    at oracle.adf.client.windows.excel.runtime.DIWorksheet.Initialize(Boolean initializeUI)
    Inner:
    UnexpectedHttpStatusException: ADFDI-00501: An unexpected status: 404 (NotFound) was returned from the server while requesting the URL: http://127.0.0.1:7101/BlogApplication-ViewController-context-root1/adfdiRemoteServlet
    Source: adfdi-datamanager
    Stack:
    at oracle.adf.client.windows.datamanager.LoginHandler.GetAuthenticationMode()
    ~~~~~~~~~~~~~~~~~~~~~~
  • With switchValue = Verbose - detailed information about the workbook events including warning, information and errors will be seen with delimiter in log file.
    Eg: Run the workbook and perform Download by clicking on Download ribbon command. Enter an invalid value for FirstName(say 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa') and perform Upload by clicking on Upload ribbon command. Upload will fail as FirstName has invalid precision/scale.
    In C:\temp\adfdi-commom-excel.txt log file, all details in About dialog will be logged similar to below :
    ~~~~~~~~~~~~~~~~~~~~~~
    "adfdi-common"|Verbose|5|"oacw.datamanager.BindingContainer.AppendExceptionToList: Exception received from server: localized message: JBO-27023: Failed to validate all rows in a transaction.; display message: null; java class name: oracle.jbo.TxnValException; cause: null
    product code: JBO; error code: 27023; severity: Error; row key: null; attribute id: null;
    Detail: localized message: JBO-27024: Failed to validate a row with key oracle.jbo.Key[100 ] in AppModule.EmployeesView1; display message: null; java class name: oracle.jbo.RowValException; cause: null
    product code: JBO; error code: 27024; severity: Error; row key: 00010000000AACED0005770400000064; attribute id: null;
    Detail: localized message: JBO-27010: Attribute set with value aaaaaaaaaaaaaaaaaaaaaaaaaaaa for FirstName in AppModule.EmployeesView1 has invalid precision/scale; display message: Attribute set with value aaaaaaaaaaaaaaaaaaaaaaaaaaaa for FirstName in AppModule.EmployeesView1 has invalid precision/scale; java class name: oracle.jbo.PrecisionScaleValidationException; cause: null
    product code: JBO; error code: 27010; severity: Error; row key: null; attribute id: FirstName; Details: null"||12780||"1"|"2013-08-23T13:10:38.8164715Z"||
    ~~~~~~~~~~~~~~~~~~~~~~

How to enable logging for a particular session:

This type of logging can be used only for DT/TST mode and cannot be used for RT mode. This logging is of major help when we want detailed log messages for a specific usecase/error.

On opening ADFdi enabled excel workbook, under Oracle ADF ribbon tab, there will be Logging section with buttons for Console, Set Output Level, Add Log Output File and Refresh Config

Logging-latest.png

Clicking on Console button, opens up Logging Console non-modal dialog. By default the logging level is set to Off and hence no logs will be seen.

logging console-latest.png

Logging levels can be set either by clicking Set Output Level in Logging section of Oracle ADF tab or by clicking Set Level in Logging Console dialog.

Logging levels-latest.png

Logs can also be saved in a file by specifying file name and location in 'Add New Temporary Logging Output File' dialog by clicking on 'Add Log Output File' button in Logging section of Oracle ADF tab.

log output file format -latest.png

The format of log can also be changed by selecting Text/XML from Output Type as seen in above image.

Through these ways we can enable client-side logging for ADF Desktop Integration to trace down the error /exception we get during development / testing / debugging the workbook.

Wednesday Jul 31, 2013

EJB DC - Using JPQL constructor expressions

In JPA 2.0, a new feature is provided - "Constructor Expressions in the SELECT Clause". This feature is mainly useful for queries with multiple Select expressions, where custom result objects is necessary. This feature works with Oracle JDeveloper 12.1.2.0.0

Implementation Steps

Create Fusion Web Application with entities based on Departments and Employees, then create a session bean.

Create a Java class "DeptAndEmp" and add the below code.

public class DeptAndEmp implements Serializable {
    private String departmentName;
    private String email;
    private String firstName;
    private String lastName;
    public DeptAndEmp() {
        super();
    }

    public DeptAndEmp(String departmentName, String email, 
		String firstName, String lastName) {
        this.setDepartmentName(departmentName);
        this.setEmail(email);
        this.setFirstName(firstName);
        this.setLastName(lastName);
    }

    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }

    public String getDepartmentName() {
        return departmentName;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setLastName(String lastName) {
	 this.lastName = lastName;
    }
    public String getLastName() {
        return lastName;
    }
}

Open the session bean and paste the below method code and expose the method filterDeptEmpResult() method in local/remote interface.

public List<DeptAndEmp> filterDeptEmpResult() { String qlString = "SELECT NEW model.util.DeptAndEmp(dept.departmentName, emp.email, emp.firstName, emp.lastName) FROM Employees emp, Departments dept where emp.departments.departmentId = dept.departmentId"; TypedQuery<DeptAndEmp> query = em.createQuery(qlString, DeptAndEmp.class); return query.getResultList(); }

Note:- In the Query "model is a package name and TypedQuery is a JPA query that returns a specific type of Object. TypedQuery eliminates the need to cast the query result to a specific type or having to add a SuppressWarnings annotation to eliminate compiler warnings about unchecked conversions. TypedQuery is well explained in Java Persistence Architecture 2.0 - Using The New TypedQuery Interface

Create a Sample Java Client and add the below code in main method.

SessionEJB sessionEJB = (SessionEJB) context.lookup("JPAConstructorApp-Model-SessionEJB#model.SessionEJB");

for (DeptAndEmp deptAndEmp : (List<DeptAndEmp>) sessionEJB.filterDeptEmpResult()) { System.out.println("departmentName = " + deptAndEmp.getDepartmentName()); System.out.println("email = " + deptAndEmp.getEmail()); System.out.println("firstName = " + deptAndEmp.getFirstName()); System.out.println("lastName = " + deptAndEmp.getLastName()); System.out.println("======================================="); }

Deploy the sessionFacade and run the java client. In the console employees details will be displayed as below.

Add-Edit multiple rows using EJB DC

Let us take a scenario where in users wants to add/edit multiple records in the ADF table,  earlier with stateless session bean we need have work around to achieve this scenario.

This scenario can be achieved using stateful session bean, the application-managed transaction model for EJB/POJO data controls which additionally maintains a cache of managed entities and using commit operations user will be able to add/edit multiple records.

Implementation Steps

Create Java EE Web Application with entity based on Dept(sequences enabled on deptId), then create a stateful session bean with Transaction Type as "CMT with Explicit Commit" and data control for the session bean.  "CMT with Explicit Commit" supported in Oracle JDeveloper 12.1.2.0.0

In View controller project, create jspx page. Drop deptFindAll->Table/List View as ADF Table with  multi-selection option enabled.



Run the jspx page (i.e the web page look something like the image shown below). Notice here the commit button will be disabled. Once user click on the create button, commit button will get enabled.



Here we will create two records, so clicking the create button twice. Enter the dept details and click on commit button to save the records. You can also configure @SequenceGenerator/@TableGenerator to auto generate the DeptNo.


Result page should contains newly added records.

About

Tips and Tricks from Oracle's JDeveloper & ADF QA

Search

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