Friday, September 18, 2015

Oracle Taleo WebService Sample Client Code to Create New Candidate and apply to a Taleo job offer (requisition)

Let's assume you are a Taleo Partner (EBS/Peoplesoft) and want to export a candidate from your application and import it into Taleo systems by attaching candidate to a existing Taleo job offer (requisition) .

<!-- Candidate Import Request with attaching Taleo job offer (requisition) -->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:e="http://www.taleo.com/ws/tee800/2009/01/candidate">
   <soapenv:Header>
      <itk:Debug xmlns:itk="http://www.taleo.com/ws/itk21/2005/07">
         <itk:StackTrace>true</itk:StackTrace>
      </itk:Debug>
   </soapenv:Header>
   <soapenv:Body>
      <e:merge>
         <e:candidate>
            <e:Address>123 Main Street</e:Address>
            <e:Address2>Apartment 1</e:Address2>
            <e:City>Seattle</e:City>
            <e:EmailAddress>xxx@invalid.address.com</e:EmailAddress>
            <e:FirstName>XYZ</e:FirstName>
            <e:LastName>XYZ</e:LastName>
            <e:ZipCode>74554-234</e:ZipCode>
            <e:Applications>
               <e:Application>
                  <e:Requisition>
                     <e:Requisition>
                        <e:ContestNumber searchType="search" searchValue="XXXXX"/>
                     </e:Requisition>
                  </e:Requisition>
               </e:Application>
            </e:Applications>
         </e:candidate>
      </e:merge>
   </soapenv:Body>
</soapenv:Envelope>

Oracle Taleo WebService Sample Client Code to Create New Candidate

Let's assume you wants to import the candidates into Taleo systems. Using Taleo BulkAPI, CandidateService allows your application to create and update a candidate in Taleo systems.

We can implement the above usecase with following options:

  • Java Code using SAAJ: SAAJ stands for "SOAP with Attachments API for Java." With it, you can make Java API calls that generate XML messages which comply with the SOAP 1.1 or 1.2 specifications, as well as the WS-I Basic Profile 1.1 specification. It allows developers to read and write SOAP-based XML messages, and in some cases, to send and receive those messages over the Internet. SAAJ offers an alternative to JAX-RPC or JAX-WS
public class SOAPClientSAAJ {
    /**
     * This method process the new candidate creation
     * @return employeeNumber
     * @throws SOAPException
     * @throws Exception
     */
    public static String processCandidateCreationSOAPRequest() throws SOAPException, Exception {
        SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
        SOAPConnection soapConnection = soapConnectionFactory.createConnection();

        // Trust to certificates
        doTrustToCertificates();

        // Send SOAP Message to SOAP Server
        String url = "https://xxx.taleo.net/enterprise/soap?ServiceName=CandidateService";
        SOAPMessage soapResponse = soapConnection.call(callCandidateCreationSOAPRequest(), url);
        String employeeNumber = processCandidateCreationSOAPResponse(soapResponse);
        return employeeNumber;
    }
 
    public SOAPClientSAAJ() {
        super();
    }
    
    /**
     * This method parse the soapResponse and gets the employeeNumber 
     * @param soapResponse
     * @return employeeNumber
     * @throws TransformerConfigurationException
     * @throws SOAPException
     * @throws ParserConfigurationException
     * @throws TransformerException
     * @throws SAXException
     * @throws IOException
     */
    private static String processCandidateCreationSOAPResponse(SOAPMessage soapResponse) throws TransformerConfigurationException,
                                                                               SOAPException,
                                                                               ParserConfigurationException,
                                                                               TransformerException, SAXException,
                                                                               IOException {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        Source sourceContent = soapResponse.getSOAPPart().getContent();
        StringWriter writer = new StringWriter();
        transformer.transform(sourceContent, new StreamResult(writer));
        String output = writer.toString();

        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        InputSource is = new InputSource();
        is.setCharacterStream(new StringReader(output));

        Document doc = db.parse(is);
        NodeList nodes = doc.getElementsByTagName("createResponse");
        Element element = (Element)nodes.item(0);
        NodeList name = element.getElementsByTagName("Number");
        Element line = (Element)name.item(0);
        return getCharacterDataFromElement(line).toString();
    }

