Friday, 16 August 2013

Passing Multiple Complex Parameters in WSDC via Backing Bean


UseCase Description

One of the most common usecases in WS is passing complex Input parameters, but incase if the user wants to pass the multiple occurence of the same complex Input parameter type, how to achieve this in WSDC ?.
This blog explains about passing the multiple complex Employee records to the WebService from the WSDC via the Backing Bean
 Let us Consider an ADFBC Component which contains an Employees Entity, now the user can select multiple employee records from the page containing List of Employee records and post it to the WebService.

Implementation Steps

1. Create an Web Service which takes the Input Argument as ArrayList of Employee Object  and returns the ArrayList .
The Sample SOAP Request/Response looks like as shown below ,
Here we can observe that the the occurence of the "emp" object is dynamic which contains different employee records with three attributes (empEmail, empId, empName).
SOAP Request
<ns1:addemp>
            <emp>
                <empemail>SLROCK</empemail>
                <empid>101</empid>
                <empname>ShawnRock</empname>
            </emp>
            <emp>
                <empemail>OKOKHLO</empemail>
                <empid>202</empid>
                <empname>OnkBank</empname>
            </emp>
  </ns1:addemp>

SOAP Response
 <ns2:addempresponse xmlns:ns2="http://project1/">
            <return>
                <empemail>SLROCK</empemail>
                <empid>101</empid>
                <empname>ShawnRock</empname>
            </return>
            <return>
                <empemail>OKOKHLO</empemail>
                <empid>202</empid>
                <empname>OnkBank</empname>
            </return>
 </ns2:addempresponse>

2. Create Business Components from Employee Table.
3. Drag and Drop the EmployeesView1 node from the DC palette onto the page1.jspx as ADF Table.Ensure that the page1.jspx is bounded to a Backing Bean
4. Create a WSDC using the WSDL url of the service deployed to the server
5. Create a Command Button in page1.jspx and bound it to an Action Listener method in the Backing Bean . The Method is used to retrieve the records selected by the user from the page1.jspx and post it to the WS.
BindingContainer bindings = getBindings();
RowKeySet selectedEmps = getT1().getSelectedRowKeys();   
Iterator selectedEmpIter = selectedEmps.iterator();
DCBindingContainer bx =  (DCBindingContainer) getBindings();
DCIteratorBinding empIter = bx.findIteratorBinding("EmployeesView1Iterator");
RowSetIterator empRSIter = empIter.getRowSetIterator();
6. This is Achieved by marshalling the selected data as an Employee Objects and adding them to an ArrayList.
ArrayList al = new ArrayList();
while(selectedEmpIter.hasNext()){
Key key = (Key)((List)selectedEmpIter.next()).get(0);
Row currentRow = empRSIter.getRow(key);                
al.add(new Employee((String)currentRow.getAttribute("FirstName"),(Integer)currentRow.getAttribute("EmployeeId"),(String)currentRow.getAttribute("Email")));
}

7. This ArrayList is then passed onto the Params Map of the WSDC method as shown below
OperationBinding operationBinding =  bindings.getOperationBinding("addEmp");
operationBinding.getParamsMap().put("emp",al);
operationBinding.execute();
8. In page2.jspx, Drag and Drop the Return type of the WSDC method as ADF Table
9. Run the Page1.jspx, select the list of Employee records and click on the post to webservice button



10. In the Next Page we can observe that the selected 4 records are posted to the WebService


Tuesday, 30 July 2013

Integrating Google Authentication in Oracle ADF Application


This blog explains about securing an ADF application using the Google Sign in Functionality which uses the OAuth 2.0 security framework

Prerequisites
package view;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Arrays;
public class MyGoogleHelperClass {

 
  private static final String CLIENT_ID = "YOUR CLIENT ID";
    private static final String CLIENT_SECRET = "YOUR CLIENT SECRET";
    private static final String CALLBACK_URI = "YOUR CALL BACK URI";
    private static final Iterable SCOPE = Arrays.asList("https://www.googleapis.com/auth/userinfo.profile;https://www.googleapis.com/auth/userinfo.email".split(";"));
    private static final String USER_INFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo";
    private static final JsonFactory JSON_FACTORY = new JacksonFactory();
    private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
    private String stateToken;
    private final GoogleAuthorizationCodeFlow flow;
    
