001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: InteractionManager.java,v 1.5 2008/08/06 17:28:10 exu Exp $
026 *
027 * Portions Copyrighted 2016-2017 ForgeRock AS.
028 */
029
030package com.sun.identity.liberty.ws.interaction;
031
032import static org.forgerock.http.util.Uris.urlEncodeQueryParameterNameOrValue;
033import static org.forgerock.openam.utils.Time.*;
034
035import com.sun.identity.common.PeriodicCleanUpMap;
036import com.sun.identity.common.SystemTimerPool;
037import com.sun.identity.common.TaskRunnable;
038import com.sun.identity.shared.debug.Debug;
039import com.sun.identity.liberty.ws.common.LogUtil;
040import com.sun.identity.liberty.ws.interaction.jaxb.InquiryElement;
041import com.sun.identity.liberty.ws.interaction.jaxb.InteractionResponseElement;
042import com.sun.identity.liberty.ws.interaction.jaxb.RedirectRequestElement;
043import com.sun.identity.liberty.ws.interaction.jaxb.StatusElement;
044import com.sun.identity.liberty.ws.interaction.jaxb.UserInteractionElement;
045import com.sun.identity.liberty.ws.soapbinding.Client;
046import com.sun.identity.liberty.ws.soapbinding.CorrelationHeader;
047import com.sun.identity.liberty.ws.soapbinding.Message;
048import com.sun.identity.liberty.ws.soapbinding.SOAPBindingConstants;
049import com.sun.identity.liberty.ws.soapbinding.SOAPBindingException;
050import com.sun.identity.liberty.ws.soapbinding.SOAPFault;
051import com.sun.identity.liberty.ws.soapbinding.SOAPFaultDetail;
052import com.sun.identity.liberty.ws.soapbinding.SOAPFaultException;
053import com.sun.identity.liberty.ws.soapbinding.Utils;
054import com.sun.identity.saml.common.SAMLUtils;
055import java.io.IOException;
056import java.math.BigInteger;
057import java.net.InetAddress;
058import java.net.MalformedURLException;
059import java.net.UnknownHostException;
060import java.net.URL;
061import java.util.ArrayList;
062import java.util.Date;
063import java.util.Enumeration;
064import java.util.Iterator;
065import java.util.logging.Level;
066import java.util.List;
067import javax.servlet.http.HttpServletRequest;
068import javax.servlet.http.HttpServletResponse;
069import javax.xml.bind.JAXBException;
070import javax.xml.namespace.QName;
071import org.w3c.dom.Element;
072
073/**
074 *  This class provides the interface and implementation for supporting 
075 *  resource owner interaction.  <code>WSC</code> and <code>WSP</code> would
076 *  collaborate with the singleton object instance of this class to provide
077 *  and use resource owner interaction.  
078 *  @supported.api
079 */
080public class InteractionManager {
081
082    /**
083     *
084     * Name of URL query parameter to be used by <code>WSC</code> to include
085     * <code>returnToURL</code>, while redirecting user agent to
086     * <code>WSP</code>.
087     *
088     * @supported.all.api
089     */
090    public static final String RETURN_TO_URL = "ReturnToURL";
091
092    /**
093     * Name of suggested URL query parameter to be used by <code>WSC</code>
094     * to include an ID to refer to request message that led to user agent
095     * redirect.
096     *
097     * @supported.api
098     */
099    public static final String REQUEST_ID = "RequestID";
100
101    /**
102     * Name of URL query parameter to be used by <code>WSC</code> to include 
103     * <code>providerID</code> of <code>IDP</code>, that was used to
104     * authenticate user.
105     *
106     * @supported.api
107     */
108    public static final String IDP = "IDP";
109
110    /**
111     * Name of URL query parameter to be used by <code>WSP</code> to include 
112     * an ID to indicate that user agent is redirected back to
113     * <code>WSC</code> from <code>WSP</code>
114     *
115     * @supported.api
116     */
117    public static final String RESEND_MESSAGE = "ResendMessage";
118
119    /**
120     * Name space URI of interaction service 
121     * @supported.api
122     */
123    public static final String INTERACTION_NAMESPACE 
124            = "urn:liberty:is:2003-08"; 
125
126    /** 
127     * <code>QName</code> for <code>s:Server</code>
128     * <code>s</code> - soap name space prefix
129     */
130    public static final QName QNAME_SERVER
131            = new QName(SOAPBindingConstants.NS_SOAP, "Server");
132    /** 
133     * <code>QName</code> for <code>is:interactIfNeeded</code>
134     * <code>is</code> - name space prefix for interaction service
135     */
136    public static final QName QNAME_INTERACT_IF_NEEDED 
137            = new QName(INTERACTION_NAMESPACE, "interactIfNeeded", "is");
138
139    /** 
140     * <code>QName</code> for <code>is:doNotInteract</code>
141     * <code>is</code> - name space prefix for interaction service
142     */
143    public static final QName QNAME_DO_NOT_INTERACT
144            = new QName(INTERACTION_NAMESPACE, "doNotInteract", "is");
145
146    /** 
147     * <code>QName</code> for <code>is:doNotInteractForData</code>
148     * <code>is</code> - name space prefix for interaction service
149     */
150    public static final QName QNAME_DO_NOT_INTERACT_FOR_DATA
151            = new QName(INTERACTION_NAMESPACE, "doNotInteractForData", "is");
152
153    /** 
154     * <code>QName</code> for <code>is:interactionRequired</code>
155     * <code>is</code> - name space prefix for interaction service
156     */
157    public static final QName QNAME_INTERACTION_REQUIRED 
158            = new QName(INTERACTION_NAMESPACE, "interactionRequired");
159
160    /** 
161     * <code>QName</code> for <code>is:forData</code>
162     * <code>is</code> - name space prefix for interaction service
163     */
164    public static final QName QNAME_INTERACTION_REQUIRED_FOR_DATA 
165            = new QName(INTERACTION_NAMESPACE, "forData");
166
167    /** 
168     * <code>QName</code> for <code>is:timeNotSufficient</code>
169     * <code>is</code> - name space prefix for interaction service
170     */
171    public static final QName QNAME_INTERACTION_TIME_NOT_SUFFICEINT 
172            = new QName(INTERACTION_NAMESPACE, "timeNotSufficient");
173
174    /** 
175     * <code>QName</code> for <code>is:timeOut</code>
176     * <code>is</code> - name space prefix for interaction service
177     */
178    public static final QName QNAME_INTERACTION_TIMED_OUT 
179            = new QName(INTERACTION_NAMESPACE, "timeOut");
180
181    /** 
182     * <code>QName</code> for <code>is:interactIfNeeded</code>
183     * <code>is</code> - name space prefix for interaction service
184     */
185    public static final QName QNAME_INTERACTION_CAN_NOT_DETERMINE_REQUEST_HOST
186            = new QName(INTERACTION_NAMESPACE,
187                        "canNotDetermineRequestHostName");
188
189    /** 
190     * Constant string to indicate generic server error
191     */
192    public static final String SERVER_ERROR = "Server Error";
193
194    static final String TRANS_ID = "TransID";
195
196    private static InteractionManager interactionManager = null;
197    private static Debug debug = Debug.getInstance("libIDWSF");
198
199    private static final String REDIRECT_URL = "redirectURL";
200
201    private static final String FAULT_ACTOR 
202            = "http://schemas.xmlsoap.org/soap/actor/next";
203
204    private static final String INTERACTION_RB_NAME = "libInteraction";
205
206    private InteractionCache cache = new InteractionCache();
207    private com.sun.identity.liberty.ws.interaction.jaxb.ObjectFactory 
208             objectFactory;
209    private InteractionConfig interactionConfig;
210
211    /**
212     * Gets singleton object instance of <code>InteractionManager</code>
213     * @return singleton object instance of <code>InteractionManager</code>
214     *
215     * @supported.api
216     */
217    synchronized public static InteractionManager getInstance() {
218        if (interactionManager == null) {
219            interactionManager = new InteractionManager();
220        }
221        return interactionManager;
222    }
223
224    private InteractionManager() {
225        objectFactory = JAXBObjectFactory.getObjectFactory();
226        interactionConfig = InteractionConfig.getInstance();
227        if (debug.messageEnabled()) {
228            debug.message(
229                    "InteractionManager():constructed singleton instance");
230        }
231    }
232
233    /**
234     * Sends SOAP request to <code>WSP</code>.  
235     * This would be invoked at <code>WSC</code> side.
236     *
237     * @param requestMessage request message.
238     * @param connectTo SOAP URL to which to send the SOAP request
239     * @param certAlias SOAP Client Certificate Alias
240     * @param soapAction SOAP Action Attribute
241     * @param returnToURL URL to which to redirect user agent after
242     *                   <code>WSP</code> - resource owner interactions
243     * @param httpRequest HTTP request object of current user agent request
244     * @param httpResponse HTTP response object of current user agent request
245     * @return response SOAP response message sent by <code>WSP</code>.
246     *
247     * @throws InteractionException for generic interaction error
248     * @throws InteractionRedirectException if user agent is redirected to 
249     *                     <code>WSP</code> for resource owner interactions
250     * @throws SOAPBindingException  for generic SOAP binding errors
251     * @throws SOAPFaultException if the response message has SOAP fault
252     *
253     * @supported.api
254     */
255    public Message sendRequest(Message requestMessage, 
256            String connectTo,
257            String certAlias,
258            String soapAction,
259            String returnToURL,
260            HttpServletRequest httpRequest, 
261            HttpServletResponse httpResponse) 
262            throws InteractionException, InteractionRedirectException, 
263            SOAPBindingException, SOAPFaultException  {
264
265        if (debug.messageEnabled()) {
266            debug.message("InteractionManager.sendRequest():"
267                    + " entering with messageID="
268                    + requestMessage.getCorrelationHeader().getMessageID()
269                    + ":refToMessageID="
270                    + requestMessage.getCorrelationHeader().getRefToMessageID()
271                    + ":requestMessage=" + requestMessage);
272        }
273
274        // construct and set UserInteraction element in requestMessage
275        if (interactionConfig.wscIncludesUserInteractionHeader()) {
276            Enumeration locales = httpRequest.getLocales();
277            List acceptLanguages = new ArrayList();
278            while (locales.hasMoreElements()) {
279                acceptLanguages.add(locales.nextElement().toString());
280            }
281            if (debug.messageEnabled()) {
282                debug.message("InteractionManager.sendRequest():"
283                        + "Accept-Language specified by httpRequest="
284                        + acceptLanguages);
285            }
286            UserInteractionElement ue = createUserInteractionElement(
287                    acceptLanguages);
288            String id = SAMLUtils.generateID();
289            ue.setId(id);
290            if (ue != null) {
291                try {
292                    Element element = Utils.convertJAXBToElement(ue);
293                    requestMessage.setOtherSOAPHeader(
294                            element,
295                            id);
296
297                } catch (JAXBException je) {
298                    debug.error("InteractionManager.sendRequest():"
299                    + "not setting userInteractionHeader:"
300                    + "can not convert JAXBObject to Element", je);
301                } 
302            }
303        }
304
305        Message responseMessage = null;
306        try {
307            if (debug.messageEnabled()) {
308                debug.message("InteractionManager.sendRequest():"
309                        + "invoking soap Client.sendRequest():"
310                        + ":requestMessage=" + requestMessage
311                        + ":connecTo=" + connectTo);
312            }
313            if (LogUtil.isLogEnabled()) {
314                String[] objs =new String[1];
315                objs[0] = requestMessage.getCorrelationHeader()
316                        .getMessageID();
317                LogUtil.access(Level.INFO, LogUtil.IS_SENDING_MESSAGE,objs);
318            }
319            responseMessage = Client.sendRequest(requestMessage, 
320                connectTo, certAlias, soapAction);
321        } catch (SOAPFaultException sfe) {
322            if (debug.messageEnabled()) {
323                debug.message("InteractionManager.sendRequest():"
324                        + " catching SOAPFaultException="
325                        + sfe);
326            }
327            String redirectURL = getRedirectURL(sfe);
328            if(redirectURL == null) {
329               throw sfe;
330            }
331            String responseID = getResponseID(sfe);
332            responseMessage = handleRedirectRequest(requestMessage, 
333                    redirectURL, responseID, connectTo, certAlias, soapAction,
334                    returnToURL, httpRequest, httpResponse);
335        }
336        if (debug.messageEnabled()) {
337            debug.message("InteractionManager.sendRequest():"
338                    + " returning response message=" + responseMessage);
339        }
340        if (LogUtil.isLogEnabled()) {
341            String[] objs = new String[2];
342            objs[0] = responseMessage.getCorrelationHeader().getMessageID();
343            objs[1] = requestMessage.getCorrelationHeader().getMessageID();
344            LogUtil.access(Level.INFO,LogUtil.IS_RETURNING_RESPONSE_MESSAGE,
345                           objs);
346        }
347        return responseMessage;
348    }
349
350    /**
351     * Resends a previously cached SOAP request message to <code>WSP</code>.
352     * This would be invoked at <code>WSC</code> side. Message ID for the cached
353     * message should be provided as value of <code>REQUEST_ID</code> query
354     * parameter in <code>httpRequest</code>.
355     *
356     * @param returnToURL URL to which to redirect user agent after
357     *        <code>WSP</code> - resource owner interactions
358     * @param httpRequest HTTP request object of current user agent request
359     * @param httpResponse HTTP response object of current user agent request
360     * @return response SOAP message sent by <code>WSP</code>.
361     *
362     * @throws InteractionException for generic interaction error
363     * @throws InteractionRedirectException if user agent is redirected to 
364     *         <code>WSP</code> for resource owner interactions
365     * @throws SOAPBindingException if there are generic SOAP errors
366     * @throws SOAPFaultException if the response message has SOAP fault
367     *
368     * @see #REQUEST_ID
369     *
370     * @supported.api
371     */
372    public Message resendRequest(String returnToURL,
373            HttpServletRequest httpRequest,  
374            HttpServletResponse httpResponse) 
375            throws InteractionRedirectException, InteractionException, 
376            SOAPBindingException, SOAPFaultException {
377
378        return resendRequest(returnToURL, httpRequest, httpResponse, null);
379    }
380
381    /**
382     * Resends a SOAP request message to <code>WSP</code>.
383     * This would be invoked at <code>WSC</code> side.
384     *
385     * @param returnToURL URL to which to redirect user agent after
386     *                    <code>WSP</code> - resource owner interactions
387     * @param httpRequest HTTP request object of current user agent request
388     * @param httpResponse HTTP response object of current user agent request
389     * @param requestMessage SOAP message to be resent.
390     * @return response SOAP message sent by <code>WSP</code>.
391     *
392     * @throws InteractionException for generic interaction error
393     * @throws InteractionRedirectException if user agent is redirected to 
394     *                     <code>WSP</code> for resource owner interactions
395     * @throws SOAPBindingException  for generic SOAP errors
396     * @throws SOAPFaultException if the response message has SOAP fault
397     *
398     * @supported.api
399     */
400    public Message resendRequest(String returnToURL,
401            HttpServletRequest httpRequest,  
402            HttpServletResponse httpResponse, Message requestMessage) 
403            throws InteractionRedirectException, InteractionException, 
404            SOAPBindingException, SOAPFaultException {
405
406        if (debug.messageEnabled()) {
407            debug.message("InteractionManager.resendRequest():entering ");
408        }
409
410        //check for RESEND_MESSAGE parameter
411        String messageID = httpRequest.getParameter(RESEND_MESSAGE);
412        if (messageID == null) {
413            debug.error("InteractionManager.resend():"
414                    + " request without " + RESEND_MESSAGE + " in requestURL=" 
415                    + httpRequest.getRequestURL().toString());
416            String objs[] = {RESEND_MESSAGE};
417            throw new InteractionException(INTERACTION_RB_NAME, 
418                    "missing_query_parameter", objs);
419        }
420
421        //check whether WSP advised not to resend
422        if ( (messageID == "0") || (messageID.equals("false")) ) {
423            debug.error("InteractionManager.resend():"
424                    + " resend not allowed in requestURL=" 
425                    + httpRequest.getRequestURL().toString());
426            throw new InteractionException(INTERACTION_RB_NAME, 
427                    "wsp_advised_not_to_resend", null);
428        }
429
430        //check for original REQUEST_ID
431        messageID = httpRequest.getParameter(REQUEST_ID);
432        if (messageID == null) {
433            debug.error("InteractionManager.resend():"
434                    + " request without " +  REQUEST_ID + " in requestURL=" 
435                    +  httpRequest.getRequestURL().toString());
436            String[] objs = {REQUEST_ID};
437            throw new InteractionException(INTERACTION_RB_NAME, 
438                    "request_missing_query_parameter", objs);
439        }
440
441        String connectTo = getConnectTo(messageID);
442        if (connectTo == null) {
443            debug.error("InteractionManager.resend():"
444                    + " old connectTo not  found for messageID=" 
445                    + messageID);
446            throw new InteractionException(INTERACTION_RB_NAME, 
447                    "old_connectTo_not_found", null);
448        }
449
450        if (requestMessage == null) {
451            if (debug.messageEnabled()) {
452                debug.message("InteractionManager.resendRequest():"
453                        + "invoking with null requestMessage:"
454                        + "old cached message would be used");
455            }
456            Message oldMessage = getRequestMessage(messageID);
457            if (oldMessage == null) {
458                debug.error("InteractionManager.resend():"
459                        + " old message not  found for messageID=" 
460                        + messageID);
461                throw new InteractionException(INTERACTION_RB_NAME, 
462                        "old_message_not_found", null);
463            }
464            requestMessage = oldMessage;
465        } else {
466            if (debug.messageEnabled()) {
467                debug.message("InteractionManager.resendRequest():"
468                        + "invoking with non null requestMessage");
469            }
470        }
471
472        CorrelationHeader ch = new CorrelationHeader();
473        ch.setRefToMessageID(InteractionManager.getInstance()
474                .getRequestMessageID(messageID));
475        requestMessage.setCorrelationHeader(ch);
476
477        if (debug.messageEnabled()) {
478            debug.message("InteractionManager.resendRequest():"
479                    + "invoking InteractionManager.sendRequest():"
480                    + "with requestMessage=" + requestMessage
481                    + ":returnToURL=" + returnToURL);
482        }
483
484        if (LogUtil.isLogEnabled()) {
485            String[] objs =new String[2];
486            objs[0] = messageID;
487            objs[1] = ch.getMessageID();
488            LogUtil.access(Level.INFO,LogUtil.IS_RESENDING_MESSAGE,objs);
489        }
490        Message responseMessage = sendRequest(requestMessage, connectTo, 
491                getClientCert(messageID), getSoapAction(messageID),
492                returnToURL, httpRequest,  httpResponse);
493        if (debug.messageEnabled()) {
494            debug.message("InteractionManager.resendRequest():"
495                    + " returning responseMessage=" + responseMessage);
496        }
497        return responseMessage;
498    }
499
500    private Message handleRedirectRequest(Message requestMessage, 
501            String redirectURL, String messageID, String connectTo, 
502            String certAlias, String soapAction, String returnToURL, 
503            HttpServletRequest httpRequest,  HttpServletResponse httpResponse)
504            throws InteractionRedirectException, InteractionException {
505        
506        if (debug.messageEnabled()) {
507            debug.message("InteractionManager.handleRedirectRequest():"
508                    + "entering with redirectURL="
509                    + redirectURL);
510        }
511
512        //redirectURL should not have ReturnToURL parameter
513        if (!(redirectURL.indexOf(RETURN_TO_URL + "=") == -1)) {
514            debug.error(
515                    "InteractionManager.handleRedirectRequest():"
516                    + "Invalid redirectURL - illegal parameter " 
517                    + RETURN_TO_URL
518                    + " in redirectURL=" + redirectURL); 
519            String objs[] = {RETURN_TO_URL, REDIRECT_URL};
520            throw new InteractionException(INTERACTION_RB_NAME,
521                    "illegal_parameter_in_redirectURL", objs);
522        }
523
524        //redirectURL should not have IDP parameter
525        if (!(redirectURL.indexOf(IDP + "=") == -1)) {
526            debug.error(
527                    "InteractionManager.handleRedirectRequest():"
528                    + "Invalid redirectURL - illegal parameter:" 
529                    + IDP 
530                    + " in redirectURL=" + redirectURL); 
531            String objs[] = {IDP, REDIRECT_URL};
532            throw new InteractionException(INTERACTION_RB_NAME,
533                    "illegal_parameter_in_redirectURL", objs);
534        }
535
536        //redirectURL should be https
537        if (InteractionConfig.getInstance().wscEnforcesHttpsCheck()
538                    && (redirectURL.indexOf("https") != 0) ) {
539            debug.error(
540                    "InteractionManager.handleRedirectRequest():"
541                    + "Invalid Request " 
542                    + InteractionManager.REDIRECT_URL
543                    + " not https"
544                    + " in redirectURL=" + redirectURL); 
545            throw new InteractionException(INTERACTION_RB_NAME,
546                    "redirectURL_not_https", null);
547        }
548
549        //redirectURL should point to connectTo host
550        if (InteractionConfig.getInstance()
551                    .wspEnforcesReturnToHostEqualsRequestHost()
552                    && !checkRedirectHost(redirectURL, connectTo)) {
553            debug.error(
554                    "InteractionManager.handleRedirectRequest():"
555                    + "Invalid Request redirectToHost differs from " 
556                    + " connectToHost:"
557                    + " in redirectURL=" + redirectURL 
558                    + ":connectTo=" + connectTo); 
559            throw new InteractionException(INTERACTION_RB_NAME,
560                    "redirectURL_differs_from_connectTo", null);
561        }
562
563        String requestID = requestMessage.getCorrelationHeader().getMessageID();
564        setRequestMessage(requestID, requestMessage);
565        setConnectTo(requestID, connectTo);
566        setSoapAction(requestID, soapAction);
567        setClientCert(requestID, certAlias);
568        setRequestMessageID(requestID, messageID);
569        if (debug.messageEnabled()) {
570            debug.message("InteractionManager.handleRedirectRequest():cached "
571                    + " request message for messageID=" + messageID
572                    + ":requestID=" + requestID
573                    + ":requestMessage:" + (requestMessage == null) );
574        }
575
576        returnToURL = returnToURL + "?" + REQUEST_ID + "=" + requestID;
577        redirectURL = redirectURL +"&"+ RETURN_TO_URL + "="
578                + urlEncodeQueryParameterNameOrValue(returnToURL);
579        if (debug.messageEnabled()) {
580            debug.message("InteractionManager.handleRedirectRequest():"
581                    + "redirecting user agent to redirectURL= "
582                    + redirectURL);
583        }
584        try {
585            httpResponse.sendRedirect(redirectURL);
586        } catch (IOException ioe) { //IOException
587            debug.error("InteractionManager.handleRedirectRequest():"
588                    + " catching IOException", ioe);
589            throw new InteractionException(INTERACTION_RB_NAME,
590                    "IOException_in_Interaction_Manager", null);
591        }
592        if (debug.messageEnabled()) {
593            debug.message("InteractionManager.handleRedirectRequest():"
594                    + "redirected user agent to redirectURL= "
595                    + redirectURL);
596        }
597        if (LogUtil.isLogEnabled()) {
598            String[] objs =new String[1];
599            objs[0] = requestID;
600            LogUtil.access(Level.INFO,LogUtil.IS_REDIRECTED_USER_AGENT,objs);
601        }
602        throw new InteractionRedirectException(requestID);
603        //return returnMessage;
604    }
605
606    /**
607     * Handles resource owner interactions on behalf of <code>WSP</code>.
608     * This is invoked at <code>WSP</code> side.
609     *
610     * @param requestMessage SOAP request that requires resource
611     *             owner interactions
612     * @param inquiryElement query that <code>WSP</code> wants to pose to
613     *        resource owner.
614     * @return SOAP message that contains <code>InteractionResponse</code>,
615     *         gathered by <code>InteractionManager</code>
616     *
617     * @throws InteractionException for generic interaction error
618     * @throws InteractionSOAPFaultException if a SOAP fault
619     *         has to be returned  to <code>WSC</code>
620     * @throws SOAPFaultException if the response message has SOAP fault
621     *
622     * @deprecated
623     *
624     */
625    public Message handleInteraction(Message requestMessage,
626            InquiryElement inquiryElement) throws InteractionException, 
627            InteractionSOAPFaultException, SOAPFaultException {
628        return handleInteraction(requestMessage, inquiryElement, null);
629    }
630
631    /**
632     * Handles resource owner interactions on behalf of <code>WSP</code>.
633     * This is invoked at <code>WSP</code> side.
634     *
635     * @param requestMessage SOAP request that requires resource
636     *             owner interactions
637     * @param inquiryElement query that <code>WSP</code> wants to pose to
638     *        resource owner 
639     * @param language language in which the query page needs to be rendered
640     * @return SOAP message that contains <code>InteractionResponse</code>,
641     *         gathered by <code>InteractionManager</code>
642     *
643     * @throws InteractionException for generic interaction error
644     * @throws InteractionSOAPFaultException if a SOAP fault
645     *         has to be returned  to <code>WSC</code>
646     * @throws SOAPFaultException if the response message has SOAP fault
647     *
648     * @supported.api
649     */
650    public Message handleInteraction(Message requestMessage,
651           InquiryElement inquiryElement, String language)
652           throws InteractionException, 
653           InteractionSOAPFaultException, SOAPFaultException {
654
655        if (debug.messageEnabled()) {
656            debug.message("InteractionManager.handleInteraction():entering");
657        }
658
659        //Check redirect is enabled for WSP
660        if (!interactionConfig.wspSupportsRedirect()) {
661            if (debug.warningEnabled()) {
662                debug.warning("InteractionManager.handleInteraction():"
663                        + " WSP requests for interaction:wspWillRedirect=" 
664                        + interactionConfig.wspSupportsRedirect());
665                debug.warning("InteractionManager.handleInteraction():"
666                        + "throwing InteractionException");
667            }
668            throw new InteractionException(INTERACTION_RB_NAME,
669                    "wsp_does_not_support_interaction", null);
670        }
671
672        //Check wsc provided UserInteraction header
673        UserInteractionElement ue 
674                = getUserInteractionElement(requestMessage);
675        if (ue == null) {
676            SOAPFaultException sfe = newRedirectFaultError(
677                    QNAME_INTERACTION_REQUIRED);
678            if (debug.warningEnabled()) {
679                debug.warning("InteractionManager.handleInteraction():"
680                        + " WSP requests for interaction - WSC did not "
681                        + " provide UserInteractionHeader");
682                debug.warning("InteractionManager.handleInteraction():"
683                        + "throwing InteractionSOAPFaultException="
684                        + sfe);
685            }
686            throw new InteractionSOAPFaultException(sfe);
687        }
688
689        //Check WSC is willing to redirect
690        if (ue.isRedirect() == false) {
691            SOAPFaultException sfe = newRedirectFaultError(
692                    QNAME_INTERACTION_REQUIRED);
693            if (debug.warningEnabled()) {
694                debug.warning("InteractionManager.handleInteraction():"
695                        + "WSP rquests for interaction - WSC  "
696                        + " says redirect=false");
697                debug.warning("InteractionManager.handleInteraction():"
698                        + "throwing InteractionSOAPFaultException="
699                        + sfe);
700            }
701            throw new InteractionSOAPFaultException(sfe);
702        }
703
704        //Check WSC allowed interaction
705        if (ue.getInteract().equals(QNAME_DO_NOT_INTERACT)) {
706            SOAPFaultException sfe = newRedirectFaultError(
707                    QNAME_INTERACTION_REQUIRED);
708            if (debug.warningEnabled()) {
709                debug.warning("InteractionManager.handleInteraction():"
710                        + "WSP rquests for interaction - WSC  "
711                        + " UserInteractionHeader says doNotInteract");
712                debug.warning("InteractionManager.handleInteraction():"
713                        + "throwing InteractionSOAPFaultException="
714                        + sfe);
715            }
716            throw new InteractionSOAPFaultException(sfe);
717        }
718
719        //Check WSC allowed interaction for data
720        if (interactionConfig.wspRedirectsForData()
721                    && ue.getInteract().equals(
722                    QNAME_DO_NOT_INTERACT_FOR_DATA)) {
723            SOAPFaultException sfe = newRedirectFaultError(
724                    QNAME_INTERACTION_REQUIRED_FOR_DATA);
725            if (debug.warningEnabled()) {
726                debug.warning("InteractionManager.handleInteraction():"
727                        + "WSP rquests interaction for data - WSC  "
728                        + " UserInteractionHeader says doNotInteractForData");
729                debug.warning("InteractionManager.handleInteraction():"
730                        + "throwing InteractionSOAPFaultException="
731                        + sfe);
732            }
733            throw new InteractionSOAPFaultException(sfe);
734        }
735
736        //Check WSP will not exceed maxInteractionTime specified by WSC
737        BigInteger uemi =  ue.getMaxInteractTime();
738        if ( (uemi != null) && (interactionConfig.getWSPRedirectTime() 
739                    > uemi.intValue()) ) {
740            SOAPFaultException sfe = newRedirectFaultError(
741                    QNAME_INTERACTION_TIME_NOT_SUFFICEINT); 
742            if (debug.warningEnabled()) {
743                debug.warning("InteractionManager.handleInteraction():"
744                        + "WSP inteaction time =" 
745                        + interactionConfig.getWSPRedirectTime() 
746                        + " exceeds WSC maxInteractTime= "
747                        + ue.getMaxInteractTime());
748                debug.warning("InteractionManager.handleInteraction():"
749                        + "throwing InteractionSOAPFaultException="
750                        + sfe);
751            }
752            throw new InteractionSOAPFaultException(sfe);
753        }
754
755        String requestMessageID 
756                = requestMessage.getCorrelationHeader().getMessageID();
757        SOAPFaultException sfe =
758                newRedirectFault(requestMessageID);
759        String redirectResponseID = getResponseID(sfe);
760        String requestIP 
761                = requestMessage.getIPAddress();
762        String requestHost = null;
763        if (interactionConfig.wspEnforcesReturnToHostEqualsRequestHost()) {
764            try {
765                InetAddress inetAddress = InetAddress.getByName(requestIP);
766                //requestHost = inetAddress.getCanonicalHostName();
767                requestHost = inetAddress.getHostName();
768                if (debug.messageEnabled()) {
769                    debug.message("InteractionManager.handleInteraction():"
770                            + " caching requestHost=" + requestHost
771                            + ", for redirectResponseID= " 
772                            + redirectResponseID);
773                }
774                setRequestHost(redirectResponseID, requestHost);
775            } catch (UnknownHostException uhe) {
776                debug.error("InteractionManager.handleInteraction():"
777                        + " can not resolve host name", uhe);
778                debug.error("InteractionManager.handleInteraction():"
779                        + " throwing InteractionSOAPFaultException", sfe);
780                SOAPFaultException sfe1 = newRedirectFaultError(
781                        QNAME_INTERACTION_CAN_NOT_DETERMINE_REQUEST_HOST); 
782                throw new InteractionSOAPFaultException(sfe1);
783            }
784        }
785
786        setInquiryElement(redirectResponseID, inquiryElement);
787        setRequestMessageID(redirectResponseID, requestMessageID);
788        setLanguage(redirectResponseID, language);
789        if (debug.messageEnabled()) {
790            debug.message("InteractionManager.handleInteraction():"
791                    + " throwing InteractionSOAPFaultException "
792                    + " to redirect user agent="
793                    + sfe);
794        }
795
796        throw new InteractionSOAPFaultException(sfe);
797        //return responseMessage;
798
799    }
800
801    private void setInquiryElement(String messageID, 
802            InquiryElement inquiryElement) {
803        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
804        if ( cacheEntry == null) {
805            cacheEntry = new CacheEntry(messageID);
806            cache.putCacheEntry(messageID, cacheEntry);
807        }
808        cacheEntry.setInquiryElement(inquiryElement);
809
810    }
811
812    InquiryElement getInquiryElement(String messageID) {
813        InquiryElement inquiryElement = null;
814        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
815        if ( cacheEntry != null) {
816            inquiryElement = cacheEntry.getInquiryElement();
817        }
818        return inquiryElement;
819    }
820
821    void setInteractionResponseElement(String messageID, 
822            InteractionResponseElement interactionResponse) {
823        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
824        if ( cacheEntry == null) {
825            cacheEntry = new CacheEntry(messageID);
826            cache.putCacheEntry(messageID, cacheEntry);
827        }
828        cacheEntry.setInteractionResponseElement(interactionResponse);
829    }
830
831    /**
832     * Gets interaction response that was gathered from resource owner
833     * by <code>InteractionManager</code>
834     *
835     * @param requestMessage request message.
836     *
837     * @return interaction response that was gathered by
838     *         <code>InteractionManager</code>.
839     *
840     * @throws InteractionException for interaction error
841     *
842     * @supported.api
843     */
844    public InteractionResponseElement getInteractionResponseElement(
845            Message requestMessage) 
846            throws InteractionException {
847        InteractionResponseElement interactionResponseElement = null;
848        CorrelationHeader ch = requestMessage.getCorrelationHeader();
849        String messageID = ch.getRefToMessageID();
850        CacheEntry cacheEntry = null;
851        if (messageID != null) {
852            cacheEntry = cache.getCacheEntry(messageID);
853            if ( cacheEntry != null) {
854                interactionResponseElement 
855                        = cacheEntry.getInteractionResponseElement();
856            }
857            if (debug.messageEnabled()) {
858                debug.message("InteractionManager.getInteractionResponseElement():"
859                        + "for messageID=" + messageID + ":"
860                        + "responseElement="
861                        + (interactionResponseElement != null));
862            }
863        }
864        if (LogUtil.isLogEnabled()) {
865            String[] objs =new String[3];
866            objs[0] = ch.getMessageID();
867            objs[1] = ch.getRefToMessageID();
868            objs[2] = (cacheEntry == null) ? "NULL" : "NOT NULL";
869            LogUtil.access(Level.INFO,LogUtil.IS_RETURNING_RESPONSE_ELEMENT,
870                objs);
871        }
872        return interactionResponseElement;
873    }
874
875    private void setRequestMessage(String messageID, Message requestMessage) {
876        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
877        if ( cacheEntry == null) {
878            cacheEntry = new CacheEntry(messageID);
879            cache.putCacheEntry(messageID, cacheEntry);
880        }
881        cacheEntry.setRequestMessage(requestMessage);
882        if (debug.messageEnabled()) {
883            debug.message("InteractionManager.setRequestMessage():"
884                    + " cached  request message for messageID="
885                    + messageID  
886                    + ":requestMessage:" + (requestMessage == null) );
887        }
888    }
889
890    private Message getRequestMessage(String messageID) {
891        Message requestMessage = null;
892        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
893        if ( cacheEntry != null) {
894            requestMessage = cacheEntry.getRequestMessage();
895        }
896        if (debug.messageEnabled()) {
897            debug.message("InteractionManager.getRequestMessage():"
898                    + " looking up request message for messageID="
899                    + messageID  
900                    + ":requestMessage=" + (requestMessage == null) );
901        }
902        return requestMessage;
903    }
904
905    private void setRequestMessageID(String messageID,
906            String requestMessageID) {
907        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
908        if ( cacheEntry == null) {
909            cacheEntry = new CacheEntry(messageID);
910            cache.putCacheEntry(messageID, cacheEntry);
911        }
912        cacheEntry.setRequestMessageID(requestMessageID);
913    }
914
915    String getRequestMessageID(String messageID) {
916        String requestMessageID = null;
917        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
918        if ( cacheEntry != null) {
919            requestMessageID = cacheEntry.getRequestMessageID();
920        }
921        return requestMessageID;
922    }
923
924
925    void setReturnToURL(String messageID, String returnToURL) {
926        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
927        if ( cacheEntry == null) {
928            cacheEntry = new CacheEntry(messageID);
929            cache.putCacheEntry(messageID, cacheEntry);
930        }
931        cacheEntry.setReturnToURL(returnToURL);
932    }
933
934    String getReturnToURL(String messageID) {
935        String returnToURL = null;
936        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
937        if ( cacheEntry != null) {
938            returnToURL = cacheEntry.getReturnToURL();
939        }
940        return returnToURL;
941    }
942
943    void setRequestHost(String messageID, String requestHost) {
944        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
945        if ( cacheEntry == null) {
946            cacheEntry = new CacheEntry(messageID);
947            cache.putCacheEntry(messageID, cacheEntry);
948        }
949        cacheEntry.setRequestHost(requestHost);
950    }
951
952    String getRequestHost(String messageID) {
953        String requestHost = null;
954        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
955        if ( cacheEntry != null) {
956            requestHost = cacheEntry.getRequestHost();
957        }
958        return requestHost;
959    }
960
961    private void setConnectTo(String messageID, String ConnectTo) {
962        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
963        if ( cacheEntry == null) {
964            cacheEntry = new CacheEntry(messageID);
965            cache.putCacheEntry(messageID, cacheEntry);
966        }
967        cacheEntry.setConnectTo(ConnectTo);
968    }
969
970   
971    private void setSoapAction(String messageID, String soapAction) {
972        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
973        if ( cacheEntry == null) {
974            cacheEntry = new CacheEntry(messageID);
975            cache.putCacheEntry(messageID, cacheEntry);
976        }
977        cacheEntry.setSoapAction(soapAction);
978    }
979
980    private void setClientCert(String messageID, String certAlias) {
981        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
982        if ( cacheEntry == null) {
983            cacheEntry = new CacheEntry(messageID);
984            cache.putCacheEntry(messageID, cacheEntry);
985        }
986        cacheEntry.setClientCert(certAlias);
987    }
988
989    private String getConnectTo(String messageID) {
990        String connectTo = null;
991        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
992        if ( cacheEntry != null) {
993            connectTo = cacheEntry.getConnectTo();
994        }
995        return connectTo;
996    }
997
998    private String getClientCert(String messageID) {
999        String clientCert = null;
1000        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
1001        if ( cacheEntry != null) {
1002            clientCert = cacheEntry.getClientCert();
1003        }
1004        return clientCert;
1005    }
1006
1007    private String getSoapAction(String messageID) {
1008        String soapAction = null;
1009        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
1010        if ( cacheEntry != null) {
1011            soapAction = cacheEntry.getSoapAction();
1012        }
1013        return soapAction;
1014    }
1015
1016    private void setLanguage(String messageID, String language) {
1017        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
1018        if ( cacheEntry == null) {
1019            cacheEntry = new CacheEntry(messageID);
1020            cache.putCacheEntry(messageID, cacheEntry);
1021        }
1022        cacheEntry.setLanguage(language);
1023    }
1024
1025    String getLanguage(String messageID) {
1026        String language = null;
1027        CacheEntry cacheEntry = cache.getCacheEntry(messageID);
1028        if ( cacheEntry != null) {
1029            language = cacheEntry.getLanguage();
1030        }
1031        return language;
1032    }
1033
1034    private UserInteractionElement createUserInteractionElement(
1035            List acceptLanguages) {
1036        UserInteractionElement ue = null; 
1037        try {
1038            ue =objectFactory.createUserInteractionElement();
1039
1040            ue.setInteract(interactionConfig
1041                    .getWSCSpecifiedInteractionChoice());
1042            ue.setRedirect(interactionConfig.wscSupportsRedirect());
1043            ue.setMaxInteractTime(
1044                    java.math.BigInteger.valueOf(interactionConfig
1045                    .getWSCSpecifiedMaxInteractionTime()));
1046            ue.getLanguage().addAll(acceptLanguages);
1047        } catch (JAXBException je) {
1048            debug.error("InteractionManager.createUserInteractionElement():"
1049                    + " can not create UserInteractionElement", je);
1050        }
1051        return ue;
1052    }
1053
1054    static UserInteractionElement getUserInteractionElement(
1055            Message message) {
1056        UserInteractionElement ue = null; 
1057        List list = message.getOtherSOAPHeaders();
1058        try {
1059            list = Utils.convertElementToJAXB(list);
1060        } catch (JAXBException je) {
1061            debug.error("InteractionManager.getUserInteractionElement():"
1062                    + "not able to get userInteractionElement:"
1063                    + "can not convert Element to JAXBObject", je);
1064            return null;
1065        }
1066        Iterator iter = list.iterator();
1067        while (iter.hasNext()) {
1068            Object obj = (Object)iter.next();
1069            if (obj instanceof UserInteractionElement) {
1070                ue = (UserInteractionElement)obj;
1071                break;
1072            }
1073        }
1074        return ue;
1075    }
1076
1077    private SOAPFaultException newRedirectFault(String messageID) {
1078        RedirectRequestElement re = null;
1079        try{
1080            re = objectFactory.createRedirectRequestElement();
1081
1082        } catch (JAXBException je) {
1083            debug.error("InteractionManager.newRedirectFault():"
1084                    + " can not create RedirectRequestElement", je);
1085        }
1086
1087        CorrelationHeader ch = new CorrelationHeader();
1088        String responseID = ch.getMessageID();
1089        ch.setRefToMessageID(messageID);
1090
1091        String redirectUrl = null;
1092        String lbRedirectUrl = interactionConfig.getLbWSPRedirectHandler();
1093        String wspRedirectUrl = interactionConfig.getWSPRedirectHandler();
1094        if(debug.messageEnabled()) {
1095            debug.message("InteractionManager.newRedirectURLFault():"
1096                    + "wspRedirectURL:" + wspRedirectUrl
1097                    + ", lbRedirectUrl:" + lbRedirectUrl);
1098        }
1099        if (lbRedirectUrl == null) {
1100            redirectUrl = wspRedirectUrl + "?" + TRANS_ID + "=" + responseID;
1101            if(debug.messageEnabled()) {
1102                debug.message("InteractionManager.newRedirectURLFault():"
1103                        + "lbRedirectURL is null, rediectUrl:"
1104                        + redirectUrl);
1105            }
1106        } else { //lbRedirectUrl defined
1107            redirectUrl = lbRedirectUrl + "?" + TRANS_ID + "=" + responseID
1108                    + "&" + InteractionConfig.HANDLER_HOST_ID 
1109                    + "=" + InteractionConfig.getInstance().getLocalServerId();
1110            if(debug.messageEnabled()) {
1111                debug.message("InteractionManager.newRedirectURLFault():"
1112                        + "lbRedirectURL is not null, rediectUrl:"
1113                        + redirectUrl);
1114            }
1115        }
1116        re.setRedirectURL(redirectUrl);
1117        List details = new ArrayList();
1118        try {
1119            details.add(Utils.convertJAXBToElement(re));
1120        } catch (JAXBException je) {
1121            debug.error("InteractionManager.newRedirectFault():"
1122                    + " can not create newRedirectFault:"
1123                    + " can not convert JAXBObject to Element", je);
1124        }
1125
1126        SOAPFault sf = new SOAPFault(
1127                QNAME_SERVER, 
1128                SERVER_ERROR,
1129                FAULT_ACTOR, new SOAPFaultDetail(details));
1130        Message sfmsg = new Message(sf);
1131        sfmsg.setCorrelationHeader(ch);
1132        SOAPFaultException sfe = new SOAPFaultException(sfmsg);
1133        return sfe;
1134    }
1135
1136    private SOAPFaultException newRedirectFaultError(QName errorCode) {
1137        StatusElement se = null;
1138        try{
1139            se = objectFactory.createStatusElement();
1140
1141        } catch (JAXBException je) {
1142            debug.error("InteractionManager.newRedirectFaultError():"
1143                    + " can not create StatusElement", je);
1144        }
1145
1146        se.setCode(errorCode);
1147        List details = new ArrayList();
1148        try {
1149            details.add(Utils.convertJAXBToElement(se));
1150        } catch (JAXBException je) {
1151            debug.error("InteractionManager.newRedirectFaultError():"
1152                    + "can not create new RedirectFaultError:" 
1153                    + "can not convert JAXBObject to Element", je);
1154        }
1155        SOAPFault sf = new SOAPFault(
1156                QNAME_SERVER, 
1157                SERVER_ERROR,
1158                FAULT_ACTOR, new SOAPFaultDetail(details));
1159        SOAPFaultException sfe = new SOAPFaultException(new Message(sf));
1160        return sfe;
1161    }
1162
1163    String getRedirectURL(SOAPFaultException sfe) throws SOAPFaultException {
1164        String redirectURL = null;
1165        List details = null;
1166        SOAPFaultDetail sfd =
1167                sfe.getSOAPFaultMessage().getSOAPFault().getDetail();
1168        if (sfd != null) {
1169            details = sfd.getOtherChildren();
1170        }
1171        try {
1172            details = Utils.convertElementToJAXB(details);
1173        } catch (JAXBException je) {
1174            debug.error("InteractionManager.getRedirectURL():"
1175                    + " can not get Redirect URL", je);
1176        }
1177
1178        if ( (details != null) && (details.size() > 0)
1179                    && (details.get(0) instanceof RedirectRequestElement)) {
1180            RedirectRequestElement rre 
1181                    = (RedirectRequestElement)details.get(0);
1182            if (rre != null) {
1183                redirectURL = rre.getRedirectURL();
1184            }
1185        }
1186        if (redirectURL == null) {
1187            throw sfe;
1188        }
1189        return redirectURL;
1190    }
1191
1192    private boolean checkRedirectHost(String redirectURL, String connectTo) {
1193        boolean answer = false;
1194        try {
1195            URL redirectToURL = new URL(redirectURL);
1196            URL connectToURL = new URL(connectTo);
1197            String redirectHost = redirectToURL.getHost();
1198            String connectToHost = connectToURL.getHost();
1199            if (redirectHost.equals(connectToHost)) {
1200                answer = true;
1201            }
1202        } catch (MalformedURLException mfe) {
1203            debug.error("InteractionManager.checkRedirectHost():"
1204                    + "redirectURL not a valid URL"
1205                    + " :redirectURL=" + redirectURL
1206                    + " :connectTo=" + connectTo, mfe);
1207        }
1208        return answer;
1209    }
1210
1211    String getResponseID(SOAPFaultException sfe) throws SOAPFaultException{
1212        String responseID = null;
1213        CorrelationHeader ch =
1214                sfe.getSOAPFaultMessage().getCorrelationHeader();
1215        if (ch == null) {
1216            debug.error("InteractionManager.getResponseID():"
1217                    + "null CorrelationHeader in SOAPFaultException");
1218            throw sfe;
1219        }
1220        responseID = ch.getMessageID();
1221        if (responseID == null) {
1222            debug.error("InteractionManager.getResponseID():"
1223                    + "null messageID in SOAPFaultException");
1224            throw sfe;
1225        }
1226        return responseID;
1227    }
1228
1229    static class InteractionCache {
1230
1231        private static final boolean VERBOSE_MESSAGE = false;
1232        private static final int SWEEP_INTERVAL = 60 * 1000;
1233        private static final int CACHE_ENTRY_MAX_IDLE_TIME = 120 * 1000;
1234
1235        PeriodicCleanUpMap cache = new PeriodicCleanUpMap(SWEEP_INTERVAL,
1236            CACHE_ENTRY_MAX_IDLE_TIME);
1237
1238        InteractionCache() {
1239            if (debug.messageEnabled()) {
1240                debug.message("InteactionCache.InteractionCache() "
1241                        + " - entering constructor");
1242            }
1243            if (debug.messageEnabled()) {
1244                debug.message("InteactionCache.InteractionCache() "
1245                        + " - starting sweeper thread");
1246                debug.message("InteractionCache.InteractionCache() "
1247                        + " SWEEP_INTERVAL = " + SWEEP_INTERVAL);
1248                debug.message("InteractionCache.InteractionCache() "
1249                        + " CACHE_ENTRY_MAX_IDLE_TIME = " 
1250                        + CACHE_ENTRY_MAX_IDLE_TIME);
1251            }
1252            SystemTimerPool.getTimerPool().schedule((TaskRunnable) cache,
1253                new Date(((currentTimeMillis() + SWEEP_INTERVAL) / 1000)
1254                * 1000));
1255            if (debug.messageEnabled()) {
1256                debug.message("InteactionCache.InteractionCache() "
1257                        + " - returning from constructor");
1258            }
1259        }
1260
1261        CacheEntry getCacheEntry(String messageID) {
1262            if (VERBOSE_MESSAGE && debug.messageEnabled()) {
1263                debug.message("InteractionCache.getCacheEntry():"
1264                        + "looking up cacheEntry  for messageID=" + messageID);
1265                debug.message("InteractionCache.getCacheEntry():"
1266                        + " cached messageIDs=" + cache.keySet());
1267            }
1268            CacheEntry entry = (CacheEntry)cache.get(messageID);
1269            if (entry != null) {
1270                cache.removeElement(messageID);
1271                cache.addElement(messageID);
1272            }
1273            return entry;
1274        }
1275
1276        void putCacheEntry(String messageID, CacheEntry cacheEntry) {
1277            cache.put(messageID, cacheEntry);
1278            if (VERBOSE_MESSAGE && debug.messageEnabled()) {
1279                debug.message("InteractionCache.putCacheEntry():"
1280                        + " cached cacheEntry for messageID=" + messageID);
1281            }
1282        }
1283
1284    }
1285
1286    static class CacheEntry {
1287        String messageID; //key
1288        String requestMessageID;
1289        Message requestMessage;
1290        InquiryElement inquiryElement;
1291        InteractionResponseElement interactionResponseElement;
1292        String connectTo;
1293        String certAlias;
1294        String soapAction;
1295        String returnToURL;
1296        String requestHost;
1297        String language;
1298
1299        CacheEntry(String messageID) {
1300            this.messageID= messageID;
1301        }
1302
1303        CacheEntry(String messageID, Message message) {
1304            this.messageID= messageID;
1305            this.requestMessage= message;
1306        }
1307
1308        void setRequestMessage(Message requestMessage) {
1309            this.requestMessage = requestMessage;
1310        }
1311
1312        Message getRequestMessage() {
1313            return requestMessage;
1314        }
1315
1316        void setRequestMessageID(String requestMessageID) {
1317            this.requestMessageID = requestMessageID;
1318        }
1319
1320        String getRequestMessageID() {
1321            return requestMessageID;
1322        }
1323
1324        void setInquiryElement(InquiryElement inquiryElement) {
1325            this.inquiryElement = inquiryElement;
1326        }
1327
1328        InquiryElement getInquiryElement() {
1329            return inquiryElement;
1330        }
1331
1332        void setInteractionResponseElement(
1333                InteractionResponseElement interactionResponseElement) {
1334            this.interactionResponseElement = interactionResponseElement;
1335        }
1336
1337        InteractionResponseElement getInteractionResponseElement() {
1338            return interactionResponseElement;
1339        }
1340
1341        void setConnectTo(String connectTo) {
1342            this.connectTo = connectTo;
1343        }
1344
1345        String getConnectTo() {
1346            return connectTo;
1347        }
1348
1349        void setClientCert(String certAlias) {
1350            this.certAlias = certAlias;
1351        }
1352
1353        String getClientCert() {
1354            return certAlias;
1355        }
1356
1357        void setSoapAction(String soapAction) {
1358            this.soapAction = soapAction;
1359        }
1360
1361        String getSoapAction() {
1362            return soapAction;
1363        }
1364
1365        void setReturnToURL(String returnToURL) {
1366            this.returnToURL = returnToURL;
1367        }
1368
1369        String getReturnToURL() {
1370            return returnToURL;
1371        }
1372
1373        void setRequestHost(String requestHost) {
1374            this.requestHost = requestHost;
1375        }
1376
1377        String getRequestHost() {
1378            return requestHost;
1379        }
1380
1381        void setLanguage(String language) {
1382            this.language = language;
1383        }
1384
1385        String getLanguage() {
1386            return language;
1387        }
1388
1389    }
1390
1391}
1392