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: */package org.apache.cxf.binding.soap.interceptor;
019:
020: import java.io.OutputStream;
021: import java.util.List;
022: import java.util.ResourceBundle;
023:
024: import javax.xml.stream.XMLStreamException;
025: import javax.xml.stream.XMLStreamWriter;
026:
027: import org.w3c.dom.Element;
028:
029: import org.apache.cxf.Bus;
030: import org.apache.cxf.binding.soap.SoapFault;
031: import org.apache.cxf.binding.soap.SoapMessage;
032: import org.apache.cxf.binding.soap.SoapVersion;
033: import org.apache.cxf.binding.soap.model.SoapHeaderInfo;
034: import org.apache.cxf.common.i18n.BundleUtils;
035: import org.apache.cxf.databinding.DataBinding;
036: import org.apache.cxf.databinding.DataWriter;
037: import org.apache.cxf.headers.Header;
038: import org.apache.cxf.headers.HeaderManager;
039: import org.apache.cxf.headers.HeaderProcessor;
040: import org.apache.cxf.interceptor.Fault;
041: import org.apache.cxf.io.WriteOnCloseOutputStream;
042: import org.apache.cxf.message.Exchange;
043: import org.apache.cxf.message.Message;
044: import org.apache.cxf.message.MessageContentsList;
045: import org.apache.cxf.message.MessageUtils;
046: import org.apache.cxf.phase.Phase;
047: import org.apache.cxf.service.Service;
048: import org.apache.cxf.service.model.BindingMessageInfo;
049: import org.apache.cxf.service.model.BindingOperationInfo;
050: import org.apache.cxf.service.model.MessagePartInfo;
051: import org.apache.cxf.service.model.ServiceModelUtil;
052: import org.apache.cxf.staxutils.StaxUtils;
053:
054: public class SoapOutInterceptor extends AbstractSoapInterceptor {
055: public static final String WROTE_ENVELOPE_START = "wrote.envelope.start";
056:
057: private static final ResourceBundle BUNDLE = BundleUtils
058: .getBundle(SoapOutInterceptor.class);
059:
060: private Bus bus;
061:
062: public SoapOutInterceptor(Bus b) {
063: super (Phase.WRITE);
064: bus = b;
065: }
066:
067: public SoapOutInterceptor(Bus b, String phase) {
068: super (phase);
069: bus = b;
070: }
071:
072: public void handleMessage(SoapMessage message) {
073: // Yes this is ugly, but it avoids us from having to implement any kind of caching strategy
074: if (!MessageUtils.isTrue(message.get(WROTE_ENVELOPE_START))) {
075: writeSoapEnvelopeStart(message);
076:
077: OutputStream os = message.getContent(OutputStream.class);
078: // Unless we're caching the whole message in memory skip the envelope writing
079: // if there's a fault later.
080: if (!(os instanceof WriteOnCloseOutputStream)
081: && !MessageUtils.isDOMPresent(message)) {
082: message.put(WROTE_ENVELOPE_START, Boolean.TRUE);
083: }
084: }
085:
086: // Add a final interceptor to write end elements
087: message.getInterceptorChain().add(
088: new SoapOutEndingInterceptor());
089: }
090:
091: private void writeSoapEnvelopeStart(SoapMessage message) {
092: SoapVersion soapVersion = message.getVersion();
093: try {
094: XMLStreamWriter xtw = message
095: .getContent(XMLStreamWriter.class);
096: xtw.setPrefix(soapVersion.getPrefix(), soapVersion
097: .getNamespace());
098: xtw.writeStartElement(soapVersion.getPrefix(), soapVersion
099: .getEnvelope().getLocalPart(), soapVersion
100: .getNamespace());
101: xtw.writeNamespace(soapVersion.getPrefix(), soapVersion
102: .getNamespace());
103:
104: boolean preexistingHeaders = message.hasHeaders();
105: if (preexistingHeaders) {
106: xtw.writeStartElement(soapVersion.getPrefix(),
107: soapVersion.getHeader().getLocalPart(),
108: soapVersion.getNamespace());
109: List<Header> hdrList = message.getHeaders();
110: for (Header header : hdrList) {
111: DataBinding b = header.getDataBinding();
112: if (b == null) {
113: HeaderProcessor hp = bus.getExtension(
114: HeaderManager.class)
115: .getHeaderProcessor(
116: header.getName()
117: .getNamespaceURI());
118: if (hp != null) {
119: b = hp.getDataBinding();
120: }
121: }
122: if (b != null) {
123: b.createWriter(XMLStreamWriter.class).write(
124: header.getObject(), xtw);
125: } else {
126: Element node = (Element) header.getObject();
127: StaxUtils.copy(node, xtw);
128: }
129: }
130: }
131: boolean endedHeader = handleHeaderPart(preexistingHeaders,
132: message);
133: if (preexistingHeaders && !endedHeader) {
134: xtw.writeEndElement();
135: }
136:
137: xtw.writeStartElement(soapVersion.getPrefix(), soapVersion
138: .getBody().getLocalPart(), soapVersion
139: .getNamespace());
140:
141: // Interceptors followed such as Wrapped/RPC/Doc Interceptor will write SOAP body
142: } catch (XMLStreamException e) {
143: throw new SoapFault(new org.apache.cxf.common.i18n.Message(
144: "XML_WRITE_EXC", BUNDLE), e, soapVersion
145: .getSender());
146: }
147: }
148:
149: private boolean handleHeaderPart(boolean preexistingHeaders,
150: SoapMessage message) {
151: //add MessagePart to soapHeader if necessary
152: boolean endedHeader = false;
153: Exchange exchange = message.getExchange();
154: BindingOperationInfo bop = (BindingOperationInfo) exchange
155: .get(BindingOperationInfo.class.getName());
156: if (bop == null) {
157: return endedHeader;
158: }
159:
160: XMLStreamWriter xtw = message.getContent(XMLStreamWriter.class);
161: boolean startedHeader = false;
162: BindingOperationInfo unwrappedOp = bop;
163: if (bop.isUnwrapped()) {
164: unwrappedOp = bop.getWrappedOperation();
165: }
166: boolean client = isRequestor(message);
167: BindingMessageInfo bmi = client ? unwrappedOp.getInput()
168: : unwrappedOp.getOutput();
169: BindingMessageInfo wrappedBmi = client ? bop.getInput() : bop
170: .getOutput();
171:
172: if (bmi == null) {
173: return endedHeader;
174: }
175:
176: List<MessagePartInfo> parts = wrappedBmi.getMessageInfo()
177: .getMessageParts();
178: if (parts.size() > 0) {
179: MessageContentsList objs = MessageContentsList
180: .getContentsList(message);
181: if (objs == null) {
182: return endedHeader;
183: }
184: SoapVersion soapVersion = message.getVersion();
185: List<SoapHeaderInfo> headers = bmi
186: .getExtensors(SoapHeaderInfo.class);
187: if (headers == null) {
188: return endedHeader;
189: }
190:
191: for (SoapHeaderInfo header : headers) {
192: MessagePartInfo part = header.getPart();
193:
194: Object arg = objs.get(part);
195: objs.remove(part);
196: if (!(startedHeader || preexistingHeaders)) {
197: try {
198: xtw.writeStartElement(soapVersion.getPrefix(),
199: soapVersion.getHeader().getLocalPart(),
200: soapVersion.getNamespace());
201: } catch (XMLStreamException e) {
202: throw new SoapFault(
203: new org.apache.cxf.common.i18n.Message(
204: "XML_WRITE_EXC", BUNDLE), e,
205: soapVersion.getSender());
206: }
207: startedHeader = true;
208: }
209: DataWriter<XMLStreamWriter> dataWriter = getDataWriter(message);
210: dataWriter.write(arg, header.getPart(), xtw);
211: }
212:
213: if (startedHeader || preexistingHeaders) {
214: try {
215: xtw.writeEndElement();
216: endedHeader = true;
217: } catch (XMLStreamException e) {
218: throw new SoapFault(
219: new org.apache.cxf.common.i18n.Message(
220: "XML_WRITE_EXC", BUNDLE), e,
221: soapVersion.getSender());
222: }
223: }
224: }
225: return endedHeader;
226: }
227:
228: protected boolean isRequestor(Message message) {
229: return Boolean.TRUE.equals(message
230: .containsKey(Message.REQUESTOR_ROLE));
231: }
232:
233: protected DataWriter<XMLStreamWriter> getDataWriter(Message message) {
234: Service service = ServiceModelUtil.getService(message
235: .getExchange());
236: DataWriter<XMLStreamWriter> dataWriter = service
237: .getDataBinding().createWriter(XMLStreamWriter.class);
238: dataWriter.setAttachments(message.getAttachments());
239:
240: if (dataWriter == null) {
241: throw new Fault(new org.apache.cxf.common.i18n.Message(
242: "NO_DATAWRITER", BUNDLE, service.getName()));
243: }
244:
245: return dataWriter;
246: }
247:
248: public class SoapOutEndingInterceptor extends
249: AbstractSoapInterceptor {
250: public SoapOutEndingInterceptor() {
251: super (SoapOutEndingInterceptor.class.getName(),
252: Phase.WRITE_ENDING);
253: }
254:
255: public void handleMessage(SoapMessage message) throws Fault {
256: SoapVersion soapVersion = message.getVersion();
257: try {
258: XMLStreamWriter xtw = message
259: .getContent(XMLStreamWriter.class);
260: if (xtw != null) {
261: xtw.writeEndElement();
262: // Write Envelope end element
263: xtw.writeEndElement();
264: xtw.writeEndDocument();
265:
266: xtw.flush();
267: }
268: } catch (XMLStreamException e) {
269: throw new SoapFault(
270: new org.apache.cxf.common.i18n.Message(
271: "XML_WRITE_EXC", BUNDLE), e,
272: soapVersion.getSender());
273: }
274: }
275:
276: }
277: }
|