    /**
     * This method creates the soapMessage and calls the webservice
     * @return
     * @throws Exception
     */
    private static SOAPMessage callCandidateCreationSOAPRequest() throws Exception {
        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage soapMessage = messageFactory.createMessage();
        SOAPPart soapPart = soapMessage.getSOAPPart();

        // SOAP Envelope
        SOAPEnvelope envelope = soapPart.getEnvelope();
        envelope.addNamespaceDeclaration("can", "http://www.taleo.com/ws/tee800/2009/01/candidate");
        envelope.addNamespaceDeclaration("ns1",
                                         "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");

        SOAPHeader soapHeader = envelope.getHeader();
        SOAPElement SecuritySoapElement = soapHeader.addChildElement("Security", "ns1");
        SOAPElement usernameTokenSoapElement = SecuritySoapElement.addChildElement("UsernameToken", "ns1");

        SOAPElement usernameSoapElement = usernameTokenSoapElement.addChildElement("Username", "ns1");
        usernameSoapElement.addTextNode("username");
        SOAPElement passwordSoapElement = usernameTokenSoapElement.addChildElement("Password", "ns1");
        passwordSoapElement.addTextNode("password");


        // SOAP Body
        SOAPBody soapBody = envelope.getBody();
        SOAPElement createSoapBodyElem = soapBody.addChildElement("create", "can");
        SOAPElement candidateSoapBodyElem = createSoapBodyElem.addChildElement("candidate", "can");

        SOAPElement emailAddressSoapBodyElem = candidateSoapBodyElem.addChildElement("EmailAddress", "can");
        emailAddressSoapBodyElem.addTextNode("xxx@invalid.address.com");

        SOAPElement addressSoapBodyElem = candidateSoapBodyElem.addChildElement("Address", "can");
        addressSoapBodyElem.addTextNode("123 Main Street");

        SOAPElement address2SoapBodyElem = candidateSoapBodyElem.addChildElement("Address2", "can");
        address2SoapBodyElem.addTextNode("Apartment 1");

        SOAPElement firstNameSoapBodyElem = candidateSoapBodyElem.addChildElement("FirstName", "can");
        firstNameSoapBodyElem.addTextNode("XYZ");

        SOAPElement lastNameSoapBodyElem = candidateSoapBodyElem.addChildElement("LastName", "can");
        lastNameSoapBodyElem.addTextNode("XYZ");

        // Save message
        soapMessage.saveChanges();
        return soapMessage;
    }

    /**
     * This method trust the https certificates 
     * @throws Exception
     */
    private static void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                    return;
                }

                public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                    return;
                }
            } };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" +
                                       session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

    private static String getCharacterDataFromElement(Element e) {
        Node child = e.getFirstChild();
        if (child instanceof CharacterData) {
            CharacterData cd = (CharacterData)child;
            return cd.getData();
        }
        return "";
    }

}

  • Java Code using Apache Axis2: - First you should Import the WSDL Files into Your Development Platform using below command.

“wsdl2java.bat -uri pathToWsdl/WsdlFilename -d xmlbeans -ns2p namespaceURL=javaPackageName”

public class CandidateDC {
 
