001: package org.objectweb.celtix.bus.ws.addressing.soap;
002:
003: import java.util.Iterator;
004: import java.util.Map;
005: import java.util.Set;
006: import java.util.logging.Level;
007: import java.util.logging.Logger;
008:
009: import javax.xml.bind.JAXBContext;
010: import javax.xml.bind.JAXBElement;
011: import javax.xml.bind.JAXBException;
012: import javax.xml.bind.Marshaller;
013: import javax.xml.bind.Unmarshaller;
014: import javax.xml.namespace.QName;
015: import javax.xml.soap.MimeHeaders;
016: import javax.xml.soap.Name;
017: import javax.xml.soap.SOAPEnvelope;
018: import javax.xml.soap.SOAPException;
019: import javax.xml.soap.SOAPFactory;
020: import javax.xml.soap.SOAPFault;
021: import javax.xml.soap.SOAPHeader;
022: import javax.xml.soap.SOAPHeaderElement;
023: import javax.xml.soap.SOAPMessage;
024: import javax.xml.ws.handler.MessageContext;
025: import javax.xml.ws.handler.soap.SOAPHandler;
026: import javax.xml.ws.handler.soap.SOAPMessageContext;
027: import javax.xml.ws.soap.SOAPFaultException;
028:
029: import org.objectweb.celtix.bus.ws.addressing.AddressingPropertiesImpl;
030: import org.objectweb.celtix.bus.ws.addressing.ContextUtils;
031: import org.objectweb.celtix.bus.ws.addressing.Names;
032: import org.objectweb.celtix.common.logging.LogUtils;
033: import org.objectweb.celtix.ws.addressing.AddressingProperties;
034: import org.objectweb.celtix.ws.addressing.AttributedURIType;
035: import org.objectweb.celtix.ws.addressing.EndpointReferenceType;
036: import org.objectweb.celtix.ws.addressing.RelatesToType;
037:
038: /**
039: * Protocol Handler responsible for {en|de}coding the Message Addressing
040: * Properties for {outgo|incom}ing messages.
041: */
042: public class MAPCodec implements SOAPHandler<SOAPMessageContext> {
043:
044: private static final Logger LOG = LogUtils
045: .getL7dLogger(MAPCodec.class);
046: private static SOAPFactory soapFactory;
047:
048: private VersionTransformer transformer;
049:
050: /**
051: * Constructor.
052: */
053: public MAPCodec() {
054: transformer = new VersionTransformer(this );
055: }
056:
057: /**
058: * Initialize the handler.
059: */
060: public void init(Map<String, Object> map) {
061: }
062:
063: /**
064: * @return the set of SOAP headers understood by this handler
065: */
066: public Set<QName> getHeaders() {
067: return VersionTransformer.HEADERS;
068: }
069:
070: /**
071: * Invoked for normal processing of inbound and outbound messages.
072: *
073: * @param context the messsage context
074: */
075: public boolean handleMessage(SOAPMessageContext context) {
076: return mediate(context);
077: }
078:
079: /**
080: * Invoked for fault processing.
081: *
082: * @param context the messsage context
083: */
084: public boolean handleFault(SOAPMessageContext context) {
085: return mediate(context);
086: }
087:
088: /**
089: * Called at the conclusion of a message exchange pattern just prior to
090: * the JAX-WS runtime dispatching a message, fault or exception.
091: *
092: * @param context the message context
093: */
094: public void close(MessageContext context) {
095: }
096:
097: /**
098: * Release handler resources.
099: */
100: public void destroy() {
101: }
102:
103: /**
104: * Mediate message flow, peforming MAP {en|de}coding.
105: *
106: * @param context the messsage context
107: * @return true if processing should continue on dispatch path
108: */
109: private boolean mediate(SOAPMessageContext context) {
110: if (ContextUtils.isOutbound(context)) {
111: encode(context, ContextUtils.retrieveMAPs(context, false,
112: true));
113: } else {
114: ContextUtils.storeMAPs(decode(context), context, false);
115: }
116: return true;
117: }
118:
119: /**
120: * Encode the current MAPs in protocol-specific headers.
121: *
122: * @param context the messsage context
123: * @param maps the MAPs to encode
124: */
125: private void encode(SOAPMessageContext context,
126: AddressingProperties maps) {
127: if (maps != null) {
128: SOAPMessage message = context.getMessage();
129: LOG.log(Level.INFO, "encoding MAPs in SOAP headers");
130: try {
131: SOAPEnvelope env = message.getSOAPPart().getEnvelope();
132: SOAPHeader header = env.getHeader() != null ? env
133: .getHeader() : env.addHeader();
134: discardMAPs(header);
135: header.addNamespaceDeclaration(
136: Names.WSA_NAMESPACE_PREFIX, maps
137: .getNamespaceURI());
138: JAXBContext jaxbContext = VersionTransformer
139: .getExposedJAXBContext(maps.getNamespaceURI());
140: Marshaller marshaller = jaxbContext.createMarshaller();
141: marshaller.setProperty(Marshaller.JAXB_FRAGMENT,
142: Boolean.TRUE);
143: transformer.encodeAsExposed(maps.getNamespaceURI(),
144: maps.getMessageID(), Names.WSA_MESSAGEID_NAME,
145: AttributedURIType.class, header, marshaller);
146: transformer.encodeAsExposed(maps.getNamespaceURI(),
147: maps.getTo(), Names.WSA_TO_NAME,
148: AttributedURIType.class, header, marshaller);
149: transformer
150: .encodeAsExposed(maps.getNamespaceURI(), maps
151: .getReplyTo(), Names.WSA_REPLYTO_NAME,
152: EndpointReferenceType.class, header,
153: marshaller);
154: transformer
155: .encodeAsExposed(maps.getNamespaceURI(), maps
156: .getFaultTo(), Names.WSA_FAULTTO_NAME,
157: EndpointReferenceType.class, header,
158: marshaller);
159: transformer.encodeAsExposed(maps.getNamespaceURI(),
160: maps.getRelatesTo(), Names.WSA_RELATESTO_NAME,
161: RelatesToType.class, header, marshaller);
162: transformer.encodeAsExposed(maps.getNamespaceURI(),
163: maps.getAction(), Names.WSA_ACTION_NAME,
164: AttributedURIType.class, header, marshaller);
165: propogateAction(maps.getAction(), message);
166: applyMAPValidation(context);
167: } catch (SOAPException se) {
168: LOG.log(Level.WARNING,
169: "SOAP_HEADER_ENCODE_FAILURE_MSG", se);
170: } catch (JAXBException je) {
171: LOG.log(Level.WARNING,
172: "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
173: }
174: }
175: }
176:
177: /**
178: * Decode the MAPs from protocol-specific headers.
179: *
180: * @param message the SOAP message
181: * @param the decoded MAPs
182: * @exception SOAPFaultException if decoded MAPs are invalid
183: */
184: public AddressingProperties unmarshalMAPs(SOAPMessage message) {
185: // REVISIT generate MessageAddressingHeaderRequired fault if an
186: // expected header is missing
187: AddressingPropertiesImpl maps = null;
188: try {
189: SOAPEnvelope env = message.getSOAPPart().getEnvelope();
190: SOAPHeader header = env.getHeader();
191: if (header != null) {
192: Unmarshaller unmarshaller = null;
193: Iterator headerElements = header
194: .examineAllHeaderElements();
195: while (headerElements.hasNext()) {
196: SOAPHeaderElement headerElement = (SOAPHeaderElement) headerElements
197: .next();
198: Name headerName = headerElement.getElementName();
199: String headerURI = headerName.getURI();
200: if (unmarshaller == null) {
201: JAXBContext jaxbContext = VersionTransformer
202: .getExposedJAXBContext(headerURI);
203: unmarshaller = jaxbContext.createUnmarshaller();
204: }
205: if (transformer.isSupported(headerURI)) {
206: if (maps == null) {
207: maps = new AddressingPropertiesImpl();
208: maps.exposeAs(headerURI);
209: }
210: String localName = headerName.getLocalName();
211: LOG.log(Level.INFO, "decoding WSA header {0}",
212: localName);
213: if (Names.WSA_MESSAGEID_NAME.equals(localName)) {
214: maps
215: .setMessageID(transformer
216: .decodeAsNative(
217: headerURI,
218: AttributedURIType.class,
219: headerElement,
220: unmarshaller));
221: } else if (Names.WSA_TO_NAME.equals(localName)) {
222: maps.setTo(transformer.decodeAsNative(
223: headerURI, AttributedURIType.class,
224: headerElement, unmarshaller));
225: } else if (Names.WSA_REPLYTO_NAME
226: .equals(localName)) {
227: maps.setReplyTo(transformer.decodeAsNative(
228: headerURI,
229: EndpointReferenceType.class,
230: headerElement, unmarshaller));
231: } else if (Names.WSA_FAULTTO_NAME
232: .equals(localName)) {
233: maps.setFaultTo(transformer.decodeAsNative(
234: headerURI,
235: EndpointReferenceType.class,
236: headerElement, unmarshaller));
237: } else if (Names.WSA_RELATESTO_NAME
238: .equals(localName)) {
239: maps
240: .setRelatesTo(transformer
241: .decodeAsNative(
242: headerURI,
243: RelatesToType.class,
244: headerElement,
245: unmarshaller));
246: } else if (Names.WSA_ACTION_NAME
247: .equals(localName)) {
248: maps.setAction(transformer.decodeAsNative(
249: headerURI, AttributedURIType.class,
250: headerElement, unmarshaller));
251: }
252: } else if (headerURI
253: .contains(Names.WSA_NAMESPACE_PATTERN)) {
254: LOG.log(Level.WARNING,
255: "UNSUPPORTED_VERSION_MSG", headerURI);
256: }
257: }
258: }
259: } catch (SOAPException se) {
260: LOG
261: .log(Level.WARNING,
262: "SOAP_HEADER_DECODE_FAILURE_MSG", se);
263: } catch (JAXBException je) {
264: LOG
265: .log(Level.WARNING,
266: "SOAP_HEADER_DECODE_FAILURE_MSG", je);
267: }
268: return maps;
269: }
270:
271: /**
272: * Decode the MAPs from protocol-specific headers.
273: *
274: * @param context the messsage context
275: * @param the decoded MAPs
276: * @exception SOAPFaultException if decoded MAPs are invalid
277: */
278: private AddressingProperties decode(SOAPMessageContext context) {
279: // REVISIT generate MessageAddressingHeaderRequired fault if an
280: // expected header is missing
281: AddressingProperties maps = null;
282: boolean isRequestor = ContextUtils.isRequestor(context);
283: SOAPMessage message = context.getMessage();
284: maps = unmarshalMAPs(message);
285: if (isRequestor && null != maps.getRelatesTo()) {
286: ContextUtils.storeCorrelationID(maps.getRelatesTo(), false,
287: context);
288: }
289: return maps;
290: }
291:
292: /**
293: * Encodes an MAP as a SOAP header.
294: *
295: * @param value the value to encode
296: * @param qname the QName for the header
297: * @param clz the class
298: * @param header the SOAP header
299: * @param marshaller the JAXB marshaller to use
300: */
301: protected <T> void encodeMAP(T value, QName qname, Class<T> clz,
302: SOAPHeader header, Marshaller marshaller)
303: throws JAXBException {
304: LOG.log(Level.INFO, "encoding WSA header {0}", qname);
305: if (value != null) {
306: marshaller.marshal(new JAXBElement<T>(qname, clz, value),
307: header);
308: }
309: }
310:
311: /**
312: * Decodes a MAP from a SOAP header.
313: *
314: * @param clz the class
315: * @param headerElement the SOAP header element
316: * @param marshaller the JAXB marshaller to use
317: * @return the decoded value
318: */
319: protected <T> T decodeMAP(Class<T> clz,
320: SOAPHeaderElement headerElement, Unmarshaller unmarshaller)
321: throws JAXBException {
322: JAXBElement<T> element = unmarshaller.unmarshal(headerElement,
323: clz);
324: return element.getValue();
325: }
326:
327: /**
328: * Discard any pre-existing MAP headers - this may occur if the runtime
329: * re-uses a SOAP message.
330: *
331: * @param header the SOAP header
332: */
333: private void discardMAPs(SOAPHeader header) throws SOAPException {
334: Iterator headerElements = header.examineAllHeaderElements();
335: while (headerElements.hasNext()) {
336: SOAPHeaderElement headerElement = (SOAPHeaderElement) headerElements
337: .next();
338: Name headerName = headerElement.getElementName();
339: if (Names.WSA_NAMESPACE_NAME.equals(headerName.getURI())) {
340: headerElement.detachNode();
341: }
342: }
343: }
344:
345: /**
346: * Propogate action to SOAPAction header
347: *
348: * @param action the Action property
349: * @param message the SOAP message
350: */
351: private void propogateAction(AttributedURIType action,
352: SOAPMessage message) {
353: if (!(action == null || "".equals(action.getValue()))) {
354: MimeHeaders mimeHeaders = message.getMimeHeaders();
355: String[] soapActionHeaders = mimeHeaders
356: .getHeader(Names.SOAP_ACTION_HEADER);
357: // only propogate to SOAPAction header if currently non-empty
358: if (!(soapActionHeaders == null
359: || soapActionHeaders.length == 0 || ""
360: .equals(soapActionHeaders[0]))) {
361: LOG.log(Level.INFO,
362: "encoding wsa:Action in SOAPAction header {0}",
363: action.getValue());
364: String soapAction = "\"" + action.getValue() + "\"";
365: mimeHeaders.setHeader(Names.SOAP_ACTION_HEADER,
366: soapAction);
367: }
368: }
369: }
370:
371: /**
372: * Apply results of validation of incoming MAPs.
373: *
374: * @param context the message context
375: * @exception SOAPFaultException if the MAPs are invalid
376: * @exception SOAPException if SOAPFault cannot be constructed
377: */
378: private void applyMAPValidation(SOAPMessageContext context)
379: throws SOAPException {
380: String faultName = ContextUtils.retrieveMAPFaultName(context);
381: if (faultName != null) {
382: String reason = ContextUtils
383: .retrieveMAPFaultReason(context);
384: throw createSOAPFaultException(faultName,
385: Names.WSA_NAMESPACE_PREFIX,
386: Names.WSA_NAMESPACE_NAME, reason);
387: }
388: }
389:
390: /**
391: * @return SOAPFactory
392: */
393: private static synchronized SOAPFactory getSOAPFactory()
394: throws SOAPException {
395: if (soapFactory == null) {
396: soapFactory = SOAPFactory.newInstance();
397: }
398: return soapFactory;
399: }
400:
401: /**
402: * Create a SOAPFaultException.
403: *
404: * @param localName the fault local name
405: * @param prefix the fault prefix
406: * @param namespace the fault namespace
407: * @param reason the fault reason
408: * @return a new SOAPFaultException
409: */
410: private SOAPFaultException createSOAPFaultException(
411: String localName, String prefix, String namespace,
412: String reason) throws SOAPException {
413: SOAPFactory factory = getSOAPFactory();
414: SOAPFault fault = factory.createFault();
415: Name qname = factory.createName(localName, prefix, namespace);
416: fault.setFaultCode(qname);
417: fault.setFaultString(reason);
418: return new SOAPFaultException(fault);
419: }
420: }
|