When a middleware is used to make a lot of Web Service calls to Siebel CRM, the usual login mechanism is too slow and expensive. In the usual login, the Siebel application makes one login / logoff to every Siebel layer involved (application, database, etc) to every single call. When making a lot of concurrent calls, contention may occur in any of the underlying layers and some requests can be delayed or rejected, returning an error to the caller in some cases.
To ease this problem and improve performance, the most commonly solution is to use a Stateless Session Management of Authorization, which means that username and password are provided only in the first call, and subsequent calls are made using a SessionToken that is returned every time for the next call.
Obviously it would be amazing to do some kind of Connection Pool, or more specifically, a Token Pool/Cache to open some stateless sessions on demand, and reuse them using the next tokens.
I did a search in Google to find some references and cases of this approach, and I found some articles about using JAX-WS SOAP Handlers. But I was disappointed because I can’t use SOAP Handlers in my Middleware, and I can’t propagate this pool solution along all my applications making a Point-To-Point integration to Siebel.
The SOA architecture that we use here takes advantage of the Oracle Service Bus 11g to encapsulate all Siebel calls acting as a Proxy, doing input validation, security centralization and enforcement, endpoint management, policy attachment, statistics monitoring, parameterized data enrichment, service lifecycle management, and so on. Basically, all clients take access to Siebel through OSB:
In this article I will show a solution to make the Siebel Connection Pool using SessionToken at the OSB level, in a SOA approach. The best thing about this solution is the fact that none of the Siebel service clients are impacted by this change because the connection pool is centralized at the mediation layer, in this case, OSB, and no interfaces are changed.
The Connection Pool Classes
To control the pool, I made some simple Java classes, the first one is a simple DTO do pack the Siebel username and password into a single object to act as the key to store the tokens (implementing equals and hashcode default generated by Eclipse):
package com.wordpress.gibaholms;
public class SiebelCredential {
private String username;
private String password;
public SiebelCredential(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "SiebelCredential [username=" + username + ", password=" + password + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((password == null) ? 0 : password.hashCode());
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SiebelCredential other = (SiebelCredential) obj;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
I created another simple class to hold a Siebel token with his creation date. This date will be used to avoid returning certainly expired tokens:
package com.wordpress.gibaholms;
import java.util.Date;
public class SiebelToken {
private String token;
private Date creationDate;
public SiebelToken(String token, Date creationDate) {
this.token = token;
this.creationDate = creationDate;
}
public SiebelToken(String token) {
this(token, new Date());
}
@Override
public String toString() {
return "SiebelToken [token=" + token + ", creationDate=" + creationDate + "]";
}
public String getToken() {
return token;
}
public Date getCreationDate() {
return creationDate;
}
}
The most important class to control the pool is very simple, just making a static cache of a Queue of tokens mapped by credential (username and password pair). I also added some counters to be retrieved by one statistics service if necessary. As you can see, the methods to get/add tokens are synchronized to support concurrent requests from OSB. Obviously some short locks may occur, but the performance loss is insignificant compared to the login/logoff Siebel problem, because the methods are very simple and cheap:
package com.wordpress.gibaholms;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
public class TokenPool {
private static final int SIEBEL_TOKEN_EXPIRATION_IN_MINUTES = 15;
private static final Map<SiebelCredential, Queue<SiebelToken>> tokensByCredential;
private static long addCount;
private static long pollCount;
private static long hitCount;
private static long missCount;
static {
tokensByCredential = new HashMap<SiebelCredential, Queue<SiebelToken>>();
}
public synchronized static String pollToken(String username, String password) {
pollCount++;
SiebelCredential credential = new SiebelCredential(username, password);
if (tokensByCredential.containsKey(credential)) {
Queue<SiebelToken> tokens = tokensByCredential.get(credential);
while (tokens.size() > 0) {
SiebelToken token = tokens.poll();
long diffInMinutes = (System.currentTimeMillis() - token.getCreationDate().getTime()) / (60 * 1000);
if (diffInMinutes < SIEBEL_TOKEN_EXPIRATION_IN_MINUTES) {
hitCount++;
return token.getToken();
}
}
}
missCount++;
return null;
}
public synchronized static void addToken(String username, String password, String token) {
addCount++;
SiebelCredential credential = new SiebelCredential(username, password);
if (!tokensByCredential.containsKey(credential)) {
tokensByCredential.put(credential, new LinkedList<SiebelToken>());
}
tokensByCredential.get(credential).add(new SiebelToken(token));
}
public static void eraseCounters() {
addCount = 0;
pollCount = 0;
hitCount = 0;
missCount = 0;
}
public static long getAddCount() {
return addCount;
}
public static long getPollCount() {
return pollCount;
}
public static long getHitCount() {
return hitCount;
}
public static long getMissCount() {
return missCount;
}
}
Notice also that we have a constant SIEBEL_TOKEN_EXPIRATION_IN_MINUTES to give us a hint to indicate if the token would be expired on Siebel. The token expiration time on Siebel is a system parameter named SessionTokenTimeout that can be adjusted according to your needs. The default value indicated in Siebel documentation is 15 minutes. More information can be found on the official docs here: http://docs.oracle.com/cd/B40099_02/books/EAI2/EAI2_WebServices32.html#wp178856
Using this simple date control we avoid having old tokens into pool and minimize the chances of waste Siebel calls to discover that the token is expired to try again with full header unnecessarily, improving the pool efficiency. However some expired tokens may still pass because sometimes may occur a milliseconds window between the token creation date on Siebel and the token creation date into our pool, so the expired token error still need to be handled.
Now just assemble these classes into a JAR file and we are ready to define the best Message Flow to get/put tokens into this cache in the appropriate moments.
The Service Bus Project
Our OSB project looks like this:
Supported Scenarios
- Success on the first call – OK;
- Generic application error on the first call, without returning token – OK;
- Generic application error on the first call, returning token – OK;
- Token expired error in the first call, and success on the retry call – OK;
- Token expired error in the first call, and generic application error on the retry call, without retuning token – OK;
- Token expired error in the first call, and generic application error on the retry call, retuning token – OK.
Benefits of the Solution
- No changes to the service interfaces;
- No changes to the service consumers;
- No code replication;
- No infrastructure logic leak to the service consumers;
- Centralized solution;
- Easy to manage and maintain;
- Pluggable solution, easy to enable/disable to any service;
- Incredible performance gain to the entire corporation, because all Siebel services can be pool enabled as fast as add a route in OSB.
Now we can easy enable the Connection Pool to any Siebel SWE service into the SOA mediation layer, with no impacts on actual service consumers and “zero” code replication, in a corporative way.
Attachments
(Updated 08/02/2012 – 08:47)
Source Code:
https://github.com/gibaholms/articles/tree/master/Siebel_Connection_Pool_in_OSB
Gilberto Holms