    /**
     * Main method call the importCandidate method
     * @param args
     */
    public static void main(String[] args) {
        try {
            long employeeNumber = importCandidate();
            System.out.println("Candidate Num: " + employeeNumber);
        } catch (AxisFault e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (WebServiceFault e) {
            e.printStackTrace();
        }
    }
 
    /**
     * This method process the new candidate creation
     * @return employeeNumber
     * @throws AxisFault
     * @throws RemoteException
     * @throws WebServiceFault
     */
    public static long importCandidate() throws AxisFault, RemoteException, WebServiceFault {
        CandidateServiceStub stub =
            new CandidateServiceStub("https://hostname/enterprise/soap?ServiceName=CandidateService");
        HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
        auth.setUsername("Username");
        auth.setPassword("Password");
        auth.setPreemptiveAuthentication(true); // activate preemptive authentication
        stub._getServiceClient().getOptions().setProperty(HTTPConstants.AUTHENTICATE, auth);


        CreateDocument createDoc = CreateDocument.Factory.newInstance();
        CreateDocument.Create create = createDoc.addNewCreate();
        Candidate candidate = create.addNewCandidate();

        SearchableStringField address = candidate.addNewAddress();
        address.setStringValue("123 Main Street");

        SearchableStringField address2 = candidate.addNewAddress2();
        address2.setStringValue("Apartment 1");

        SearchableStringField lastName = candidate.addNewLastName();
        lastName.setStringValue("XYZ");

        SearchableStringField firstName = candidate.addNewFirstName();
        firstName.setStringValue("XYZ");

        SearchableStringField emailAddress = candidate.addNewEmailAddress();
        emailAddress.setStringValue("xxx@invalid.address.com");

        CreateResponseDocument createResponse = stub.create(createDoc);
        long val = createResponse.getCreateResponse().getNumber();
        return val;
    }
 
 public CandidateDC() {
        super();
    }
}

If you get the "java.net.SocketException: Software caused connection abort: recv failed" issue - More Details

Thursday, January 15, 2015

Oracle Taleo WebService Integration - "java.net.SocketException: Software caused connection abort: recv failed"

Recently I was trying to integrate Oracle Taleo WebService with one of the 3rd party system. I tried with WSDL2Java from the Apache Axis2 and SAAJ (SOAP with Attachments API for Java) types for the integration.

But on both the types I was getting "java.net.SocketException: Software caused connection abort: recv failed" issue. Below is the error detail.

org.apache.axis2.AxisFault: Software caused connection abort: recv failed 
at org.apache.axis2.AxisFault.makeFault(AxisFault.java:430) 
at org.apache.axis2.transport.http.HTTPSender.sendViaPost(HTTPSender.java:197) 
at org.apache.axis2.transport.http.HTTPSender.send(HTTPSender.java:75) 
at org.apache.axis2.transport.http.CommonsHTTPTransportSender.writeMessageWithCommons(CommonsHTTPTransportSender.java:404) 
at org.apache.axis2.transport.http.CommonsHTTPTransportSender.invoke(CommonsHTTPTransportSender.java:231) 
at org.apache.axis2.engine.AxisEngine.send(AxisEngine.java:443) 
at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:406) 
at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:229) 
at org.apache.axis2.client.OperationClient.execute(OperationClient.java:165) 
at com.taleo.www.ws.tee800._2009._01.candidate.CandidateServiceStub.create(CandidateServiceStub.java:1284) 
at com.taleo.Generic.main(Generic.java:54) 
Caused by: java.net.SocketException: Software caused connection abort: recv failed 
at java.net.SocketInputStream.socketRead0(Native Method) 
at java.net.SocketInputStream.read(SocketInputStream.java:152) 
at java.net.SocketInputStream.read(SocketInputStream.java:122) 
at java.io.BufferedInputStream.fill(BufferedInputStream.java:235) 
at java.io.BufferedInputStream.read(BufferedInputStream.java:254) 
at org.apache.commons.httpclient.HttpParser.readRawLine(HttpParser.java:78) 
at org.apache.commons.httpclient.HttpPars 

Solution:-

The error message that we are getting is not because of the java code, it's related to a HTTPS/SSL setup/configuration problem.