    public MyGoogleHelperClass() {
        super();
        flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT,
                        JSON_FACTORY, CLIENT_ID, CLIENT_SECRET, SCOPE).build();
        generateStateToken();
    }
    
    public String buildLoginUrl() {
            final GoogleAuthorizationCodeRequestUrl url = flow.newAuthorizationUrl();                   
            return url.setRedirectUri(CALLBACK_URI).setState(stateToken).build();
    }
    
    private void generateStateToken(){
            SecureRandom sr1 = new SecureRandom();
            stateToken = "google;"+sr1.nextInt();
    }
    public void setStateToken(String stateToken) {
        this.stateToken = stateToken;
    }

    public String getStateToken() {
        return stateToken;
    }
    public String getUserInfoJson(final String authCode) throws IOException {                           
            final GoogleTokenResponse response = flow.newTokenRequest(authCode).setRedirectUri(CALLBACK_URI).execute();
            final Credential credential = flow.createAndStoreCredential(response, null);
            final HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(credential);
            // Make an authenticated request
            final GenericUrl url = new GenericUrl(USER_INFO_URL);
            final HttpRequest request = requestFactory.buildGetRequest(url);
            request.getHeaders().setContentType("application/json");
            final String jsonIdentity = request.execute().parseAsString(); 
            return jsonIdentity;

    }
}

Implementation Steps
1. Create a Fusion Application. In the Model create a WSDC of a WS which returns an complex employee Object
2. In the View Controller, create a jspx page i.e DCPage
3. Drag and Drop the Return type node of the method onto the page as ADF Table
4. Create another index.jspx page with a backing bean, then create a command menu button/link and then bind to the method in the backing Bean which contains code as shown below
public void verifytheUser(ActionEvent awt)
    {
     FacesContext context = FacesContext.getCurrentInstance();
     MyGoogleHelperClass ghc = new MyGoogleHelperClass();
     String urlPath     =        ghc.buildLoginUrl();       
try {
            context.getExternalContext().getSessionMap().put("StateToken",ghc.getStateToken());
           context.getExternalContext().redirect(urlPath);
        } catch (IOException e) {
        System.out.println(e);
        }
    }

5. Run the index.jspx page and click on the action button/link, we can see that the page is navigated to the Google Sign-In Page where the user is prompted to login with his credentials
6. If Login is successful, the user is asked to allow the application to access his resource, once accepted the page will be redirected back to the Callback URI
7. In the CallBack Page, get the following request parameters from the redirect uri. here we should check that the state which we sent while making the request matches with the state sent as query param along with the redirect uri. This Security is needed to avoid CSRF Hacking.
public void gettheUserdata()
{
FacesContext context = FacesContext.getCurrentInstance();
Map hm =   context.getExternalContext().getRequestParameterMap();
GoogleHelperClass ghc = new GoogleHelperClass();
// This needs to be verified to Avoid CSRF(Cross-Site Request Forgery)
String sentState = (String)context.getExternalContext().getSessionMap().get("StateToken ");
try {
if(hm.containsKey("code")&&(sentState.equalsIgnoreCase(hm.get("state"))))
{
String userInfo = ghc.getUserInfoJson((String)hm.get("code"));
context.getExternalContext().getSessionMap().put("USERNAME",extracttheName(userInfo));
context.getExternalContext().redirect("DC PAGE URL");            
} else{
context.getExternalContext().redirect("ACCESS DENIED URL");
}
}catch (IOException e) {

                    }
}


 public String extracttheName(String jsonStr)
    {
        String nmeoftheUser=null;
        try {
            JSONArray array = new JSONArray("["+jsonStr+"]");
            for (int i = 0; i < array.length(); i++) {
            JSONObject element = array.getJSONObject(0);
            NmeoftheUser = element.get("name");
            }      
        } catch (JSONException e) {
            System.out.println(e);
        }
        return nmeoftheUser;
    }
  • Code
  • State
8.  Use the Value set in the Session to display the name of the user on the page with the resource