001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.saaj;
020:
021: import org.apache.axiom.om.OMAttribute;
022: import org.apache.axiom.om.OMElement;
023: import org.apache.axiom.om.OMNode;
024: import org.apache.axiom.om.OMText;
025: import org.apache.axiom.om.impl.llom.OMTextImpl;
026: import org.apache.axis2.AxisFault;
027: import org.apache.axis2.Constants;
028: import org.apache.axis2.addressing.EndpointReference;
029: import org.apache.axis2.client.OperationClient;
030: import org.apache.axis2.client.Options;
031: import org.apache.axis2.client.ServiceClient;
032: import org.apache.axis2.context.MessageContext;
033: import org.apache.axis2.engine.AxisConfiguration;
034: import org.apache.axis2.engine.DispatchPhase;
035: import org.apache.axis2.saaj.util.IDGenerator;
036: import org.apache.axis2.saaj.util.SAAJUtil;
037: import org.apache.axis2.saaj.util.UnderstandAllHeadersHandler;
038: import org.apache.axis2.wsdl.WSDLConstants;
039:
040: import javax.activation.DataHandler;
041: import javax.xml.namespace.QName;
042: import javax.xml.soap.AttachmentPart;
043: import javax.xml.soap.MessageFactory;
044: import javax.xml.soap.MimeHeaders;
045: import javax.xml.soap.SOAPBody;
046: import javax.xml.soap.SOAPConnection;
047: import javax.xml.soap.SOAPElement;
048: import javax.xml.soap.SOAPException;
049: import javax.xml.soap.SOAPHeader;
050: import javax.xml.soap.SOAPHeaderElement;
051: import javax.xml.soap.SOAPMessage;
052: import javax.xml.soap.SOAPPart;
053: import java.io.IOException;
054: import java.io.InputStream;
055: import java.net.HttpURLConnection;
056: import java.net.MalformedURLException;
057: import java.net.URL;
058: import java.util.Collection;
059: import java.util.HashMap;
060: import java.util.Iterator;
061: import java.util.List;
062: import java.util.Map;
063: import java.util.Set;
064: import java.util.StringTokenizer;
065:
066: /**
067: *
068: */
069: public class SOAPConnectionImpl extends SOAPConnection {
070:
071: /** Attribute which keeps track of whether this connection has been closed */
072: private boolean closed = false;
073:
074: private ServiceClient serviceClient;
075: private HashMap unaccessedAttachments = new HashMap();
076:
077: /**
078: * Sends the given message to the specified endpoint and blocks until it has returned the
079: * response.
080: *
081: * @param request the <CODE>SOAPMessage</CODE> object to be sent
082: * @param endpoint an <code>Object</code> that identifies where the message should be sent. It
083: * is required to support Objects of type <code>java.lang.String</code>,
084: * <code>java.net.URL</code>, and when JAXM is present <code>javax.xml.messaging.URLEndpoint</code>
085: * @return the <CODE>SOAPMessage</CODE> object that is the response to the message that was
086: * sent
087: * @throws javax.xml.soap.SOAPException if there is a SOAP error, or this SOAPConnection is
088: * already closed
089: */
090: public SOAPMessage call(SOAPMessage request, Object endpoint)
091: throws SOAPException {
092: if (closed) {
093: throw new SOAPException("SOAPConnection closed");
094: }
095:
096: // initialize URL
097: URL url;
098: try {
099: url = (endpoint instanceof URL) ? (URL) endpoint : new URL(
100: endpoint.toString());
101: } catch (MalformedURLException e) {
102: throw new SOAPException(e.getMessage());
103: }
104:
105: // initialize and set Options
106: Options options = new Options();
107: options.setTo(new EndpointReference(url.toString()));
108:
109: // initialize the Sender
110: OperationClient opClient;
111: try {
112: serviceClient = new ServiceClient();
113: disableMustUnderstandProcessing(serviceClient
114: .getAxisConfiguration());
115: opClient = serviceClient
116: .createClient(ServiceClient.ANON_OUT_IN_OP);
117: } catch (AxisFault e) {
118: throw new SOAPException(e);
119: }
120:
121: options
122: .setProperty(
123: Constants.Configuration.CHARACTER_SET_ENCODING,
124: request
125: .getProperty(SOAPMessage.CHARACTER_SET_ENCODING));
126:
127: opClient.setOptions(options);
128:
129: if (request.countAttachments() != 0) { // SOAPMessage with attachments
130: opClient.getOptions().setProperty(
131: Constants.Configuration.ENABLE_MTOM,
132: Constants.VALUE_TRUE);
133: return handleSOAPMessage(request, opClient);
134: } else { // simple SOAPMessage
135: return handleSOAPMessage(request, opClient);
136: }
137: }
138:
139: /*
140: * Installs UnderstandAllHeadersHandler that marks all headers as processed
141: * because MU validation should not be done for SAAJ clients.
142: */
143: private void disableMustUnderstandProcessing(
144: AxisConfiguration config) {
145: DispatchPhase phase;
146: phase = getDispatchPhase(serviceClient.getAxisConfiguration()
147: .getInFlowPhases());
148: if (phase != null) {
149: phase.addHandler(new UnderstandAllHeadersHandler());
150: }
151: phase = getDispatchPhase(serviceClient.getAxisConfiguration()
152: .getInFaultFlowPhases());
153: if (phase != null) {
154: phase.addHandler(new UnderstandAllHeadersHandler());
155: }
156: }
157:
158: private static DispatchPhase getDispatchPhase(List phases) {
159: Iterator iter = phases.iterator();
160: while (iter.hasNext()) {
161: Object phase = iter.next();
162: if (phase instanceof DispatchPhase) {
163: return (DispatchPhase) phase;
164: }
165: }
166: return null;
167: }
168:
169: /**
170: * Closes this <CODE>SOAPConnection</CODE> object.
171: *
172: * @throws javax.xml.soap.SOAPException if there is a SOAP error, or this SOAPConnection is
173: * already closed
174: */
175: public void close() throws SOAPException {
176: if (serviceClient != null) {
177: try {
178: serviceClient.cleanup();
179: } catch (AxisFault axisFault) {
180: throw new SOAPException(axisFault.getMessage());
181: }
182: }
183: if (closed) {
184: throw new SOAPException("SOAPConnection Closed");
185: }
186: closed = true;
187: }
188:
189: private SOAPMessage handleSOAPMessage(SOAPMessage request,
190: OperationClient opClient) throws SOAPException {
191:
192: MessageContext requestMsgCtx = new MessageContext();
193: try {
194: requestMsgCtx.setEnvelope(toOMSOAPEnvelope(request));
195: opClient.addMessageContext(requestMsgCtx);
196: opClient.execute(true);
197:
198: MessageContext msgCtx = opClient
199: .getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
200: return getSOAPMessage(msgCtx.getEnvelope());
201: } catch (Exception e) {
202: throw new SOAPException(e.getMessage(), e);
203: }
204: }
205:
206: /**
207: * This method handles the conversion of an OM SOAP Envelope to a SAAJ SOAPMessage
208: *
209: * @param respOMSoapEnv
210: * @return the SAAJ SOAPMessage
211: * @throws SOAPException If an exception occurs during this conversion
212: */
213: private SOAPMessage getSOAPMessage(
214: org.apache.axiom.soap.SOAPEnvelope respOMSoapEnv)
215: throws SOAPException {
216:
217: // Create the basic SOAP Message
218: MessageFactory mf = MessageFactory.newInstance();
219: SOAPMessage response = mf.createMessage();
220: SOAPPart sPart = response.getSOAPPart();
221: javax.xml.soap.SOAPEnvelope env = sPart.getEnvelope();
222: SOAPBody body = env.getBody();
223: SOAPHeader header = env.getHeader();
224:
225: // Convert all header blocks
226: org.apache.axiom.soap.SOAPHeader header2 = respOMSoapEnv
227: .getHeader();
228: if (header2 != null) {
229: for (Iterator hbIter = header2.examineAllHeaderBlocks(); hbIter
230: .hasNext();) {
231: // Converting a single OM SOAP HeaderBlock to a SAAJ SOAP
232: // HeaderBlock
233: org.apache.axiom.soap.SOAPHeaderBlock hb = (org.apache.axiom.soap.SOAPHeaderBlock) hbIter
234: .next();
235: final QName hbQName = hb.getQName();
236: final SOAPHeaderElement headerEle = header
237: .addHeaderElement(env.createName(hbQName
238: .getLocalPart(), hbQName.getPrefix(),
239: hbQName.getNamespaceURI()));
240: for (Iterator attribIter = hb.getAllAttributes(); attribIter
241: .hasNext();) {
242: OMAttribute attr = (OMAttribute) attribIter.next();
243: final QName attrQName = attr.getQName();
244: headerEle.addAttribute(env.createName(attrQName
245: .getLocalPart(), attrQName.getPrefix(),
246: attrQName.getNamespaceURI()), attr
247: .getAttributeValue());
248: }
249: final String role = hb.getRole();
250: if (role != null) {
251: headerEle.setActor(role);
252: }
253: headerEle.setMustUnderstand(hb.getMustUnderstand());
254:
255: toSAAJElement(headerEle, hb, response);
256: }
257: }
258:
259: // Convert the body
260: toSAAJElement(body, respOMSoapEnv.getBody(), response);
261: // if there are unrefferenced attachments, add that to response
262: if (!unaccessedAttachments.isEmpty()) {
263: Collection attachments = unaccessedAttachments.values();
264: Iterator attachementsIterator = attachments.iterator();
265: while (attachementsIterator.hasNext()) {
266: AttachmentPart attachment = (AttachmentPart) attachementsIterator
267: .next();
268: response.addAttachmentPart(attachment);
269: }
270: }
271:
272: return response;
273: }
274:
275: /**
276: * Converts an OMNode into a SAAJ SOAPElement
277: *
278: * @param saajEle
279: * @param omNode
280: * @param saajSOAPMsg
281: * @throws SOAPException If conversion fails
282: */
283: private void toSAAJElement(SOAPElement saajEle, OMNode omNode,
284: javax.xml.soap.SOAPMessage saajSOAPMsg)
285: throws SOAPException {
286:
287: if (omNode instanceof OMText) {
288: return; // simply return since the text has already been added to saajEle
289: }
290:
291: if (omNode instanceof OMElement) {
292: OMElement omEle = (OMElement) omNode;
293: for (Iterator childIter = omEle.getChildren(); childIter
294: .hasNext();) {
295: OMNode omChildNode = (OMNode) childIter.next();
296: SOAPElement saajChildEle = null;
297:
298: if (omChildNode instanceof OMText) {
299: // check whether the omtext refers to an attachment
300:
301: final OMText omText = (OMText) omChildNode;
302: if (omText.isOptimized()) { // is this an attachment?
303: final DataHandler datahandler = (DataHandler) omText
304: .getDataHandler();
305: AttachmentPart attachment = saajSOAPMsg
306: .createAttachmentPart(datahandler);
307: final String id = IDGenerator.generateID();
308: attachment.setContentId(id);
309: attachment.setContentType(datahandler
310: .getContentType());
311: saajSOAPMsg.addAttachmentPart(attachment);
312:
313: saajEle.addAttribute(saajSOAPMsg.getSOAPPart()
314: .getEnvelope().createName("href"),
315: "cid:" + id);
316: } else {
317: saajChildEle = saajEle.addTextNode(omText
318: .getText());
319: }
320: } else if (omChildNode instanceof OMElement) {
321: OMElement omChildEle = (OMElement) omChildNode;
322: final QName omChildQName = omChildEle.getQName();
323: saajChildEle = saajEle.addChildElement(omChildQName
324: .getLocalPart(), omChildQName.getPrefix(),
325: omChildQName.getNamespaceURI());
326: for (Iterator attribIter = omChildEle
327: .getAllAttributes(); attribIter.hasNext();) {
328: OMAttribute attr = (OMAttribute) attribIter
329: .next();
330: final QName attrQName = attr.getQName();
331: saajChildEle.addAttribute(saajSOAPMsg
332: .getSOAPPart().getEnvelope()
333: .createName(attrQName.getLocalPart(),
334: attrQName.getPrefix(),
335: attrQName.getNamespaceURI()),
336: attr.getAttributeValue());
337: }
338: }
339:
340: // go down the tree adding child elements, till u reach a leaf(i.e. text element)
341: toSAAJElement(saajChildEle, omChildNode, saajSOAPMsg);
342: }
343: }
344: }
345:
346: /**
347: * Converts a SAAJ SOAPMessage to an OM SOAPEnvelope
348: *
349: * @param saajSOAPMsg
350: * @return
351: * @throws SOAPException
352: */
353: protected org.apache.axiom.soap.SOAPEnvelope toOMSOAPEnvelope(
354: SOAPMessage saajSOAPMsg) throws SOAPException {
355:
356: final org.apache.axiom.soap.SOAPEnvelope omSOAPEnv = SAAJUtil
357: .toOMSOAPEnvelope(saajSOAPMsg.getSOAPPart()
358: .getDocumentElement());
359:
360: Map attachmentMap = new HashMap();
361: final Iterator attachments = saajSOAPMsg.getAttachments();
362: while (attachments.hasNext()) {
363: final AttachmentPart attachment = (AttachmentPart) attachments
364: .next();
365: if (attachment.getContentId() == null
366: || attachment.getContentId().trim().length() == 0) {
367: attachment.setContentId(IDGenerator.generateID());
368: }
369: if (attachment.getDataHandler() == null) {
370: throw new SOAPException(
371: "Attachment with NULL DataHandler");
372: }
373: attachmentMap.put(attachment.getContentId(), attachment);
374: }
375:
376: //Get keys of attachments to a hashmap
377: //This hashmap will be updated when attachment is accessed atleast once.
378: //Doing this here instead of inside insertAttachmentNodes()is much simpler
379: //as insertAttachmentNodes() has recursive calls
380: Set keySet = attachmentMap.keySet();
381: Iterator keySetItr = keySet.iterator();
382: HashMap keyAccessStatus = new HashMap();
383: while (keySetItr.hasNext()) {
384: String key = (String) keySetItr.next();
385: keyAccessStatus.put(key, "not-accessed");
386: }
387:
388: insertAttachmentNodes(attachmentMap, omSOAPEnv, keyAccessStatus);
389: unaccessedAttachments = getUnReferencedAttachmentNodes(
390: attachmentMap, omSOAPEnv, keyAccessStatus);
391:
392: return omSOAPEnv;
393: }
394:
395: /**
396: * Inserts the attachments in the proper places
397: *
398: * @param attachments
399: * @param omEnvelope
400: * @throws SOAPException
401: */
402: private void insertAttachmentNodes(Map attachments,
403: OMElement omEnvelope, HashMap keyAccessStatus)
404: throws SOAPException {
405:
406: Iterator childIter = omEnvelope.getChildElements();
407: while (childIter.hasNext()) {
408: OMElement child = (OMElement) childIter.next();
409: final OMAttribute hrefAttr = child.getAttribute(new QName(
410: "href"));
411: String contentID = getContentID(hrefAttr);
412:
413: if (contentID != null) {//This is an omEnvelope referencing an attachment
414: child.build();
415: AttachmentPart ap = ((AttachmentPart) attachments
416: .get(contentID.trim()));
417: //update the key status as accessed
418: keyAccessStatus.put(contentID.trim(), "accessed");
419: OMText text = new OMTextImpl(ap.getDataHandler(), true,
420: omEnvelope.getOMFactory());
421: child.removeAttribute(hrefAttr);
422: child.addChild(text);
423: } else {
424: //possibly there can be references in the children of this omEnvelope
425: //so recurse through.
426: insertAttachmentNodes(attachments, child,
427: keyAccessStatus);
428: }
429: }
430: }
431:
432: private HashMap getUnReferencedAttachmentNodes(Map attachments,
433: OMElement omEnvelope, HashMap keyAccessStatus)
434: throws SOAPException {
435:
436: HashMap unaccessedAttachments = new HashMap();
437: //now check for unaccessed keys
438: Set keySet = keyAccessStatus.keySet();
439: Iterator keySetItr = keySet.iterator();
440: while (keySetItr.hasNext()) {
441: String key = (String) keySetItr.next();
442: String keyStatus = (String) keyAccessStatus.get(key);
443: if ("not-accessed".equals(keyStatus)) {
444: //The value for this key has not been accessed in the
445: //referencing attachment scenario.Hence it must be an
446: //unreferenced one.
447: AttachmentPart ap = ((AttachmentPart) attachments
448: .get(key));
449: unaccessedAttachments.put(key, ap);
450: keyAccessStatus.put(key, "accessed");
451: }
452: }
453: return unaccessedAttachments;
454: }
455:
456: /**
457: * This method checks the value of attribute and if it is a valid CID then returns the contentID
458: * (with cid: prefix stripped off) or else returns null. A null return value can be assumed that
459: * this attribute is not an attachment referencing attribute
460: *
461: * @return the ContentID
462: */
463: private String getContentID(OMAttribute attr) {
464: String contentId;
465: if (attr != null) {
466: contentId = attr.getAttributeValue();
467: } else {
468: return null;
469: }
470:
471: if (contentId.startsWith("cid:")) {
472: contentId = contentId.substring(4);
473: return contentId;
474: }
475: return null;
476: }
477:
478: /** overrided SOAPConnection's get() method */
479:
480: public SOAPMessage get(Object to) throws SOAPException {
481: URL url = null;
482: try {
483: url = (to instanceof URL) ? (URL) to : new URL(to
484: .toString());
485: } catch (MalformedURLException e) {
486: throw new SOAPException(e);
487: }
488:
489: int responseCode;
490: boolean isFailure = false;
491: HttpURLConnection httpCon = null;
492: try {
493: httpCon = (HttpURLConnection) url.openConnection();
494: httpCon.setDoOutput(true);
495: httpCon.setDoInput(true);
496: httpCon.setUseCaches(false);
497: httpCon.setRequestMethod("GET");
498: HttpURLConnection.setFollowRedirects(true);
499:
500: httpCon.connect();
501: responseCode = httpCon.getResponseCode();
502: // 500 is allowed for SOAP faults
503: if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
504: isFailure = true;
505: } else if ((responseCode / 100) != 2) {
506: throw new SOAPException("Error response: ("
507: + responseCode + httpCon.getResponseMessage());
508: }
509: } catch (IOException e) {
510: throw new SOAPException(e);
511: }
512:
513: //Construct the soapmessage from http response
514: SOAPMessage soapMessage = null;
515: if (responseCode == HttpURLConnection.HTTP_OK) {
516: try {
517: //read http headers & load mimeheaders
518: MimeHeaders mimeHeaders = new MimeHeaders();
519: String key, value;
520: // skip status line
521: int i = 1;
522: while (true) {
523: key = httpCon.getHeaderFieldKey(i);
524: value = httpCon.getHeaderField(i);
525: if (key == null && value == null) {
526: break;
527: }
528:
529: if (key != null) {
530: StringTokenizer values = new StringTokenizer(
531: value, ",");
532: while (values.hasMoreTokens()) {
533: mimeHeaders.addHeader(key, values
534: .nextToken().trim());
535: }
536: }
537: i++;
538: }
539: InputStream httpInputStream;
540: if (isFailure) {
541: httpInputStream = httpCon.getErrorStream();
542: } else {
543: httpInputStream = httpCon.getInputStream();
544: }
545:
546: soapMessage = new SOAPMessageImpl(httpInputStream,
547: mimeHeaders);
548: httpInputStream.close();
549: httpCon.disconnect();
550:
551: } catch (SOAPException e) {
552: throw e;
553: } catch (Exception e) {
554: throw new SOAPException(e.getMessage());
555: }
556: }
557: return soapMessage;
558: }
559:
560: }
|