Connection to the Taleo zone requires use of the "HTTPS" protocol which uses "SSL". You need to Import the HTTPS Certificate into Java HTTPS certificates, this should solve the above issue.

Note:- If you are deploying your application to any web server, please check if the certificate is trusted by the 3rd party web server.

Steps to Import Certificate :-

  • Export the HTTPS Certificate into a file
    • Open your web browser
    •  Enter your web browser's "Options" utility. Internet Explorer users will need to click "Tools" and then "Internet Options," for example. Mozilla Firefox users will need to click "Tools" and then "Options..." Google Chrome users will need to click "Customize" and then "Options"
    • Click "Content" and then "Certificates" in Internet Explorer. Click on the certificate you'd like to export and then click on the "Export" button to export it for use on other servers
    • Click "Advanced" and then "View Certificates" in Mozilla Firefox. Click on the certificate you'd like to export and then click on the "Export" button to export it for use on other servers
    • Click "Under the Hood" and then "Manage Certificates" in Google Chrome. Click on the certificate you'd like to export and then click on the "Export" button to export it for use on other servers
  • Import the HTTPS Certificate into Java HTTPS certificates
    • Java HTTPS certificates stored in - $JAVA_HOME/jre/lib/security/cacerts
    • To import your new certificate into “cacerts” run below command:
“keytool -import -file amirssw.cer -keystore cacerts”
The Password is “changeit”

Saturday, June 28, 2014

How to check AMX page is fully loaded or not

Sometimes you might requires to manipulate the HTML elements after the page is fully loaded. In HTML file it an be checked using window.onload or deviceready but same doesn't guarantees the appropriate ADF Mobile state.

In ADF mobile using showpagecomplete event on the handlePageShown callback function it can be checked.
<script>
        function handlePageShown() {
          console.log("Page is shown!");
        }
        document.addEventListener("showpagecomplete", handlePageShown, false);
</script>
The showpagecomplete event guarantees the appropriate ADF Mobile state; other browser and third-party events, such as load and Cordova's deviceready, may not.

In my next article I will show the usage of showpagecomplete event with example.

Friday, June 27, 2014

ADF Mobile - Get network status dynamically

While working on ADF mobile application with offline/online capabilities, before calling webservices network has to be checked whether it is "connected" or "disconnected".

In my previous article Offline Data Synchronization for ADF Mobile, I was using the javascript to evaluate the network connectivity and store the connection status in pageFlowScope variable. But after subsequent checks pageFlowScope variable was not refreshing properly. Please see the oracle forum for more info,  ADF Mobile - Get network status.

Steps to check network status dynamically:-

1. Java Script to check the network status
 /**
  * Method to check the network status
  */
  application.checkConnection = function () {
     var isConnected;
     var connectionType = navigator.network.connection.type;
     var states = {
      };
     states[Connection.UNKNOWN] = 'Unknown connection';
     states[Connection.ETHERNET] = 'Ethernet connection';
     states[Connection.WIFI] = 'WiFi connection';
     states[Connection.CELL_2G] = 'Cell 2G connection';
     states[Connection.CELL_3G] = 'Cell 3G connection';
     states[Connection.CELL_4G] = 'Cell 4G connection';
     states[Connection.NONE] = 'No network connection';
     if (connectionType == Connection.NONE || connectionType == Connection.UNKNOWN) {
        isConnected = false;
     }else {
        isConnected = true;
     }
     var obj = {connectionType : connectionType, isConnected : isConnected};
     return obj;
  }
2. In managed bean call the application.checkConnection method
public void checkNetworkStatusAction(ActionEvent actionEvent) {
String networkDetails = (String)AdfmfContainerUtilities.invokeContainerJavaScriptFunction(AdfmfJavaUtilities.getFeatureName(),"application.checkConnection",new Object[] { });
   try {
        JSONObject obj = new JSONObject(networkDetails);
        String connectionType = (String)obj.getString("connectionType");
        boolean isConnected = (boolean)obj.getBoolean("isConnected");
        setIsConnected(isConnected);
        setConnectionType(connectionType);
    } catch (JSONException e) {
        throw new AdfException("Error while trying to connect to network", AdfException.ERROR);
    }
}
Application screen looks like below when it deployed to Android device, When there is no network available in the device.


