001: package org.objectweb.celtix.bus.ws.rm.soap;
002:
003: import java.util.ArrayList;
004: import java.util.Collection;
005: import java.util.Iterator;
006: import java.util.Map;
007: import java.util.Set;
008: import java.util.logging.Level;
009: import java.util.logging.Logger;
010:
011: import javax.xml.bind.JAXBContext;
012: import javax.xml.bind.JAXBElement;
013: import javax.xml.bind.JAXBException;
014: import javax.xml.bind.Marshaller;
015: import javax.xml.bind.Unmarshaller;
016: import javax.xml.namespace.QName;
017: import javax.xml.soap.Name;
018: import javax.xml.soap.SOAPEnvelope;
019: import javax.xml.soap.SOAPException;
020: import javax.xml.soap.SOAPHeader;
021: import javax.xml.soap.SOAPHeaderElement;
022: import javax.xml.soap.SOAPMessage;
023: import javax.xml.ws.handler.MessageContext;
024: import javax.xml.ws.handler.soap.SOAPHandler;
025: import javax.xml.ws.handler.soap.SOAPMessageContext;
026:
027: import org.objectweb.celtix.bindings.BindingContextUtils;
028: import org.objectweb.celtix.bindings.DataBindingCallback;
029: import org.objectweb.celtix.bus.ws.addressing.ContextUtils;
030: import org.objectweb.celtix.bus.ws.rm.CreateSequenceRequest;
031: import org.objectweb.celtix.bus.ws.rm.CreateSequenceResponse;
032: import org.objectweb.celtix.bus.ws.rm.Names;
033: import org.objectweb.celtix.bus.ws.rm.RMContextUtils;
034: import org.objectweb.celtix.bus.ws.rm.RMPropertiesImpl;
035: import org.objectweb.celtix.bus.ws.rm.RMUtils;
036: import org.objectweb.celtix.bus.ws.rm.TerminateSequenceRequest;
037: import org.objectweb.celtix.common.logging.LogUtils;
038: import org.objectweb.celtix.context.ObjectMessageContext;
039: import org.objectweb.celtix.ws.addressing.AddressingProperties;
040: import org.objectweb.celtix.ws.addressing.AttributedURIType;
041: import org.objectweb.celtix.ws.rm.AckRequestedType;
042: import org.objectweb.celtix.ws.rm.RMProperties;
043: import org.objectweb.celtix.ws.rm.SequenceAcknowledgement;
044: import org.objectweb.celtix.ws.rm.SequenceType;
045:
046: /**
047: * Protocol Handler responsible for {en|de}coding the RM
048: * Properties for {outgo|incom}ing messages.
049: */
050: public class RMSoapHandler implements SOAPHandler<SOAPMessageContext> {
051:
052: private static final Logger LOG = LogUtils
053: .getL7dLogger(RMSoapHandler.class);
054: private static final String WS_RM_PACKAGE = SequenceType.class
055: .getPackage().getName();
056: protected JAXBContext jaxbContext;
057:
058: /**
059: * Constructor.
060: */
061: public RMSoapHandler() {
062: }
063:
064: /**
065: * Initialize the handler.
066: */
067: public void init(Map<String, Object> map) {
068: }
069:
070: /**
071: * @return the set of SOAP headers understood by this handler
072: */
073: public Set<QName> getHeaders() {
074: return Names.HEADERS;
075: }
076:
077: /**
078: * Invoked for normal processing of inbound and outbound messages.
079: *
080: * @param context the messsage context
081: */
082: public boolean handleMessage(SOAPMessageContext context) {
083: return mediate(context);
084: }
085:
086: /**
087: * Invoked for fault processing.
088: *
089: * @param context the messsage context
090: */
091: public boolean handleFault(SOAPMessageContext context) {
092: return mediate(context);
093: }
094:
095: /**
096: * Called at the conclusion of a message exchange pattern just prior to
097: * the JAX-WS runtime dispatching a message, fault or exception.
098: *
099: * @param context the message context
100: */
101: public void close(MessageContext context) {
102: }
103:
104: /**
105: * Release handler resources.
106: */
107: public void destroy() {
108: }
109:
110: /**
111: * Mediate message flow, peforming MAP {en|de}coding.
112: *
113: * @param context the messsage context
114: * @return true if processing should continue on dispatch path
115: */
116: private boolean mediate(SOAPMessageContext context) {
117: if (ContextUtils.isOutbound(context)) {
118: encode(context);
119: } else {
120: decode(context);
121: storeBindingInfo(context);
122: }
123: return true;
124: }
125:
126: /**
127: * Encode the current RM properties in protocol-specific headers.
128: *
129: * @param context the message context.
130: */
131: private void encode(SOAPMessageContext context) {
132: RMProperties rmps = RMContextUtils.retrieveRMProperties(
133: context, true);
134: if (null == rmps) {
135: // nothing to encode
136: return;
137: }
138: SOAPMessage message = context.getMessage();
139: try {
140: SOAPEnvelope env = message.getSOAPPart().getEnvelope();
141: SOAPHeader header = env.getHeader() != null ? env
142: .getHeader() : env.addHeader();
143:
144: discardRMHeaders(header);
145: header.addNamespaceDeclaration(Names.WSRM_NAMESPACE_PREFIX,
146: Names.WSRM_NAMESPACE_NAME);
147: Marshaller marshaller = getJAXBContext().createMarshaller();
148: marshaller.setProperty(Marshaller.JAXB_FRAGMENT,
149: Boolean.TRUE);
150:
151: SequenceType seq = rmps.getSequence();
152: if (null != seq) {
153: encodeProperty(seq, Names.WSRM_SEQUENCE_QNAME,
154: SequenceType.class, header, marshaller);
155: }
156: Collection<SequenceAcknowledgement> acks = rmps.getAcks();
157: if (null != acks) {
158: for (SequenceAcknowledgement ack : acks) {
159: encodeProperty(ack, Names.WSRM_SEQUENCE_ACK_QNAME,
160: SequenceAcknowledgement.class, header,
161: marshaller);
162: }
163: }
164: Collection<AckRequestedType> requested = rmps
165: .getAcksRequested();
166: if (null != requested) {
167: for (AckRequestedType ar : requested) {
168: encodeProperty(ar, Names.WSRM_ACK_REQUESTED_QNAME,
169: AckRequestedType.class, header, marshaller);
170: }
171: }
172: } catch (SOAPException se) {
173: LOG
174: .log(Level.WARNING,
175: "SOAP_HEADER_ENCODE_FAILURE_MSG", se);
176: } catch (JAXBException je) {
177: LOG
178: .log(Level.WARNING,
179: "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
180: }
181: }
182:
183: /**
184: * Decode the RM properties from protocol-specific headers.
185: *
186: * @param context the messsage context
187: * @param the decoded MAPs
188: * @exception SOAPFaultException if decoded MAPs are invalid
189: */
190: private void decode(SOAPMessageContext context) {
191: SOAPMessage message = context.getMessage();
192: RMProperties rmps = unmarshalRMProperties(message);
193: RMContextUtils.storeRMProperties(context, rmps, false);
194: }
195:
196: /**
197: * Decode the RM properties from the SOAP message.
198: *
199: * @param message the SOAP message
200: * @return the RM properties
201: */
202: public RMProperties unmarshalRMProperties(SOAPMessage message) {
203: RMProperties rmps = new RMPropertiesImpl();
204:
205: try {
206: Collection<SequenceAcknowledgement> acks = new ArrayList<SequenceAcknowledgement>();
207: Collection<AckRequestedType> requested = new ArrayList<AckRequestedType>();
208:
209: SOAPEnvelope env = message.getSOAPPart().getEnvelope();
210: SOAPHeader header = env.getHeader();
211:
212: if (header != null) {
213: Unmarshaller unmarshaller = getJAXBContext()
214: .createUnmarshaller();
215: Iterator headerElements = header
216: .examineAllHeaderElements();
217: while (headerElements.hasNext()) {
218: SOAPHeaderElement headerElement = (SOAPHeaderElement) headerElements
219: .next();
220: Name headerName = headerElement.getElementName();
221: String localName = headerName.getLocalName();
222: if (Names.WSRM_NAMESPACE_NAME.equals(headerName
223: .getURI())) {
224: LOG.log(Level.INFO, "decoding RM header {0}",
225: localName);
226: if (Names.WSRM_SEQUENCE_NAME.equals(localName)) {
227: SequenceType s = decodeProperty(
228: SequenceType.class, headerElement,
229: unmarshaller);
230:
231: rmps.setSequence(s);
232: } else if (Names.WSRM_SEQUENCE_ACK_NAME
233: .equals(localName)) {
234: SequenceAcknowledgement ack = decodeProperty(
235: SequenceAcknowledgement.class,
236: headerElement, unmarshaller);
237: acks.add(ack);
238: } else if (Names.WSRM_ACK_REQUESTED_NAME
239: .equals(localName)) {
240: AckRequestedType ar = decodeProperty(
241: AckRequestedType.class,
242: headerElement, unmarshaller);
243: requested.add(ar);
244: }
245: }
246: }
247: if (acks.size() > 0) {
248: rmps.setAcks(acks);
249: }
250: if (requested.size() > 0) {
251: rmps.setAcksRequested(requested);
252: }
253: }
254: } catch (SOAPException se) {
255: LOG
256: .log(Level.WARNING,
257: "SOAP_HEADER_DECODE_FAILURE_MSG", se);
258: } catch (JAXBException je) {
259: LOG
260: .log(Level.WARNING,
261: "SOAP_HEADER_DECODE_FAILURE_MSG", je);
262: }
263: return rmps;
264: }
265:
266: /**
267: * @return a JAXBContext
268: */
269: private synchronized JAXBContext getJAXBContext()
270: throws JAXBException {
271: if (jaxbContext == null) {
272: jaxbContext = JAXBContext.newInstance(WS_RM_PACKAGE);
273: }
274: return jaxbContext;
275: }
276:
277: /**
278: * Encodes an RM property as a SOAP header.
279: *
280: * @param value the value to encode
281: * @param qname the QName for the header
282: * @param clz the class
283: * @param header the SOAP header
284: * @param marshaller the JAXB marshaller to use
285: */
286: private <T> void encodeProperty(T value, QName qname, Class<T> clz,
287: SOAPHeader header, Marshaller marshaller)
288: throws JAXBException {
289: if (value != null) {
290: LOG.log(Level.INFO, "encoding " + value
291: + " into RM header {0}", qname);
292: marshaller.marshal(new JAXBElement<T>(qname, clz, value),
293: header);
294: }
295: }
296:
297: /**
298: * Decodes an RM property from a SOAP header.
299: *
300: * @param clz the class
301: * @param headerElement the SOAP header element
302: * @param marshaller the JAXB marshaller to use
303: * @return the decoded EndpointReference
304: */
305: private <T> T decodeProperty(Class<T> clz,
306: SOAPHeaderElement headerElement, Unmarshaller unmarshaller)
307: throws JAXBException {
308: JAXBElement<T> element = unmarshaller.unmarshal(headerElement,
309: clz);
310: return element.getValue();
311: }
312:
313: /**
314: * Discard any pre-existing RM headers - this may occur if the runtime
315: * re-uses a SOAP message.
316: *
317: * @param header the SOAP header
318: */
319: private void discardRMHeaders(SOAPHeader header)
320: throws SOAPException {
321: Iterator headerElements = header.examineAllHeaderElements();
322: while (headerElements.hasNext()) {
323: SOAPHeaderElement headerElement = (SOAPHeaderElement) headerElements
324: .next();
325: Name headerName = headerElement.getElementName();
326: if (Names.WSRM_NAMESPACE_NAME.equals(headerName.getURI())) {
327: headerElement.detachNode();
328: }
329:
330: if (org.objectweb.celtix.bus.ws.addressing.Names.WSA_NAMESPACE_NAME
331: .equals(headerName.getURI())
332: && org.objectweb.celtix.bus.ws.addressing.Names.WSA_ACTION_NAME
333: .equals(headerName.getLocalName())) {
334: headerElement.detachNode();
335: }
336: }
337: }
338:
339: /**
340: * When invoked inbound, check if the action indicates that this is one of the
341: * RM protocol messages (CreateSequence, CreateSequenceResponse, TerminateSequence)
342: * and if so, store method, operation name and data binding callback in the context.
343: * The action has already been extracted from its associated soap header into the
344: * addressing properties as the addressing protocol handler is executed.
345: *
346: * @param context
347: */
348: private void storeBindingInfo(MessageContext context) {
349: assert !ContextUtils.isOutbound(context);
350: AddressingProperties maps = ContextUtils.retrieveMAPs(context,
351: false, false);
352: AttributedURIType actionURI = null == maps ? null : maps
353: .getAction();
354: String action = null == actionURI ? null : actionURI.getValue();
355: DataBindingCallback callback = null;
356: String operationName = null;
357: boolean rmProtocolMessage = true;
358:
359: if (RMUtils.getRMConstants().getCreateSequenceAction().equals(
360: action)) {
361: callback = CreateSequenceRequest
362: .createDataBindingCallback();
363: operationName = CreateSequenceRequest.getOperationName();
364: } else if (RMUtils.getRMConstants()
365: .getCreateSequenceResponseAction().equals(action)) {
366: callback = CreateSequenceResponse
367: .createDataBindingCallback();
368: operationName = CreateSequenceResponse.getOperationName();
369: } else if (RMUtils.getRMConstants()
370: .getTerminateSequenceAction().equals(action)) {
371: callback = TerminateSequenceRequest
372: .createDataBindingCallback();
373: operationName = TerminateSequenceRequest.getOperationName();
374: } else if (RMUtils.getRMConstants().getLastMessageAction()
375: .equals(action)
376: || RMUtils.getRMConstants()
377: .getSequenceAcknowledgmentAction().equals(
378: action)) {
379: // It does not really matter what callback we are using here as the body
380: // in messages with these actions is always empty
381: callback = TerminateSequenceRequest
382: .createDataBindingCallback();
383: operationName = TerminateSequenceRequest.getOperationName();
384: } else {
385: rmProtocolMessage = false;
386: }
387:
388: if (rmProtocolMessage) {
389: BindingContextUtils.storeDispatch(context, false);
390: BindingContextUtils.storeDataBindingCallback(context,
391: callback);
392: context.put(MessageContext.WSDL_OPERATION, new QName("",
393: operationName));
394: context.put(ObjectMessageContext.MESSAGE_INPUT,
395: Boolean.FALSE);
396: }
397: }
398:
399: }
|