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: DSTClient.java,v 1.5 2008/12/16 01:48:32 exu Exp $
026 *
027 */
028
029package com.sun.identity.liberty.ws.dst;
030
031import com.sun.identity.liberty.ws.security.SecurityAssertion;
032import com.sun.identity.liberty.ws.common.wsse.BinarySecurityToken;
033import com.sun.identity.liberty.ws.disco.EncryptedResourceID;
034import com.sun.identity.liberty.ws.disco.ResourceOffering;
035import com.sun.identity.liberty.ws.disco.ServiceInstance;
036import com.sun.identity.liberty.ws.disco.Description;
037import com.sun.identity.liberty.ws.disco.ResourceID;
038import com.sun.identity.liberty.ws.soapbinding.Message;
039import com.sun.identity.liberty.ws.soapbinding.Client;
040import com.sun.identity.liberty.ws.soapbinding.ProviderHeader;
041import com.sun.identity.liberty.ws.soapbinding.SOAPFaultException;
042import com.sun.identity.liberty.ws.soapbinding.SOAPBindingException;
043import java.util.List;
044import java.util.ArrayList;
045import java.util.Iterator;
046import org.w3c.dom.Element;
047import com.sun.identity.liberty.ws.security.SecurityTokenManagerClient;
048import com.sun.identity.liberty.ws.interaction.InteractionRedirectException;
049import com.sun.identity.liberty.ws.interaction.InteractionException;
050import com.sun.identity.liberty.ws.interaction.InteractionManager;
051import com.sun.identity.liberty.ws.soapbinding.ServiceInstanceUpdateHeader;
052import com.sun.identity.liberty.ws.soapbinding.SOAPBindingConstants;
053import com.sun.identity.liberty.ws.soapbinding.Utils;
054import com.sun.identity.shared.configuration.SystemPropertiesManager;
055import javax.servlet.http.HttpServletRequest;
056import javax.servlet.http.HttpServletResponse;
057
058/**
059 * The class <code>DSTClient</code> provides methods for Liberty
060 * Data Service Template.
061 *
062 * @supported.all.api
063 */
064public class DSTClient {
065
066     private String resourceID = null;
067     private EncryptedResourceID encryptedResourceID = null;
068     private String soapURI = null;
069     private String certAlias = null;
070     private SecurityAssertion assertion = null;
071     private BinarySecurityToken token = null;
072     private boolean isEncryptedResourceID = false;
073     // default to anonymous
074     private int securityProfile = Message.ANONYMOUS;
075     private String soapAction = null;
076     private String serviceType = null;
077     private HttpServletRequest httpRequest = null;
078     private HttpServletResponse httpResponse = null;
079     private boolean clientAuthEnabled = false;
080     private String providerID = null;
081     private ServiceInstanceUpdateHeader serviceInstanceUpdateHeader = null;
082     private String wsfVersion = Utils.getDefaultWSFVersion();
083
084
085    /**
086     * Constructor
087     * The constructor connects to Data Service without <code>WSS</code> token 
088     * @param soapURI URI of the SOAP end point for this Data service
089     *                       instance
090     * @param providerID ID of service provider. 
091     */
092    public DSTClient(String soapURI, String providerID) {
093        this.soapURI = soapURI;
094        this.providerID = providerID;
095        this.securityProfile = Message.ANONYMOUS;
096    }
097
098    /**
099     * Constructor
100     * The constructor connects to Data Service using <code>WSS</code> 
101     * SAML Token
102     * @param assertion <code>WSS</code> SAML Token
103     * @param soapURI URI of the SOAP end point for this data 
104     *                       service instance
105     * @param providerID ID of service provider. 
106     */
107    public DSTClient (SecurityAssertion assertion, 
108                      String soapURI,
109                      String providerID) {
110        this.assertion = assertion;
111        this.soapURI = soapURI;
112        this.providerID = providerID;
113        if(assertion != null && assertion.isBearer()) {
114           this.securityProfile = Message.BEARER_TOKEN;
115        } else {
116           this.securityProfile = Message.SAML_TOKEN;
117        }
118    }
119
120    /**
121     * Constructor
122     * The constructor connects to the data Service using <code>WSS X509</code>
123     * Token.
124     *
125     * @param token  <code>WSS X.509</code> Certificate Token
126     * @param soapURI URI of the SOAP end point for this Data 
127     *                       service instance
128     * @param providerID ID of service provider. 
129     */
130    public DSTClient(BinarySecurityToken token, 
131                     String soapURI,
132                     String providerID) {
133        this.token = token;
134        this.soapURI = soapURI;
135        this.securityProfile = Message.X509_TOKEN;
136        this.providerID = providerID;
137    }
138
139    /**
140     * Constructor
141     * The constructor connects to Data Service without <code>WSS</code> token, 
142     * the <code>HttpServletRequest</code> and <code>HttpServletResponse</code>
143     * object of the current request agent will be used for resource owner
144     * interactions if needed.
145     *
146     * @param soapURI URI of the SOAP end point for this Data 
147     *        service instance
148     * @param providerID ID of service provider. 
149     * @param httpRequest <code>HttpServletRequest</code> object of current
150     *        user agent request.
151     * @param httpResponse <code>HttpServletResponse</code> object of current
152     *        user agent request.
153     */ 
154    public DSTClient(String soapURI,
155                     String providerID,
156                     HttpServletRequest httpRequest, 
157                     HttpServletResponse httpResponse) {
158        this.soapURI = soapURI;
159        this.securityProfile = Message.ANONYMOUS;
160        this.httpRequest = httpRequest;
161        this.httpResponse = httpResponse;
162        this.providerID = providerID;
163    } 
164
165    /**
166     * Constructor
167     * The constructor connects to Data Service with <code>WSS</code>
168     * SAML token, the <code>HttpServletRequest</code> and
169     * <code>HttpServletResponse</code> object of the current request
170     * agent will be used for resource owner interactions if needed.
171     * @param assertion <code>WSS</code> SAML Token.
172     * @param soapURI URI of the SOAP end point for this Data service instance.
173     * @param providerID ID of service provider. 
174     * @param httpRequest <code>HttpServletRequest</code> object of current
175     *        user agent request.
176     * @param httpResponse <code>HttpServletResponse</code> object of current
177     *        user agent request.
178     */
179    public DSTClient(SecurityAssertion assertion, 
180                     String soapURI,
181                     String providerID,
182                     HttpServletRequest httpRequest,
183                     HttpServletResponse httpResponse) {
184
185        this.httpRequest = httpRequest;
186        this.httpResponse = httpResponse;
187        this.assertion = assertion;
188        this.soapURI = soapURI;
189        this.securityProfile = Message.SAML_TOKEN;
190        this.providerID = providerID;
191    }
192
193    /**
194     * Constructor
195     * The constructor connects to Data Service with <code>WSS</code> 
196     * SAML token, the <code>HttpServletRequest</code> and
197     * <code>HttpServletResponse</code> object of the current request
198     * agent will be used for resource owner interactions if needed.
199     * @param token <code>WSS X.509</code> Certificate Token
200     * @param soapURI URI of the SOAP end point for this Data 
201     *                service instance
202     * @param providerID ID of service provider. 
203     * @param httpRequest <code>HttpServletRequest</code> object of current
204     *        user agent request.
205     * @param httpResponse <code>HttpServletResponse</code> object of current
206     *        user agent.
207     */
208    public DSTClient(BinarySecurityToken token, 
209                     String soapURI,
210                     String providerID,
211                     HttpServletRequest httpRequest,
212                     HttpServletResponse httpResponse) {
213        this.token = token;
214        this.soapURI = soapURI;
215        this.securityProfile = Message.X509_TOKEN;
216        this.httpRequest = httpRequest;
217        this.httpResponse = httpResponse;
218        this.providerID = providerID;
219    }
220
221    /**
222     * Contructor
223     * Creates a data service template client instance.
224     * Connects to data Service specified by the resource offering.
225     * <code>resourceID</code>, security mechanism and SOAP endpoint defined
226     * in the <code>ResourceOffering</code> will be used.
227     *
228     * @param resourceOffering resource offering for this 
229     *        discovery service instance 
230     * @param providerID ID of this service provider. 
231     * @param credential Credential of <code>WSC</code>
232     * @param httpRequest <code>HttpServletRequest</code> object of current
233     *        user agent request.
234     * @param httpResponse <code>HttpServletResponse</code> object of current
235     *        user agent.
236     * @exception DSTException if the <code>resourceOffering</code> is not valid
237     */
238    public DSTClient(
239        ResourceOffering resourceOffering,
240        String providerID,
241        Object credential,
242        HttpServletRequest httpRequest,
243        HttpServletResponse httpResponse
244    ) throws DSTException {
245        if(resourceOffering == null) {
246            DSTUtils.debug.error("DSTClient: resource offering is null");
247            throw new DSTException(
248                DSTUtils.bundle.getString("nullInputParams"));
249        }
250        parseResourceOffering(resourceOffering);
251        if(securityProfile == Message.X509_TOKEN) {
252            generateBinarySecurityToken(credential);
253        }
254        this.httpRequest = httpRequest;
255        this.httpResponse = httpResponse;
256        this.providerID = providerID;
257    }
258
259    /**
260     * Constructor
261     * Creates a data service template client instance.
262     * Connects to data Service specified by the resource offering.
263     * <code>resourceID</code>, security mechanism and SOAP endpoint defined
264     * in the <code>ResourceOffering</code> will be used.
265     *
266     * @param resourceOffering resource offering for this 
267     *        discovery service instance 
268     * @param providerID ID of this service provider. 
269     * @param credential Credential of <code>WSC</code>
270     * @exception DSTException if the <code>resourceOffering</code> is not valid
271     */
272    public DSTClient(ResourceOffering resourceOffering,
273                     String providerID,
274                     Object credential)
275    throws DSTException { 
276        if(resourceOffering == null) {
277           DSTUtils.debug.error("DSTClient: resource offering is null");
278           throw new DSTException(DSTUtils.bundle.getString("nullInputParams"));
279        }
280        parseResourceOffering(resourceOffering);
281        if(securityProfile == Message.X509_TOKEN) {
282           generateBinarySecurityToken(credential);
283        }
284        this.providerID = providerID;
285    }
286
287    /**
288     * Parses the given discovery resource offering for the Data service.
289     * @param offering ResourceOffering 
290     * @exception DSTException.
291     */
292    private void parseResourceOffering(ResourceOffering offering)
293    throws DSTException {
294        //Try for the encrypted resource offering first;
295        encryptedResourceID = offering.getEncryptedResourceID(); 
296        if(encryptedResourceID != null) {
297           isEncryptedResourceID = true;
298        } else {
299           ResourceID resID = offering.getResourceID();
300           if(resID == null) {
301              DSTUtils.debug.error("DSTClient:parseResourceOffering: " +
302              "No ResourceID");
303              throw new DSTException(DSTUtils.bundle.getString("noResourceID"));
304           }
305           resourceID = resID.getResourceID();
306        }
307
308        ServiceInstance serviceInstance = offering.getServiceInstance();
309        // providerID = serviceInstance.getProviderID();
310        if(serviceInstance == null) {
311           DSTUtils.debug.error("DSTClient:parseResourceOffering: " +
312           "No service instance.");
313           throw new DSTException(
314           DSTUtils.bundle.getString("noServiceInstance"));
315        }
316        serviceType = serviceInstance.getServiceType();
317        if(serviceType == null) {
318           DSTUtils.debug.error("DSTClient:parseResourceOffering: " +
319           "service type is null.");
320           throw new DSTException(DSTUtils.bundle.getString("noServiceType"));
321        }
322      
323        List descriptions = serviceInstance.getDescription();
324        if(descriptions == null || descriptions.isEmpty()) {
325           DSTUtils.debug.error("DSTClient:parseResourceOffering: " +
326           "descriptions are null.");
327           throw new DSTException(DSTUtils.bundle.getString("noDescriptions")); 
328        }
329        // A service instance can have mutiple descriptions. In this case,
330        // we will try to use a valid description. 
331        Iterator iter = descriptions.iterator(); 
332        while(iter.hasNext()) {
333           Description description = (Description)iter.next();
334           soapAction = description.getSoapAction();
335           soapURI = description.getEndpoint();
336           if(soapURI == null || soapURI.length() == 0) {
337              continue;
338           }
339
340           List secMechIDs = description.getSecurityMechID();
341           if(secMechIDs == null || secMechIDs.isEmpty()) {
342              continue;
343           }
344           boolean foundProfile = false;
345           int size = secMechIDs.size();
346
347           for(int i=0; i < size; i++) {
348               String secProfile = (String)secMechIDs.get(i);
349               secProfile = secProfile.trim();
350
351               if(secProfile.equals(Message.NULL_NULL) ||
352                  secProfile.equals(Message.TLS_NULL) ||
353                  secProfile.equals(Message.CLIENT_TLS_NULL)) {
354
355                  securityProfile = Message.ANONYMOUS;
356                  if(secProfile.equals(Message.CLIENT_TLS_NULL)) {
357                     clientAuthEnabled = true;
358                  }
359                  foundProfile = true;
360                  break;
361
362               } else if(secProfile.equals(Message.NULL_X509) ||
363                  secProfile.equals(Message.TLS_X509) ||
364                  secProfile.equals(Message.CLIENT_TLS_X509) ||
365                  secProfile.equals(Message.NULL_X509_WSF11) ||
366                  secProfile.equals(Message.TLS_X509_WSF11) ||
367                  secProfile.equals(Message.CLIENT_TLS_X509_WSF11)) {
368
369                  securityProfile = Message.X509_TOKEN;
370                  if (secProfile.equals(Message.NULL_X509) ||
371                      secProfile.equals(Message.TLS_X509) ||
372                      secProfile.equals(Message.CLIENT_TLS_X509)) {
373                      wsfVersion = SOAPBindingConstants.WSF_10_VERSION;
374                  } else {
375                      wsfVersion = SOAPBindingConstants.WSF_11_VERSION;
376                  }
377
378                  securityProfile = Message.X509_TOKEN;
379                  if (secProfile.equals(Message.CLIENT_TLS_X509) ||
380                      secProfile.equals(Message.CLIENT_TLS_X509_WSF11)) {
381                      clientAuthEnabled = true;
382                  }
383                  foundProfile = true;
384                  break;
385
386               } else if(secProfile.equals(Message.NULL_SAML) ||
387                  secProfile.equals(Message.TLS_SAML) ||
388                  secProfile.equals(Message.CLIENT_TLS_SAML) ||
389                  secProfile.equals(Message.NULL_SAML_WSF11) ||
390                  secProfile.equals(Message.TLS_SAML_WSF11) ||
391                  secProfile.equals(Message.CLIENT_TLS_SAML_WSF11)) {
392
393                  securityProfile = Message.SAML_TOKEN;
394                  if (secProfile.equals(Message.NULL_SAML) ||
395                      secProfile.equals(Message.TLS_SAML) ||
396                      secProfile.equals(Message.CLIENT_TLS_SAML)) {
397                      wsfVersion = SOAPBindingConstants.WSF_10_VERSION;
398                  } else {
399                      wsfVersion = SOAPBindingConstants.WSF_11_VERSION;
400                  }
401                  if (secProfile.equals(Message.CLIENT_TLS_SAML) ||
402                      secProfile.equals(Message.CLIENT_TLS_SAML_WSF11)) {
403                      clientAuthEnabled = true;
404                  }
405                  foundProfile = true;
406                  break;
407               } else if(secProfile.equals(Message.NULL_BEARER) ||
408                  secProfile.equals(Message.TLS_BEARER) ||
409                  secProfile.equals(Message.CLIENT_TLS_BEARER) ||
410                  secProfile.equals(Message.NULL_BEARER_WSF11) ||
411                  secProfile.equals(Message.TLS_BEARER_WSF11) ||
412                  secProfile.equals(Message.CLIENT_TLS_BEARER_WSF11)) {
413
414                  securityProfile = Message.BEARER_TOKEN;
415                  if (secProfile.equals(Message.NULL_BEARER) ||
416                      secProfile.equals(Message.TLS_BEARER) ||
417                      secProfile.equals(Message.CLIENT_TLS_BEARER)) {
418                      wsfVersion = SOAPBindingConstants.WSF_10_VERSION;
419                  } else {
420                      wsfVersion = SOAPBindingConstants.WSF_11_VERSION;
421                  }
422
423                  if (secProfile.equals(Message.CLIENT_TLS_BEARER) ||
424                      secProfile.equals(Message.CLIENT_TLS_BEARER_WSF11)) {
425                      clientAuthEnabled = true;
426                  }
427                  foundProfile = true;
428                  break;
429               }
430                 
431           }
432           if(foundProfile) {
433              break;
434           }
435        }
436
437        if(soapURI == null) {
438           DSTUtils.debug.error("DSTClient:parseResourceOffering: " +
439           "SOAP Endpoint or security profile is null");
440           throw new DSTException(
441           DSTUtils.bundle.getString("invalidResourceOffering"));
442        }
443        if(DSTUtils.debug.messageEnabled()) {
444           DSTUtils.debug.message("DSTClient.parseResourceOffering:" +
445           "soapURI = " + soapURI + "soapAction = " + soapAction + 
446           "securityProfile = " + securityProfile);
447        }
448         
449    }
450
451    /**
452     * Generates X509 security token for the WSC.
453     * @param credential Credential of WSC
454     * @exception DSTException
455     */
456    private void generateBinarySecurityToken(Object credential)
457     throws DSTException {
458        try {
459            SecurityTokenManagerClient manager =
460                new SecurityTokenManagerClient(credential);
461            if (certAlias == null) {
462                certAlias = SystemPropertiesManager.get(
463                    "com.sun.identity.liberty.ws.wsc.certalias");
464            }
465            manager.setCertAlias(certAlias);
466            token =  manager.getX509CertificateToken();
467            token.setWSFVersion(wsfVersion);
468        } catch (Exception e) {
469            DSTUtils.debug.error("DSTClient:generateBinarySecurityToken:" +
470            "Error in generating binary security token.", e);
471            throw new DSTException(e);
472        }
473    }
474 
475
476    /**
477     * Sets the resource ID to be accessed
478     * @param resourceID resource ID String
479     */
480    public void setResourceID(String resourceID) {
481        this.resourceID = resourceID;
482        isEncryptedResourceID = false;
483    }
484
485    /**
486     * Sets the encrypted resource ID to be accessed
487     * @param encResourceID encrypted resource ID 
488     */
489    public void setResourceID(EncryptedResourceID encResourceID) {
490        this.encryptedResourceID = encResourceID;
491        isEncryptedResourceID = true;
492    }
493
494    /**
495     * Sets the provider ID.
496     * @param providerID Provider ID.
497     */
498    public void setProviderID(String providerID) {
499        this.providerID = providerID;
500    }
501
502    /**
503     * Sets the alias for the client certificate if the connection is TLS/SSL
504     * with client authentication.
505     *
506     * @param certAlias certificate alias name
507     */ 
508    public void setClientCert(String certAlias) {
509        this.certAlias = certAlias;
510    }
511
512    /**
513     * Sets SOAP Action such as query or modify
514     * @param action action for this soap request
515     */
516    public void setSOAPAction(String action) {
517        this.soapAction = action;
518    }
519
520    /**
521     * Sets the client authentication.
522     *
523     * @param value true value to enable client authentication.
524     */
525    public void setClientAuth(boolean value) {
526        this.clientAuthEnabled = value;
527    }
528
529    /**
530     * Sets the SOAP Endpoint.
531     * @param endpoint SOAP Endpoint. 
532     */
533    public void setSOAPEndPoint(String endpoint) {
534        this.soapURI = endpoint;
535    }
536
537    /**
538     * Sets the Security Assertion.
539     * @param secAssertion Security Assertion.
540     */
541    public void setSecurityAssertion(SecurityAssertion secAssertion) {
542        this.assertion = secAssertion;
543    }
544
545    /**
546     * Sets the binary security token.
547     * @param binaryToken Binary Security Token.
548     */
549    public void setBinarySecurityToken(BinarySecurityToken binaryToken) {
550        this.token = binaryToken;
551    }
552
553    /**
554     * Sets the security mechanism.
555     * @param secMech security mechanism.
556     */
557    public void setSecurityMech(String secMech) {
558        if(secMech == null || secMech.endsWith("null")) {
559           securityProfile = Message.ANONYMOUS;
560        } else if(secMech.endsWith("X509")) {
561           securityProfile = Message.X509_TOKEN;
562        } else if(secMech.endsWith("SAML")) {
563           securityProfile = Message.SAML_TOKEN;
564        } else if(secMech.endsWith("Bearer")) {
565           securityProfile = Message.BEARER_TOKEN;
566        }
567    }
568 
569
570    /**
571     * Gets data for the specified query items.
572     * @param items list of <code>DSTQueryItem</code> object 
573     * @return List of <code>DSTData</code> objects which have one-to-one
574     *         correspondence to the list of query items. If no response for
575     *         one of the query item, the corresponding return
576     *         <code>DSTData</code> object will be null.
577     * @exception DSTException if error occurs when trying to get data
578     * @exception InteractionRedirectException if user agent is redirected to
579     *            Web Service Provider (<code>WSP</code>) for resource owner
580     *            interactions 
581     */
582    public java.util.List getData(java.util.List items) 
583      throws DSTException, InteractionRedirectException
584    {
585        DSTUtils.debug.message("DSTClient:getData:Init");
586
587        if(items == null || items.size() == 0) {
588           DSTUtils.debug.error("DSTUtils.getData:Query items are null.");
589           throw new DSTException(DSTUtils.bundle.getString("nullInputParams"));
590        }
591
592        DSTQuery dstQuery = null;
593        
594        if(isEncryptedResourceID) {
595           dstQuery = new DSTQuery(encryptedResourceID, items, null);
596        } else {
597           dstQuery = new DSTQuery(resourceID, items, null);
598        }
599 
600        List query = new ArrayList();
601        query.add(DSTUtils.parseXML(dstQuery.toString(true, true)));
602       
603        List  response = sendMessage(query);
604
605        if(response == null || response.size() == 0) {
606           DSTUtils.debug.message("DSTClient:getData: response is null");
607           return null;
608        }
609
610        DSTQueryResponse queryResponse = 
611            new DSTQueryResponse((Element)response.get(0));
612        return queryResponse.getData();
613    }
614
615    /**
616     * Gets <code>QueryResponse</code> for the specified query.
617     * @param query <code>DSTQuery</code> object. 
618     * @return <code>DSTDQueryResponse</code> Object.
619     * @exception DSTException if error occurs when trying to get data
620     * @exception InteractionRedirectException if user agent is redirected to
621     *            Web Service Provider (<code>WSP</code>) for resource owner
622     *            interactions 
623     */
624    public DSTQueryResponse query(DSTQuery query) 
625    throws DSTException, InteractionRedirectException {
626
627        DSTUtils.debug.message("DSTClient:query:Init");
628        if(query == null) {
629           DSTUtils.debug.message("DSTClient:query:null value");
630           throw new DSTException(DSTUtils.bundle.getString("nullInputParams"));
631        }
632
633        List request = new ArrayList();
634        request.add(DSTUtils.parseXML(query.toString(true, true)));
635        List response = sendMessage(request);
636        Element queryResponse = (Element)response.get(0);
637        return new DSTQueryResponse(queryResponse); 
638    }
639
640    /**
641     * Gets response for a list of <code>DST</code> Modifications.
642     * @param items List of <code>DSTModification</code> objects.
643     * @return <code>DSTModifyResponse</code>.
644     * @exception DSTException if error occurs when trying to modify data
645     * @exception InteractionRedirectException if user agent is redirected to
646     *            Web Service Provider (<code>WSP</code>) for resource owner
647     *            interactions.
648     */
649    public DSTModifyResponse modify(java.util.List items) 
650    throws DSTException, InteractionRedirectException {
651
652        DSTUtils.debug.message("DSTClient:modify:init:");
653        if(items == null) {
654           DSTUtils.debug.message("DSTClient:modify:null values");
655           throw new DSTException(DSTUtils.bundle.getString("nullInputParams"));
656        }
657
658        DSTModify modify = null;
659
660        if(isEncryptedResourceID) {
661           modify = new DSTModify(encryptedResourceID, items, null);
662        } else {
663           modify = new DSTModify(resourceID, items, null);
664        }
665
666        List request = new ArrayList();
667        request.add(DSTUtils.parseXML(modify.toString(true, true)));
668
669        List response = sendMessage(request);
670        if(response == null || response.size() == 0) {
671           DSTUtils.debug.message("DSTClient:modify: response is null");
672           return null;
673        }
674
675        return new DSTModifyResponse((Element)response.get(0));
676
677    }
678
679    /**
680     * Gets modify response for the specified modify.
681     * @param modify <code>DSTModify</code> object.
682     * @return <code>DSTModifyResponse</code> object.
683     * @exception DSTException if error occurs when trying to modify data
684     * @exception InteractionRedirectException if user agent is redirected to
685     *            Web Service Provider (<code>WSP</code>) for resource owner
686     *            interactions 
687     */ 
688    public DSTModifyResponse modify(DSTModify modify) 
689     throws DSTException, InteractionRedirectException {
690
691        DSTUtils.debug.message("DSTClient:modify:init");
692        if(modify == null) {
693           DSTUtils.debug.message("DSTClient:modify:null values");
694           throw new DSTException(DSTUtils.bundle.getString("nullInputParams"));
695        }
696
697        List request = new ArrayList();
698        request.add(DSTUtils.parseXML(modify.toString(true, true)));
699
700        List response = sendMessage(request);
701        return new DSTModifyResponse((Element)response.get(0));
702    } 
703
704    /**
705     * Gets query responses for a list of <code>DST</code> queries
706     * @param queries list of <code>DSTQuery</code> objects
707     * @return List of <code>DSTQueryResponse</code> objects, corresponding
708     *         to each <code>DSTQuery</code> object.
709     * @exception DSTException if error occurs when trying to get data
710     * @exception InteractionRedirectException if interaction is required.
711     */
712    public java.util.List getQueryResponse(java.util.List queries) 
713     throws DSTException,  InteractionRedirectException
714    {
715        DSTUtils.debug.message("DSTClient.getQueryResponse:Init");
716        if(queries == null || queries.size() == 0) {
717           DSTUtils.debug.error("DSTClient.getQueryResponse:null values");
718           throw new DSTException(DSTUtils.bundle.getString("nullInputParams"));
719        }
720        Iterator iter = queries.iterator();
721        List requests = new ArrayList();
722        while(iter.hasNext()) {
723           DSTQuery query = (DSTQuery)iter.next();
724           requests.add(DSTUtils.parseXML(query.toString(true, true)));
725        }
726        List responses = sendMessage(requests); 
727        if(responses == null || responses.size() == 0) {
728           DSTUtils.debug.error("DSTClient.getQueryResponse:null responses");
729           throw new DSTException(DSTUtils.bundle.getString("nullResponse"));
730        }
731        List queryResponses = new ArrayList();
732        Iterator iter1 = responses.iterator();
733        while(iter1.hasNext()) {
734            queryResponses.add(new DSTQueryResponse((Element)iter1.next()));   
735        }
736        return queryResponses;
737    }
738
739    /**
740     * Performs a list of modifications specified by a list of
741     * <code>DSTModify</code> objects (possible on different resource ID).
742     *
743     * @param modifies list of <code>DSTModify</code> objects specifying
744     *        resource and modifications to be made.
745     * @return List of <code>DSTModifyResponse</code> object corresponding
746     *         to each <code>DSTModify</code>.
747     * @exception DSTException if error occurs when trying to modify data
748     * @exception InteractionRedirectException if interaction is required.
749     */
750    public java.util.List getModifyResponse(java.util.List modifies) 
751     throws DSTException , InteractionRedirectException
752    {
753        DSTUtils.debug.message("DSTClient.getModifyResponse:Init");
754        if(modifies == null || modifies.size() == 0) {
755           DSTUtils.debug.error("DSTClient.getModifyResponse:null values");
756           throw new DSTException(DSTUtils.bundle.getString("nullInputParams"));
757        }
758        Iterator iter = modifies.iterator();
759        List requests = new ArrayList();
760        while(iter.hasNext()) {
761           DSTModify modify = (DSTModify)iter.next();
762           requests.add(DSTUtils.parseXML(modify.toString(true, true)));
763        }
764        List responses = sendMessage(requests);
765        if(responses == null || responses.size() == 0) {
766           DSTUtils.debug.error("DSTClient.getModifyResponse:null responses");
767           throw new DSTException(DSTUtils.bundle.getString("nullResponse"));
768        }
769        List modifyResponses = new ArrayList();
770        Iterator iter1 = responses.iterator();
771        while(iter1.hasNext()) {
772            modifyResponses.add(new DSTModifyResponse((Element)iter1.next()));
773        }
774        return modifyResponses;
775
776    }
777
778    /**
779     * Sends the SOAP Message to the data service.
780     * @param List of Request Objects.
781     * @return List of Response Objects.
782     * @exception DSTException for failure.
783     */
784    private List sendMessage(List requestObjects) 
785    throws DSTException, InteractionRedirectException {
786
787        DSTUtils.debug.message("DSTClient:sendMessage:Init");
788        if(requestObjects == null || requestObjects.size() == 0) {
789           DSTUtils.debug.message("DSTClient:sendMessage: requestobj are null");
790           throw new DSTException(DSTUtils.bundle.getString("nullInputParams"));
791        }
792
793        try {
794            Message msg = null;
795
796            ProviderHeader provH = null;
797            if(providerID != null) {
798               provH = new ProviderHeader(providerID);
799            }
800
801            if(securityProfile == Message.X509_TOKEN) {
802               if(token == null) {
803                  throw new DSTException(
804                  DSTUtils.bundle.getString("nullToken"));
805               }
806               DSTUtils.debug.message("DSTClient:sendMessage:using x509");
807               msg = new Message(provH, token);
808           
809            } else if(securityProfile == Message.SAML_TOKEN) {
810               DSTUtils.debug.message("DSTClient:sendMessage:using SAML");
811               msg = new Message(provH, assertion);
812
813            } else if(securityProfile == Message.BEARER_TOKEN) {
814               DSTUtils.debug.message("DSTClient:sendMessage:using Bearer");
815               msg = new Message(provH, assertion);
816
817            } else if(securityProfile == Message.ANONYMOUS) {
818               DSTUtils.debug.message("DSTClient:sendMessage:using Anonymous");
819               msg = new Message(provH);
820
821            } else {
822               throw new DSTException(
823               DSTUtils.bundle.getString("invalidSecurityProfile"));
824            }
825                
826            msg.setSOAPBodies(requestObjects);
827            msg.setWSFVersion(wsfVersion);
828
829            if(clientAuthEnabled) {
830               msg.setClientAuthentication(clientAuthEnabled);
831            }
832
833            if(DSTUtils.debug.messageEnabled()) {
834               DSTUtils.debug.message("DSTClient:sendMessage: request:" 
835                 + msg.toString());
836            }
837
838            Message response = null;
839
840            if(httpRequest != null) {
841               response = handleInteraction(msg);
842            } else {
843               response = Client.sendRequest(
844               msg, soapURI, certAlias, soapAction);
845            }
846
847            if(DSTUtils.debug.messageEnabled()) {
848               DSTUtils.debug.message("DSTClient:sendMessage:response = " +
849               response.toString());
850            }
851
852            serviceInstanceUpdateHeader = 
853                   response.getServiceInstanceUpdateHeader();
854
855            return response.getBodies();
856
857        } catch(SOAPBindingException sbe) {
858            DSTUtils.debug.error("DSTClient:sendMessage:soapbindexception",sbe);
859            throw new DSTException(sbe);
860        } catch(SOAPFaultException sfe) {
861            DSTUtils.debug.error("DSTClient:sendMessage:soapfault", sfe);
862            serviceInstanceUpdateHeader =
863                    sfe.getSOAPFaultMessage().getServiceInstanceUpdateHeader();
864            throw new DSTException(sfe);
865        }
866    }
867
868    /**
869     * Handles interaction request processing.
870     * When the interaction is required, it throws and InteractRedirect
871     * Exception, and redirect to the caller application(servlet). 
872     */
873    private Message handleInteraction(Message requestMsg) 
874     throws DSTException, SOAPFaultException, 
875           SOAPBindingException, InteractionRedirectException {
876
877        if(requestMsg == null || httpRequest == null ||
878           httpResponse == null || soapURI == null) {
879           DSTUtils.debug.error("DSTClient:handeInteraction:null values");
880           throw new DSTException(
881           DSTUtils.bundle.getString("nullInputParams"));
882        }
883        DSTUtils.debug.message("DSTClient:handleInteraction:init");
884        String resend = httpRequest.getParameter(
885                        InteractionManager.RESEND_MESSAGE);
886        String returnURL = httpRequest.getRequestURL().toString();
887        Message response;
888        try {
889            InteractionManager manager = InteractionManager.getInstance();
890            if(resend == null) {
891               //If the interaction is not required, this will send a
892               // original response.
893               response = manager.sendRequest(requestMsg, soapURI, certAlias,
894                          soapAction, returnURL, httpRequest, httpResponse);
895            } else {
896               response = manager.resendRequest(returnURL, 
897                                 httpRequest, httpResponse);
898            }
899            return response;
900        } catch (InteractionRedirectException ire) {
901            DSTUtils.debug.message("DSTClient:handleInteraction: Interaction" +
902            "Redirection happened.");
903            throw ire; 
904        }  catch (InteractionException ie) {
905            DSTUtils.debug.error("DSTClient:handleInteraction: Interaction" +
906            " Error occured.", ie);
907            throw new DSTException(ie);
908        }
909    }
910
911    /**
912     * Gets the <code>serviceInstanceUpdate</code> header from the SOAP
913     * response message.
914     * Applications can make use of this method to check if there is an
915     * alternate endpoint or credential available for the service requests.
916     *
917     * @return <code>ServiceInstanceUpdateHeader</code> from the response
918     *         message.
919     */ 
920    public ServiceInstanceUpdateHeader getServiceInstanceUpdateHeader() {
921        return serviceInstanceUpdateHeader;
922    }
923
924    /**
925     * Sets the web services version.
926     *
927     * @param wsfVersion the web services version that needs to be set.
928     */
929    public void setWSFVersion(String wsfVersion) {
930        this.wsfVersion = wsfVersion;
931    }
932}