Enable the Mobile Data or Wireless and click on the Check button, Connection Type and Network Status will be displayed on the screen.


You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.4.0]

Thursday, March 6, 2014

ADF Mobile - Popup at center of the window

Sometimes you may want to position amx:popup at the center of the window. At this moment, in 11.1.2.4.0 there is no declarative solution for this usecase, In this post I'm sharing a code snippet that I noticed from Denis Tyrell(Oracle) for the same.

You can align the amx:showPopupBehavior with overlapTop property, this indicates how the popup should be aligned relative to the panelPage and then in the amx:popup code you can set the inlineStyle as below:
<amx:popup id="p1" autoDismiss="true"
     inlineStyle="position:relative;margin-top:#{(deviceScope.hardware.screen.availableHeight-200)/2}px;margin-left:#{(deviceScope.hardware.screen.availableHeight*10)/100}px;margin-right:#{(deviceScope.hardware.screen.availableHeight*10)/100}px;width:#{deviceScope.hardware.screen.availableWidth};height:200px;">
    <amx:outputText value="Popup at center of the window" id="ot4"/>
</amx:popup>
Application screen looks like below when it deployed to IOS simulator.

Sunday, March 2, 2014

ADF Mobile - Reading custom properties from adf-config.xml

Take a scenario where you have integrated the push notification feature into mobile application and application needs to be shipped to client, to work push notification feature you need Sender ID for both Android and iOS and client want's to use their own Sender Id.

Next question will be where can we store the custom Sender Id in the application so that the client can edit. Credit goes to Srini Indla(Oracle), I have extracted the idea based on one of his forum reply and below is one of the way.

Custom values can be stored in add-config.xml file under <adf:adf-properties-child> tag, add-config.xml is used to configure application-level settings, including the Configuration Service parameters and read more on ADF Mobile Configuration Service Usage. The advantage of storing the properties and accessing it via the adf-confg.xml is that the values can be accessible using the EL expression at run time.

Below is a sample adf-confif.xml with custom properties:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<adf-config xmlns="http://xmlns.oracle.com/adf/config" xmlns:config="http://xmlns.oracle.com/bc4j/configuration"
            xmlns:adf="http://xmlns.oracle.com/adf/config/properties">
  <adf-adfm-config xmlns="http://xmlns.oracle.com/adfm/config">
    <defaults changeEventPolicy="ppr" useBindVarsForViewCriteriaLiterals="true"/>
    <startup>
      <amconfig-overrides>
        <config:Database jbo.locking.mode="optimistic"/>
      </amconfig-overrides>
    </startup>
  </adf-adfm-config>
  <adf:adf-properties-child xmlns="http://xmlns.oracle.com/adf/config/properties">
    <adf-property name="adfAppUID" value="MobileAdfConfig-1564"/>
    <adf-property name="username" value="Weblogic"/>
    <adf-property name="password" value="Weblogic1"/>
  </adf:adf-properties-child>
</adf-config>
In Amx page you can access the above custom defined properties using EL expression.
For ex:  "#{applicationScope.configuration.username}"

Below is the sample code to access custom properties from adf-config.xml in managed bean.
ValueExpression userNameVe =
            AdfmfJavaUtilities.getValueExpression("#{applicationScope.configuration.username}", String.class);
String userName = (String)userNameVe.getValue(AdfmfJavaUtilities.getAdfELContext());

ValueExpression passwordVe =
            AdfmfJavaUtilities.getValueExpression("#{applicationScope.configuration.password}", String.class);
String password = (String)passwordVe.getValue(AdfmfJavaUtilities.getAdfELContext());