Tutorial: Extend the CRM On Demand Connector

This tutorial enables the user to use another form of web service call authentication by extending the necessary classes of the Connector. The objective of the sample is to use stateful web service calls via SSO login instead of the out-of-the-box stateless SOAP requests.

Implementing a SSO Login has the following steps:

Extend the WebServiceBroker class

Implement the OnSessionCreatedEventHandler event handler

Manually register the two classes as plugins of Oracle Web Determinations

Set the fixed-authentication property to false

Create the Web Applet in CRM On Demand to pass the SSO token

Extend the WebServiceBroker class

This class contains two abstract methods used for authenticating web service calls:

StatefulWebService.java:

package com.crmod.extension;

 

import java.io.IOException;

import java.io.UnsupportedEncodingException;

import java.net.HttpURLConnection;

import java.net.MalformedURLException;

import java.net.ProtocolException;

import java.net.URL;

import java.net.URLEncoder;

import java.util.List;

import java.util.Map;

import java.util.StringTokenizer;

 

import com.oracle.determinations.web.crmod.ConnectorContext;

import com.oracle.determinations.web.crmod.exception.CrmodAuthenticationException;

import com.oracle.determinations.web.crmod.webservice.*;

 

public class StatefulWebService extends WebServiceBroker{

 

private static final String CRMOD_SSO_LOGIN_URI = "?command=ssologin";

private static final String CRMOD_LOGOUT_URI = "?command=logoff";

private static final String REQUEST_METHOD = "POST";

private static final String SSO_TOKEN_TEXT = "odSsoToken=";

private static final String COOKIE = "Cookie";

private static final String JSESSIONID = "JSESSIONID";

private String jSessionId = "";

 

public StatefulWebService(ConnectorContext context) {

super(context);

}

 

@Override

public void logoff() {

try {

URL wsURL = new URL(this.getContext().getUrl() + CRMOD_LOGOUT_URI);

HttpURLConnection wsConnection = (HttpURLConnection) wsURL.openConnection();

wsConnection.setUseCaches(false);

wsConnection.setRequestProperty(JSESSIONID, jSessionId);

wsConnection.setRequestMethod(REQUEST_METHOD);

 

} catch (ProtocolException e) {

throw new CrmodAuthenticationException(e.getMessage());

} catch (MalformedURLException e) {

throw new CrmodAuthenticationException(e.getMessage());

} catch (IOException e) {

throw new CrmodAuthenticationException(e.getMessage());

}

}

 

@Override

public boolean logon() {

boolean success = false;

 

//get the token from the caseID

String ssoToken = this.getContext().getCaseId().getArgs();

 

//Now Make the login request

String loginUrl;

try {

loginUrl = this.getContext().getUrl() + CRMOD_SSO_LOGIN_URI + "&" + SSO_TOKEN_TEXT +

URLEncoder.encode(ssoToken, "UTF-8");

 

//Try connecting to the login endpoint

URL wsURL = new URL(loginUrl);

HttpURLConnection wsConnection = (HttpURLConnection) wsURL.openConnection();

wsConnection.setUseCaches(false);

wsConnection.setDoOutput(true);

wsConnection.setRequestMethod(REQUEST_METHOD);

 

// if we have a response, get the jsessionid from the cookie headers

int httpResponseCode = wsConnection.getResponseCode();

 

if (httpResponseCode == HttpURLConnection.HTTP_OK) {

String jSessionCookie = getCookieFromHeaders(wsConnection, JSESSIONID);

jSessionId = getJSessionId(jSessionCookie);

// this will attach the session ID to the SOAP message as a cookie

this.addConnectionProperty(COOKIE, JSESSIONID + "=" + jSessionId);

success = true;

}

else {

new CrmodAuthenticationException(wsConnection.getResponseMessage());

}

} catch (UnsupportedEncodingException e) {

new CrmodAuthenticationException(e.getMessage());

} catch (MalformedURLException e) {

new CrmodAuthenticationException(e.getMessage());

} catch (IOException e) {

new CrmodAuthenticationException(e.getMessage());

}

return success;

}

 

private String getJSessionId(String cookie) {

StringTokenizer st = new StringTokenizer(cookie, ";");

String jsessionid = st.nextToken();

st = new StringTokenizer(jsessionid, "=");

st.nextToken();

return st.nextToken();

}

 

private String getCookieFromHeaders(HttpURLConnection wsConnection,

String pstrCookieName) {

 

String headerName;

String headerValue = "FAIL";

Map<String, List<String>> m = wsConnection.getHeaderFields();

for (int i = 0; i <= m.size(); i++) {

headerName = wsConnection.getHeaderFieldKey(i);

if (headerName != null

&& headerName.equals("Set-Cookie")

&& wsConnection.getHeaderField(i).indexOf(pstrCookieName) >= 0) {

headerValue = wsConnection.getHeaderField(i);

break;

}

}

return headerValue;

}

}

