001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.soap.interceptors.jbi;
018
019 import java.util.List;
020 import java.util.ArrayList;
021 import java.util.Collections;
022
023 import javax.xml.XMLConstants;
024 import javax.xml.namespace.QName;
025 import javax.xml.namespace.NamespaceContext;
026 import javax.xml.stream.XMLStreamReader;
027 import javax.xml.stream.XMLStreamException;
028 import javax.xml.stream.Location;
029 import javax.xml.transform.Source;
030 import javax.xml.transform.dom.DOMSource;
031
032 import org.apache.servicemix.soap.api.Fault;
033 import org.apache.servicemix.soap.api.Message;
034 import org.apache.servicemix.soap.api.model.Operation;
035 import org.apache.servicemix.soap.bindings.soap.SoapFault;
036 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapBinding;
037 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapMessage;
038 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapOperation;
039 import org.apache.servicemix.soap.bindings.soap.model.wsdl1.Wsdl1SoapPart;
040 import org.apache.servicemix.soap.core.AbstractInterceptor;
041 import org.apache.servicemix.soap.util.DomUtil;
042 import org.apache.servicemix.soap.util.QNameUtil;
043 import org.apache.servicemix.soap.util.stax.StaxUtil;
044 import org.apache.servicemix.soap.util.stax.StaxSource;
045 import org.apache.servicemix.soap.util.stax.DOMStreamReader;
046 import org.apache.servicemix.soap.util.stax.FragmentStreamReader;
047 import org.apache.servicemix.soap.util.stax.ExtendedXMLStreamReader;
048
049 import org.w3c.dom.Document;
050 import org.w3c.dom.DocumentFragment;
051 import org.w3c.dom.Element;
052 import org.w3c.dom.Node;
053 import org.w3c.dom.NodeList;
054
055 /**
056 * @author <a href="mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
057 */
058 public class JbiInWsdl1Interceptor extends AbstractInterceptor {
059
060 private final boolean server;
061
062 public JbiInWsdl1Interceptor(boolean server) {
063 this.server = server;
064 }
065
066 public void handleMessage(Message message) {
067 // Ignore faults messages
068 if (message.getContent(Exception.class) != null) {
069 return;
070 }
071 // Check if we should not use the JBI wrapper
072 if (message.get(JbiConstants.USE_JBI_WRAPPER) instanceof Boolean && ((Boolean) message.get(JbiConstants.USE_JBI_WRAPPER)) == false) {
073 XMLStreamReader xmlReader = message.getContent(XMLStreamReader.class);
074 if (xmlReader != null) {
075 message.setContent(Source.class, StaxUtil.createSource(xmlReader));
076 }
077 return;
078 }
079
080 try {
081 XMLStreamReader reader = new StaxJbiWrapper(message);
082 message.setContent(Source.class, new StaxSource(reader));
083 //throw new UnsupportedOperationException();
084 } catch (UnsupportedOperationException e) {
085 Document document = createDomWrapper(message);
086 message.setContent(Source.class, new DOMSource(document));
087 }
088 }
089
090 protected Document createDomWrapper(Message message) {
091 Wsdl1SoapOperation wsdlOperation = getOperation(message);
092 Wsdl1SoapMessage wsdlMessage = server ? wsdlOperation.getInput() : wsdlOperation.getOutput();
093 Document document = DomUtil.createDocument();
094 Element root = DomUtil.createElement(document, JbiConstants.WSDL11_WRAPPER_MESSAGE);
095 String typeNamespace = wsdlMessage.getName().getNamespaceURI();
096 if (typeNamespace == null || typeNamespace.length() == 0) {
097 throw new IllegalArgumentException("messageType namespace is null or empty");
098 }
099 root.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX,
100 typeNamespace);
101 String typeLocalName = wsdlMessage.getName().getLocalPart();
102 if (typeLocalName == null || typeLocalName.length() == 0) {
103 throw new IllegalArgumentException("messageType local name is null or empty");
104 }
105 root.setAttribute(JbiConstants.WSDL11_WRAPPER_TYPE, JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX + ":" + typeLocalName);
106 String messageName = wsdlMessage.getMessageName();
107 root.setAttribute(JbiConstants.WSDL11_WRAPPER_NAME, messageName);
108 root.setAttribute(JbiConstants.WSDL11_WRAPPER_VERSION, "1.0");
109
110 Element body = getBodyElement(message);
111 for (Wsdl1SoapPart part : wsdlMessage.getParts()) {
112 if (part.isBody()) {
113 if (wsdlOperation.getStyle() == Wsdl1SoapBinding.Style.DOCUMENT) {
114 addPart(root, body);
115 } else /* rpc-style */ {
116 // SOAP:Body element is the operation name, children are operation parameters
117 Element param = DomUtil.getFirstChildElement(body);
118 boolean found = false;
119 while (param != null) {
120 if (part.getName().equals(param.getLocalName())) {
121 found = true;
122 addPart(root, param.getChildNodes());
123 }
124 param = DomUtil.getNextSiblingElement(param);
125 }
126 if (!found) {
127 throw new SoapFault(SoapFault.SENDER, "Missing part '" + part.getName() + "'");
128 }
129 }
130 } else {
131 QName element = part.getElement();
132 DocumentFragment header = message.getSoapHeaders().remove(element);
133 if (header == null) {
134 throw new SoapFault(SoapFault.SENDER, "Missing required header element: "
135 + QNameUtil.toString(element));
136 }
137 addPart(root, header.getChildNodes());
138 }
139 }
140 return document;
141 }
142
143 protected Wsdl1SoapOperation getOperation(Message message) {
144 Operation operation = message.get(Operation.class);
145 if (operation == null) {
146 throw new Fault("Operation not bound on this message");
147 }
148 if (operation instanceof Wsdl1SoapOperation == false) {
149 throw new Fault("Message is not bound to a WSDL 1.1 SOAP operation");
150 }
151 return (Wsdl1SoapOperation) operation;
152 }
153
154 /**
155 * Extract the content as a jaxp Source
156 */
157 protected Element getBodyElement(Message message) {
158 XMLStreamReader xmlReader = message.getContent(XMLStreamReader.class);
159 return xmlReader != null ? StaxUtil.createElement(xmlReader) : null;
160 }
161
162 /**
163 * Add a jbi:part to a normalized message document
164 */
165 private static void addPart(Element parent, Node partValue) {
166 Element element = DomUtil.createElement(parent, JbiConstants.WSDL11_WRAPPER_PART);
167 element.appendChild(element.getOwnerDocument().importNode(partValue, true));
168 }
169
170 /**
171 * Add a jbi:part to a normalized message document
172 */
173 private static void addPart(Element parent, NodeList nodes) {
174 Element element = DomUtil.createElement(parent, JbiConstants.WSDL11_WRAPPER_PART);
175 for (int i = 0; i < nodes.getLength(); i++) {
176 Node node = nodes.item(i);
177 element.appendChild(element.getOwnerDocument().importNode(node, true));
178 }
179 }
180
181 protected class StaxJbiWrapper implements XMLStreamReader {
182 public static final int STATE_START_DOC = 0;
183 public static final int STATE_START_ELEMENT_WRAPPER = 1;
184 public static final int STATE_START_ELEMENT_PART = 2;
185 public static final int STATE_RUN_PART = 3;
186 public static final int STATE_END_ELEMENT_PART = 4;
187 public static final int STATE_END_ELEMENT_WRAPPER = 5;
188 public static final int STATE_END_DOC = 6;
189
190 private Message message;
191 private Wsdl1SoapMessage wsdlMessage;
192 private int state = STATE_START_DOC;
193 private int part = -1;
194 private int reader = -1;
195 private int event = START_DOCUMENT;
196 private List<List<XMLStreamReader>> parts = new ArrayList<List<XMLStreamReader>>();
197
198 public StaxJbiWrapper(Message message) {
199 this.message = message;
200 Wsdl1SoapOperation wsdlOperation = getOperation(message);
201 wsdlMessage = server ? wsdlOperation.getInput() : wsdlOperation.getOutput();
202 XMLStreamReader xmlReader = message.getContent(XMLStreamReader.class);
203 int nbBodyParts = 0;
204 for (Wsdl1SoapPart part : wsdlMessage.getParts()) {
205 if (part.isBody()) {
206 if (nbBodyParts++ > 0) {
207 throw new UnsupportedOperationException();
208 }
209 if (wsdlOperation.getStyle() == Wsdl1SoapBinding.Style.DOCUMENT) {
210 parts.add(Collections.<XMLStreamReader>singletonList(new FragmentStreamReader(xmlReader)));
211 } else /* rpc-style */ {
212 throw new UnsupportedOperationException();
213 }
214 } else {
215 QName element = part.getElement();
216 DocumentFragment header = message.getSoapHeaders().get(element);
217 if (header == null) {
218 throw new SoapFault(SoapFault.SENDER, "Missing required header element: "
219 + QNameUtil.toString(element));
220 }
221 List<XMLStreamReader> readers = new ArrayList<XMLStreamReader>();
222 NodeList l = header.getChildNodes();
223 for (int i = 0; i < l.getLength(); i++) {
224 Node n = l.item(i);
225 if (n instanceof Element) {
226 readers.add(new DOMStreamReader((Element) n));
227 }
228 }
229 parts.add(readers);
230 }
231 }
232 // Now that we know we can handle the message, remove header parts that are included in the jbi wrapper
233 for (Wsdl1SoapPart part : wsdlMessage.getParts()) {
234 if (!part.isBody()) {
235 message.getSoapHeaders().remove(part.getElement());
236 }
237 }
238 }
239
240 public int getEventType() {
241 return event;
242 }
243
244 public int next() throws XMLStreamException {
245 switch (state) {
246 case STATE_START_DOC:
247 state = STATE_START_ELEMENT_WRAPPER;
248 event = START_ELEMENT;
249 break;
250 case STATE_START_ELEMENT_WRAPPER:
251 if (parts.size() > 0) {
252 state = STATE_START_ELEMENT_PART;
253 event = START_ELEMENT;
254 part = 0;
255 reader = 0;
256 } else {
257 state = STATE_END_ELEMENT_WRAPPER;
258 event = END_ELEMENT;
259 }
260 break;
261 case STATE_START_ELEMENT_PART:
262 if (reader >= parts.get(part).size()) {
263 state = STATE_END_ELEMENT_PART;
264 event = END_ELEMENT;
265 } else {
266 state = STATE_RUN_PART;
267 event = parts.get(part).get(reader).next();
268 if (event == START_DOCUMENT) {
269 event = parts.get(part).get(reader).next();
270 }
271 }
272 break;
273 case STATE_RUN_PART:
274 event = parts.get(part).get(reader).next();
275 if (event == END_DOCUMENT) {
276 if (++reader >= parts.get(part).size()) {
277 state = STATE_END_ELEMENT_PART;
278 event = END_ELEMENT;
279 } else {
280 event = parts.get(part).get(reader).next();
281 if (event == START_DOCUMENT) {
282 event = parts.get(part).get(reader).next();
283 }
284 }
285 }
286 break;
287 case STATE_END_ELEMENT_PART:
288 if (++part >= parts.size()) {
289 state = STATE_END_ELEMENT_WRAPPER;
290 event = END_ELEMENT;
291 } else {
292 state = STATE_START_ELEMENT_PART;
293 event = START_ELEMENT;
294 reader = 0;
295 }
296 break;
297 case STATE_END_ELEMENT_WRAPPER:
298 case STATE_END_DOC:
299 state = STATE_END_DOC;
300 event = END_DOCUMENT;
301 break;
302 }
303 return event;
304 }
305
306 public QName getName() {
307 switch (state) {
308 case STATE_START_ELEMENT_WRAPPER:
309 case STATE_END_ELEMENT_WRAPPER:
310 return JbiConstants.WSDL11_WRAPPER_MESSAGE;
311 case STATE_START_ELEMENT_PART:
312 case STATE_END_ELEMENT_PART:
313 return JbiConstants.WSDL11_WRAPPER_PART;
314 case STATE_RUN_PART:
315 return parts.get(part).get(reader).getName();
316 default:
317 throw new IllegalStateException();
318 }
319 }
320
321 public String getLocalName() {
322 switch (state) {
323 case STATE_START_ELEMENT_WRAPPER:
324 case STATE_END_ELEMENT_WRAPPER:
325 return JbiConstants.WSDL11_WRAPPER_MESSAGE_LOCALNAME;
326 case STATE_START_ELEMENT_PART:
327 case STATE_END_ELEMENT_PART:
328 return JbiConstants.WSDL11_WRAPPER_PART_LOCALNAME;
329 case STATE_RUN_PART:
330 return parts.get(part).get(reader).getLocalName();
331 default:
332 throw new IllegalStateException();
333 }
334 }
335
336 public String getNamespaceURI() {
337 switch (state) {
338 case STATE_START_ELEMENT_WRAPPER:
339 case STATE_END_ELEMENT_WRAPPER:
340 case STATE_START_ELEMENT_PART:
341 case STATE_END_ELEMENT_PART:
342 return JbiConstants.WSDL11_WRAPPER_NAMESPACE;
343 case STATE_RUN_PART:
344 return parts.get(part).get(reader).getNamespaceURI();
345 default:
346 throw new IllegalStateException();
347 }
348 }
349
350 public String getPrefix() {
351 switch (state) {
352 case STATE_START_ELEMENT_WRAPPER:
353 case STATE_END_ELEMENT_WRAPPER:
354 case STATE_START_ELEMENT_PART:
355 case STATE_END_ELEMENT_PART:
356 return JbiConstants.WSDL11_WRAPPER_PREFIX;
357 case STATE_RUN_PART:
358 return parts.get(part).get(reader).getPrefix();
359 default:
360 throw new IllegalStateException();
361 }
362 }
363
364 public boolean hasName() {
365 return state == STATE_RUN_PART ? parts.get(part).get(reader).isStartElement() : (event == START_ELEMENT || event == END_ELEMENT);
366 }
367
368 public Object getProperty(String s) throws IllegalArgumentException {
369 return null; //To change body of implemented methods use File | Settings | File Templates.
370 }
371
372 public void require(int i, String s, String s1) throws XMLStreamException {
373 //To change body of implemented methods use File | Settings | File Templates.
374 }
375
376 public String getElementText() throws XMLStreamException {
377 return null; //To change body of implemented methods use File | Settings | File Templates.
378 }
379
380 public int nextTag() throws XMLStreamException {
381 while (hasNext()) {
382 int e = next();
383 if (e == START_ELEMENT || e == END_ELEMENT)
384 return e;
385 }
386 return event;
387 }
388
389 public boolean hasNext() throws XMLStreamException {
390 return event != END_DOCUMENT;
391 }
392
393 public void close() throws XMLStreamException {
394 //To change body of implemented methods use File | Settings | File Templates.
395 }
396
397 public String getNamespaceURI(String s) {
398 return null; //To change body of implemented methods use File | Settings | File Templates.
399 }
400
401 public boolean isStartElement() {
402 return state == STATE_RUN_PART ? parts.get(part).get(reader).isStartElement() : event == START_ELEMENT;
403 }
404
405 public boolean isEndElement() {
406 return state == STATE_RUN_PART ? parts.get(part).get(reader).isEndElement() : event == END_ELEMENT;
407 }
408
409 public boolean isCharacters() {
410 return state == STATE_RUN_PART ? parts.get(part).get(reader).isCharacters() : event == CHARACTERS;
411 }
412
413 public boolean isWhiteSpace() {
414 return state == STATE_RUN_PART ? parts.get(part).get(reader).isWhiteSpace() : event == SPACE;
415 }
416
417 public String getAttributeValue(String s, String s1) {
418 throw new UnsupportedOperationException("Not implemented");
419 }
420
421 public int getAttributeCount() {
422 switch (state) {
423 case STATE_START_ELEMENT_WRAPPER:
424 return 4;
425 case STATE_START_ELEMENT_PART:
426 return 0;
427 case STATE_RUN_PART:
428 return parts.get(part).get(reader).getAttributeCount();
429 default:
430 throw new IllegalStateException();
431 }
432 }
433
434 public QName getAttributeName(int i) {
435 switch (state) {
436 case STATE_START_ELEMENT_WRAPPER:
437 switch (i) {
438 case 0: return new QName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI,
439 JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX,
440 XMLConstants.XMLNS_ATTRIBUTE);
441 case 1: return new QName(JbiConstants.WSDL11_WRAPPER_TYPE);
442 case 2: return new QName(JbiConstants.WSDL11_WRAPPER_NAME);
443 case 3: return new QName(JbiConstants.WSDL11_WRAPPER_VERSION);
444 default: throw new IllegalStateException();
445 }
446 case STATE_RUN_PART:
447 return parts.get(part).get(reader).getAttributeName(i);
448 default:
449 throw new IllegalStateException();
450 }
451 }
452
453 public String getAttributeNamespace(int i) {
454 switch (state) {
455 case STATE_START_ELEMENT_WRAPPER:
456 switch (i) {
457 case 0: return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
458 case 1:
459 case 2:
460 case 3: return XMLConstants.NULL_NS_URI;
461 default: throw new IllegalStateException();
462 }
463 case STATE_RUN_PART:
464 return parts.get(part).get(reader).getAttributeNamespace(i);
465 default:
466 throw new IllegalStateException();
467 }
468 }
469
470 public String getAttributeLocalName(int i) {
471 switch (state) {
472 case STATE_START_ELEMENT_WRAPPER:
473 switch (i) {
474 case 0: return JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX;
475 case 1: return JbiConstants.WSDL11_WRAPPER_TYPE;
476 case 2: return JbiConstants.WSDL11_WRAPPER_NAME;
477 case 3: return JbiConstants.WSDL11_WRAPPER_VERSION;
478 default: throw new IllegalStateException();
479 }
480 case STATE_RUN_PART:
481 return parts.get(part).get(reader).getAttributeLocalName(i);
482 default:
483 throw new IllegalStateException();
484 }
485 }
486
487 public String getAttributePrefix(int i) {
488 switch (state) {
489 case STATE_START_ELEMENT_WRAPPER:
490 switch (i) {
491 case 0: return XMLConstants.XMLNS_ATTRIBUTE;
492 case 1:
493 case 2:
494 case 3: return XMLConstants.DEFAULT_NS_PREFIX;
495 default: throw new IllegalStateException();
496 }
497 case STATE_RUN_PART:
498 return parts.get(part).get(reader).getAttributePrefix(i);
499 default:
500 throw new IllegalStateException();
501 }
502 }
503
504 public String getAttributeType(int i) {
505 return "CDATA";
506 }
507
508 public String getAttributeValue(int i) {
509 switch (state) {
510 case STATE_START_ELEMENT_WRAPPER:
511 switch (i) {
512 case 0:
513 {
514 String typeNamespace = wsdlMessage.getName().getNamespaceURI();
515 if (typeNamespace == null || typeNamespace.length() == 0) {
516 throw new IllegalArgumentException("messageType namespace is null or empty");
517 }
518 return typeNamespace;
519 }
520 case 1:
521 {
522 //root.setAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX,
523 // typeNamespace);
524 String typeLocalName = wsdlMessage.getName().getLocalPart();
525 if (typeLocalName == null || typeLocalName.length() == 0) {
526 throw new IllegalArgumentException("messageType local name is null or empty");
527 }
528 return JbiConstants.WSDL11_WRAPPER_MESSAGE_PREFIX + ":" + typeLocalName;
529 }
530 case 2:
531 return wsdlMessage.getMessageName();
532 case 3:
533 return "1.0";
534 default: throw new IllegalStateException();
535 }
536 case STATE_RUN_PART:
537 return parts.get(part).get(reader).getAttributeValue(i);
538 default:
539 throw new IllegalStateException();
540 }
541 }
542
543 public boolean isAttributeSpecified(int i) {
544 throw new UnsupportedOperationException("Not implemented");
545 }
546
547 public int getNamespaceCount() {
548 return 0;
549 }
550
551 public String getNamespacePrefix(int i) {
552 throw new UnsupportedOperationException("Not implemented");
553 }
554
555 public String getNamespaceURI(int i) {
556 throw new UnsupportedOperationException("Not implemented");
557 }
558
559 public NamespaceContext getNamespaceContext() {
560 if (state == STATE_RUN_PART) {
561 return parts.get(part).get(reader).getNamespaceContext();
562 } else {
563 return new ExtendedXMLStreamReader.SimpleNamespaceContext();
564 }
565 }
566
567 public String getText() {
568 if (state == STATE_RUN_PART) {
569 return parts.get(part).get(reader).getText();
570 } else {
571 throw new IllegalStateException();
572 }
573 }
574
575 public char[] getTextCharacters() {
576 if (state == STATE_RUN_PART) {
577 return parts.get(part).get(reader).getTextCharacters();
578 } else {
579 throw new IllegalStateException();
580 }
581 }
582
583 public int getTextCharacters(int i, char[] chars, int i1, int i2) throws XMLStreamException {
584 if (state == STATE_RUN_PART) {
585 return parts.get(part).get(reader).getTextCharacters(i, chars, i1, i2);
586 } else {
587 throw new IllegalStateException();
588 }
589 }
590
591 public int getTextStart() {
592 if (state == STATE_RUN_PART) {
593 return parts.get(part).get(reader).getTextStart();
594 } else {
595 throw new IllegalStateException();
596 }
597 }
598
599 public int getTextLength() {
600 if (state == STATE_RUN_PART) {
601 return parts.get(part).get(reader).getTextLength();
602 } else {
603 throw new IllegalStateException();
604 }
605 }
606
607 public String getEncoding() {
608 throw new UnsupportedOperationException("Not implemented");
609 }
610
611 public boolean hasText() {
612 if (state == STATE_RUN_PART) {
613 return parts.get(part).get(reader).hasText();
614 } else {
615 return false;
616 }
617 }
618
619 public Location getLocation() {
620 return new Location() {
621 public int getCharacterOffset() {
622 return 0;
623 }
624 public int getColumnNumber() {
625 return 0;
626 }
627 public int getLineNumber() {
628 return 0;
629 }
630 public String getPublicId() {
631 return null;
632 }
633 public String getSystemId() {
634 return null;
635 }
636 };
637 }
638
639 public String getVersion() {
640 throw new UnsupportedOperationException("Not implemented");
641 }
642
643 public boolean isStandalone() {
644 throw new UnsupportedOperationException("Not implemented");
645 }
646
647 public boolean standaloneSet() {
648 throw new UnsupportedOperationException("Not implemented");
649 }
650
651 public String getCharacterEncodingScheme() {
652 throw new UnsupportedOperationException("Not implemented");
653 }
654
655 public String getPITarget() {
656 throw new UnsupportedOperationException("Not implemented");
657 }
658
659 public String getPIData() {
660 throw new UnsupportedOperationException("Not implemented");
661 }
662 }
663 }