Salesforce wsc hacking: getting rid of SessionTimedOutException

The SoapConnection java class sports a private static inner Exception, the SessionTimedOutException. This Exception is used internally only to quickly pass control from the parseDetail() method back to the send() method.

This is bad practice, using an Exception to control flow of execution. And in this case the infrastructure is doing exception handling in the first place… There has to be a better way.


First thing is to remove the SessionTimedOutException private static inner class.

Then change the parseDetail() method as follows to always just throw the correct exception:

228
229
230
231
232
233
234
235
        try {
            e = (ConnectionException) typeMapper.readObject(xin, info, ConnectionException.class);
            if (e instanceof SoapFaultException) {
                ((SoapFaultException)e).setFaultCode(faultCode);
            }
        } catch (ConnectionException ce) {
            throw new ConnectionException("Failed to parse detail: " + xin + " due to: " + ce, ce.getCause());
        }

Biggest modification is to be in the send() method. But before we do that we’re introducing a little convenience method: isSessionTimedOutFault(). This basically implements the test that before was in the hack in the parseDetail() method above:

private boolean isSessionTimedOutFault(SoapFaultException sfe) {
    return "INVALID_SESSION_ID".equals(sfe.getFaultCode().getLocalPart()) 
        && sfe.getMessage() != null 
        && (sfe.getMessage().contains("Session timed out") || sfe.getMessage().contains("Session not found"));
}

Now we’re ready to modify the send() method. Not only do we get rid of the SessionTimedOutException and simplify the implementation, we also fix a minor bug in reporting the calculated request period if the second operation try after the SessionRenewer has done its work fails with a SocketTimeoutException:

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
public XMLizable send(String soapAction, QName requestElement, XMLizable request, QName responseElement, Class<?> responseType)
        throws ConnectionException {
 
    long startTime = 0;
    try {
        boolean firstTime = true;
        while(true) {
            try {
	        startTime = System.currentTimeMillis();
                Transport transport = newTransport(config);
		OutputStream out = transport.connect(url, soapAction);
		sendRequest(out, request, requestElement);
		InputStream in = transport.getContent();
		return receive(transport, responseElement, responseType, in);
            } catch (SoapFaultException se) {
                if (config.getSessionRenewer() != null && firstTime && isSessionTimedOutFault(se)) {
                    SessionRenewer.SessionRenewalHeader sessionHeader = config.getSessionRenewer().renewSession(config);
		    if (sessionHeader != null) {
		        addHeader(sessionHeader.name, sessionHeader.headerElement);
                    }
                } else {
                    throw se;
                }
            }
            firstTime = false;
        }
    } catch (SocketTimeoutException e) {
        long timeTaken = System.currentTimeMillis() - startTime;
        throw new RequestTimedOutException("Request to " + url + " timed out. TimeTaken=" + timeTaken +
                " ConnectionTimeout=" + config.getConnectionTimeout() + " ReadTimeout=" + 
                config.getReadTimeout(), e);
    } catch (IOException e) {
        throw new ConnectionException("Failed to send request to " + url, e);
    }
}

This has not yet been added as feature to the WSC issues list – I’m awaiting them to respond to the other requests I’ve added 🙂

About Jesper Udby

I'm a freelance computer Geek living in Denmark with my wife and 3 kids. I've done professional software development since 1994 and JAVA development since 1998.
This entry was posted in Java, Salesforce and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.