Logon()

This method is invoked once all SOAP requests are already created and prior to executing these SOAP requests. It should return true if the logon was successful otherwise false. The primary purpose of this method is to establish a successful connection to login endpoint and to receive a session Id which is used for authenticating each web service request.

Logoff()

This method is invoked once all SOAP requests have been executed. The objective of this method is to invoke the logout URL, sending the session ID as a cookie named JSESSIONID

Implement the OnSessionCreatedEventHandler event handler

The OnSessionCreatedEventHandler class allows us to modify the instance of the CRM On Demand data adaptor by attaching the implementation of the WebServiceBroker class before the interview session is created.

StatefulEventHandler.java:

package com.crmod.extension;

 

import com.oracle.determinations.interview.engine.events.OnSessionCreatedEvent;

import com.oracle.determinations.interview.engine.events.handlers.OnSessionCreatedEventHandler;

import com.oracle.determinations.interview.engine.exceptions.DataAdaptorException;

import com.oracle.determinations.interview.engine.plugins.InterviewSessionPlugin;

import com.oracle.determinations.interview.engine.plugins.InterviewSessionRegisterArgs;

import com.oracle.determinations.web.crmod.exception.CrmodConnectorException;

import com.oracle.determinations.web.crmod.plugins.CrmodAdapter;

import com.oracle.determinations.web.crmod.webservice.WebServiceBroker;

 

public class StatefulEventHandler implements OnSessionCreatedEventHandler{

 

public InterviewSessionPlugin getInstance(InterviewSessionRegisterArgs arg0) {

return new StatefulEventHandler();

}

 

public void handleEvent(Object arg0, OnSessionCreatedEvent arg1) {

 

if (arg1.getCreatedSession().getDataAdaptor() instanceof CrmodAdapter){

CrmodAdapter crmodAdapter = (CrmodAdapter) arg1.getCreatedSession().getDataAdaptor();

 

if (crmodAdapter != null){

WebServiceBroker webservice;

try {

webservice = new StatefulWebService(crmodAdapter.getContext());

crmodAdapter.setWebServiceBroker(webservice);

} catch (CrmodConnectorException e) {

throw new DataAdaptorException(e);

}

}

}

}

}

handleEvent()

The objective of this method is to get the instance of CrmodAdapter to attach to the instance of StatefulWebService class to be used during web service calls.

The two classes should be packaged in a JAR file which then needs to be deployed to the lib directory of Web Determinations.

Manually register the two classes as plugins of Oracle Web Determinations

The two classes we have implemented should be manually registered to Oracle Web Determinations by modifying the application.properties file location in the configuration directory. Add the fully qualified class name of the two classes to the plugin.libraries property. There is already an existing library for com.oracle.determinations.web.crmod.plugins.CrmodAdapter so a complete example of this property should be as follows:

Example:

plugin.libraries =com.oracle.determinations.web.crmod.plugins.CrmodAdapter;com.crmod.extension.StatefulWebService;com.crmod.extension.StatefulEventHandler;

Set the Fixed Authentication property to False

Open crmod-data-adapter.properties file located in the configuration folder. Set the fixed-authentication property to false to support stateful login.

Example:

fixed-authentication=false

Create the Web Applet in CRM On Demand to pass the SSO token

Finally, we need to create a web applet suitable for SSO login. The SSO token can be added to the caseID as the second argument separated by a comma as shown here: