001: package org.objectweb.celtix.bus.ws.addressing;
002:
003: import java.lang.reflect.Method;
004: import java.util.UUID;
005: import java.util.logging.Level;
006: import java.util.logging.Logger;
007:
008: import javax.jws.WebMethod;
009: import javax.jws.WebService;
010: import javax.xml.bind.JAXBContext;
011: import javax.xml.bind.JAXBException;
012: import javax.xml.ws.RequestWrapper;
013: import javax.xml.ws.ResponseWrapper;
014: import javax.xml.ws.WebFault;
015: import javax.xml.ws.handler.MessageContext;
016: import static javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY;
017:
018: import org.objectweb.celtix.bindings.BindingContextUtils;
019: import org.objectweb.celtix.bindings.DataBindingCallback;
020: import org.objectweb.celtix.bindings.ServerBinding;
021: import org.objectweb.celtix.bus.jaxws.JAXBDataBindingCallback;
022: import org.objectweb.celtix.common.logging.LogUtils;
023: import org.objectweb.celtix.context.ObjectMessageContext;
024: import org.objectweb.celtix.context.OutputStreamMessageContext;
025: import org.objectweb.celtix.transports.ServerTransport;
026: import org.objectweb.celtix.ws.addressing.AddressingProperties;
027: import org.objectweb.celtix.ws.addressing.AttributedURIType;
028: import org.objectweb.celtix.ws.addressing.EndpointReferenceType;
029: import org.objectweb.celtix.ws.addressing.ObjectFactory;
030: import org.objectweb.celtix.ws.addressing.RelatesToType;
031:
032: import static org.objectweb.celtix.context.ObjectMessageContext.CORRELATION_IN;
033: import static org.objectweb.celtix.context.ObjectMessageContext.CORRELATION_OUT;
034: import static org.objectweb.celtix.context.ObjectMessageContext.REQUESTOR_ROLE_PROPERTY;
035: import static org.objectweb.celtix.context.OutputStreamMessageContext.ONEWAY_MESSAGE_TF;
036: import static org.objectweb.celtix.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES;
037: import static org.objectweb.celtix.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES_INBOUND;
038: import static org.objectweb.celtix.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES_OUTBOUND;
039: import static org.objectweb.celtix.ws.addressing.JAXWSAConstants.SERVER_ADDRESSING_PROPERTIES_INBOUND;
040: import static org.objectweb.celtix.ws.addressing.JAXWSAConstants.SERVER_ADDRESSING_PROPERTIES_OUTBOUND;
041:
042: /**
043: * Holder for utility methods relating to contexts.
044: */
045: public final class ContextUtils {
046:
047: public static final ObjectFactory WSA_OBJECT_FACTORY = new ObjectFactory();
048:
049: private static final String WS_ADDRESSING_PACKAGE = EndpointReferenceType.class
050: .getPackage().getName();
051: private static final Logger LOG = LogUtils
052: .getL7dLogger(ContextUtils.class);
053:
054: private static final String TO_PROPERTY = "org.objectweb.celtix.ws.addressing.to";
055: private static final String REPLYTO_PROPERTY = "org.objectweb.celtix.ws.addressing.replyto";
056: private static final String USING_PROPERTY = "org.objectweb.celtix.ws.addressing.using";
057:
058: /**
059: * Used to fabricate a Uniform Resource Name from a UUID string
060: */
061: private static final String URN_UUID = "urn:uuid:";
062:
063: private static JAXBContext jaxbContext;
064:
065: /**
066: * Used by MAPAggregator to cache bad MAP fault name
067: */
068: private static final String MAP_FAULT_NAME_PROPERTY = "org.objectweb.celtix.ws.addressing.map.fault.name";
069:
070: /**
071: * Used by MAPAggregator to cache bad MAP fault reason
072: */
073: private static final String MAP_FAULT_REASON_PROPERTY = "org.objectweb.celtix.ws.addressing.map.fault.reason";
074:
075: /**
076: * Prevents instantiation.
077: */
078: private ContextUtils() {
079: }
080:
081: /**
082: * Determine if context indicates message is outbound.
083: *
084: * @param context the current MessageContext
085: * @return true iff the message direction is outbound
086: */
087: public static boolean isOutbound(MessageContext context) {
088: Boolean outbound = (Boolean) context
089: .get(MESSAGE_OUTBOUND_PROPERTY);
090: return outbound != null && outbound.booleanValue();
091: }
092:
093: /**
094: * Determine if context indicates current messaging role is that of
095: * requestor.
096: *
097: * @param context the current MessageContext
098: * @return true iff the current messaging role is that of requestor
099: */
100: public static boolean isRequestor(MessageContext context) {
101: Boolean requestor = (Boolean) context
102: .get(REQUESTOR_ROLE_PROPERTY);
103: return requestor != null && requestor.booleanValue();
104: }
105:
106: /**
107: * Determine if context indicates current invocation is oneway.
108: *
109: * @param context the current MessageContext
110: * @return true iff the current invocation is oneway
111: */
112: public static boolean isOneway(MessageContext context) {
113: Boolean oneway = (Boolean) context.get(ONEWAY_MESSAGE_TF);
114: return oneway != null && oneway.booleanValue();
115: }
116:
117: /**
118: * Get appropriate context property name for message addressing properties.
119: *
120: * @param isProviderContext true if the binding provider request context
121: * available to the client application as opposed to the message context
122: * visible to handlers
123: * @param isRequestor true iff the current messaging role is that of
124: * requestor
125: * @param isOutbound true iff the message is outbound
126: * @return the property name to use when caching the MAPs in the context
127: */
128: public static String getMAPProperty(boolean isRequestor,
129: boolean isProviderContext, boolean isOutbound) {
130: return isRequestor ? isProviderContext ? CLIENT_ADDRESSING_PROPERTIES
131: : isOutbound ? CLIENT_ADDRESSING_PROPERTIES_OUTBOUND
132: : CLIENT_ADDRESSING_PROPERTIES_INBOUND
133: : isOutbound ? SERVER_ADDRESSING_PROPERTIES_OUTBOUND
134: : SERVER_ADDRESSING_PROPERTIES_INBOUND;
135: }
136:
137: /**
138: * Get appropriate context property name for correlation ID.
139: *
140: * @param isOutbound true iff the message is outbound
141: * @return the property name to use when caching the
142: * correlation ID in the context
143: */
144: public static String getCorrelationIDProperty(boolean isOutbound) {
145: return isOutbound ? CORRELATION_OUT : CORRELATION_IN;
146: }
147:
148: /**
149: * Store MAPs in the context.
150: *
151: * @param context the message context
152: * @param isOutbound true iff the message is outbound
153: */
154: public static void storeMAPs(AddressingProperties maps,
155: MessageContext context, boolean isOutbound) {
156: storeMAPs(maps, context, isOutbound, isRequestor(context),
157: true, false);
158: }
159:
160: /**
161: * Store MAPs in the context.
162: *
163: * @param maps the MAPs to store
164: * @param context the message context
165: * @param isOutbound true iff the message is outbound
166: * @param isRequestor true iff the current messaging role is that of
167: * requestor
168: * @param handler true if HANDLER scope, APPLICATION scope otherwise
169: */
170: public static void storeMAPs(AddressingProperties maps,
171: MessageContext context, boolean isOutbound,
172: boolean isRequestor, boolean handler) {
173: storeMAPs(maps, context, isOutbound, isRequestor, handler,
174: false);
175: }
176:
177: /**
178: * Store MAPs in the context.
179: *
180: * @param maps the MAPs to store
181: * @param context the message context
182: * @param isOutbound true iff the message is outbound
183: * @param isRequestor true iff the current messaging role is that of
184: * requestor
185: * @param handler true if HANDLER scope, APPLICATION scope otherwise
186: * @param isProviderContext true if the binding provider request context
187: */
188: public static void storeMAPs(AddressingProperties maps,
189: MessageContext context, boolean isOutbound,
190: boolean isRequestor, boolean handler,
191: boolean isProviderContext) {
192: if (maps != null) {
193: String mapProperty = getMAPProperty(isRequestor,
194: isProviderContext, isOutbound);
195: LOG.log(Level.INFO,
196: "associating MAPs with context property {0}",
197: mapProperty);
198: context.put(mapProperty, maps);
199: context.setScope(mapProperty,
200: handler ? MessageContext.Scope.HANDLER
201: : MessageContext.Scope.APPLICATION);
202: }
203: }
204:
205: /**
206: * @param context the message context
207: * @param isProviderContext true if the binding provider request context
208: * available to the client application as opposed to the message context
209: * visible to handlers
210: * @param isOutbound true iff the message is outbound
211: * @return the current addressing properties
212: */
213: public static AddressingPropertiesImpl retrieveMAPs(
214: MessageContext context, boolean isProviderContext,
215: boolean isOutbound) {
216: boolean isRequestor = ContextUtils.isRequestor(context);
217: String mapProperty = ContextUtils.getMAPProperty(
218: isProviderContext, isRequestor, isOutbound);
219: LOG.log(Level.INFO,
220: "retrieving MAPs from context property {0}",
221: mapProperty);
222: AddressingPropertiesImpl maps = (AddressingPropertiesImpl) context
223: .get(mapProperty);
224: if (maps != null) {
225: LOG.log(Level.INFO, "current MAPs {0}", maps);
226: } else if (!isProviderContext) {
227: LOG.warning("MAPS_RETRIEVAL_FAILURE_MSG");
228: }
229: return maps;
230: }
231:
232: /**
233: * Helper method to get an attributed URI.
234: *
235: * @param uri the URI
236: * @return an AttributedURIType encapsulating the URI
237: */
238: public static AttributedURIType getAttributedURI(String uri) {
239: AttributedURIType attributedURI = WSA_OBJECT_FACTORY
240: .createAttributedURIType();
241: attributedURI.setValue(uri);
242: return attributedURI;
243: }
244:
245: /**
246: * Helper method to get a RealtesTo instance.
247: *
248: * @param uri the related URI
249: * @return a RelatesToType encapsulating the URI
250: */
251: public static RelatesToType getRelatesTo(String uri) {
252: RelatesToType relatesTo = WSA_OBJECT_FACTORY
253: .createRelatesToType();
254: relatesTo.setValue(uri);
255: return relatesTo;
256: }
257:
258: /**
259: * Helper method to determine if an EPR address is generic (either null,
260: * none or anonymous).
261: *
262: * @param ref the EPR under test
263: * @return true iff the address is generic
264: */
265: public static boolean isGenericAddress(EndpointReferenceType ref) {
266: return ref == null
267: || ref.getAddress() == null
268: || Names.WSA_ANONYMOUS_ADDRESS.equals(ref.getAddress()
269: .getValue())
270: || Names.WSA_NONE_ADDRESS.equals(ref.getAddress()
271: .getValue());
272: }
273:
274: /**
275: * Helper method to determine if an MAPs Action is empty (a null action
276: * is considered empty, whereas a zero length action suppresses
277: * the propogation of the Action property).
278: *
279: * @param ref the MAPs Action under test
280: * @return true iff the Action is empty
281: */
282: public static boolean hasEmptyAction(AddressingProperties maps) {
283: boolean empty = maps.getAction() == null;
284: if (maps.getAction() != null
285: && maps.getAction().getValue().length() == 0) {
286: maps.setAction(null);
287: empty = false;
288: }
289: return empty;
290: }
291:
292: /**
293: * Rebase server transport on replyTo
294: *
295: * @param inMAPs the incoming MAPs
296: * @param context the message context
297: */
298: public static void rebaseTransport(AddressingProperties inMAPs,
299: MessageContext context, ServerBinding serverBinding,
300: ServerTransport serverTransport) {
301: // ensure there is a MAPs instance available for the outbound
302: // partial response that contains appropriate To and ReplyTo
303: // properties (i.e. anonymous & none respectively)
304: AddressingPropertiesImpl maps = new AddressingPropertiesImpl();
305: maps.setTo(ContextUtils
306: .getAttributedURI(Names.WSA_ANONYMOUS_ADDRESS));
307: maps.setReplyTo(WSA_OBJECT_FACTORY
308: .createEndpointReferenceType());
309: maps.getReplyTo().setAddress(
310: getAttributedURI(Names.WSA_NONE_ADDRESS));
311: maps.setAction(getAttributedURI(""));
312: maps.exposeAs(inMAPs.getNamespaceURI());
313: storeMAPs(maps, context, true, true, true, true);
314:
315: if (serverTransport != null && serverBinding != null) {
316: try {
317: OutputStreamMessageContext outputContext = serverTransport
318: .rebase(context, inMAPs.getReplyTo());
319: if (outputContext != null) {
320: serverBinding.partialResponse(outputContext,
321: getDataBindingCallback());
322: }
323: BindingContextUtils.storeDecoupledResponse(context,
324: true);
325: } catch (Exception e) {
326: LOG.log(Level.WARNING,
327: "SERVER_TRANSPORT_REBASE_FAILURE_MSG", e);
328: }
329: }
330: }
331:
332: /**
333: * Store UsingAddressing override flag in the context
334: *
335: * @param override true if UsingAddressing should be overridden
336: * @param context the message context
337: */
338: public static void storeUsingAddressing(boolean override,
339: MessageContext context) {
340: context.put(USING_PROPERTY, Boolean.valueOf(override));
341: context.setScope(USING_PROPERTY,
342: MessageContext.Scope.APPLICATION);
343: }
344:
345: /**
346: * Retrieve UsingAddressing override flag from the context
347: *
348: * @param override true if UsingAddressing should be overridden
349: * @param context the message context
350: */
351: public static boolean retrieveUsingAddressing(MessageContext context) {
352: Boolean override = (Boolean) context.get(USING_PROPERTY);
353: return override != null && override.booleanValue();
354: }
355:
356: /**
357: * Store To EPR in the context
358: *
359: * @param to the To EPR
360: * @param context the message context
361: */
362: public static void storeTo(EndpointReferenceType to,
363: MessageContext context) {
364: context.put(TO_PROPERTY, to);
365: context.setScope(TO_PROPERTY, MessageContext.Scope.APPLICATION);
366: }
367:
368: /**
369: * Retrieve To EPR from the context.
370: *
371: * @param context the message context
372: * @returned the retrieved EPR
373: */
374: public static EndpointReferenceType retrieveTo(
375: MessageContext context) {
376: /*
377: // required?
378: ClientTransport transport = BindingContextUtils.retrieveClientTransport(context);
379: EndpointReferenceType to = null;
380: if (transport != null) {
381: to = transport.getTargetEndpoint();
382: } else {
383: to = (EndpointReferenceType)context.get(TO_PROPERTY);
384: }
385: return to;
386: */
387: return (EndpointReferenceType) context.get(TO_PROPERTY);
388: }
389:
390: /**
391: * Store ReplyTo EPR in the context
392: *
393: * @param replyTo the ReplyTo EPR
394: * @param context the message context
395: */
396: public static void storeReplyTo(EndpointReferenceType replyTo,
397: MessageContext context) {
398: context.put(REPLYTO_PROPERTY, replyTo);
399: context.setScope(REPLYTO_PROPERTY,
400: MessageContext.Scope.APPLICATION);
401: }
402:
403: /**
404: * Retrieve ReplyTo EPR from the context.
405: *
406: * @param context the message context
407: * @returned the retrieved EPR
408: */
409: public static EndpointReferenceType retrieveReplyTo(
410: MessageContext context) {
411: /*
412: // required?
413: ClientTransport transport = BindingContextUtils.retrieveClientTransport(context);
414: EndpointReferenceType replyTo = null;
415: if (transport != null) {
416: try {
417: replyTo = transport.getDecoupledEndpoint();
418: } catch (IOException ioe) {
419: // ignore
420: }
421: } else {
422: replyTo = (EndpointReferenceType)context.get(REPLYTO_PROPERTY);
423: }
424: return replyTo;
425: */
426: return (EndpointReferenceType) context.get(REPLYTO_PROPERTY);
427: }
428:
429: /**
430: * Store bad MAP fault name in the context.
431: *
432: * @param faultName the fault name to store
433: * @param context the message context
434: */
435: public static void storeMAPFaultName(String faultName,
436: MessageContext context) {
437: context.put(MAP_FAULT_NAME_PROPERTY, faultName);
438: context.setScope(MAP_FAULT_NAME_PROPERTY,
439: MessageContext.Scope.HANDLER);
440: }
441:
442: /**
443: * Retrieve MAP fault name from the context.
444: *
445: * @param context the message context
446: * @returned the retrieved fault name
447: */
448: public static String retrieveMAPFaultName(MessageContext context) {
449: return (String) context.get(MAP_FAULT_NAME_PROPERTY);
450: }
451:
452: /**
453: * Store MAP fault reason in the context.
454: *
455: * @param reason the fault reason to store
456: * @param context the message context
457: */
458: public static void storeMAPFaultReason(String reason,
459: MessageContext context) {
460: context.put(MAP_FAULT_REASON_PROPERTY, reason);
461: context.setScope(MAP_FAULT_REASON_PROPERTY,
462: MessageContext.Scope.HANDLER);
463: }
464:
465: /**
466: * Retrieve MAP fault reason from the context.
467: *
468: * @param context the message context
469: * @returned the retrieved fault reason
470: */
471: public static String retrieveMAPFaultReason(MessageContext context) {
472: return (String) context.get(MAP_FAULT_REASON_PROPERTY);
473: }
474:
475: /**
476: * Store correlation ID in the context
477: *
478: * @param id the correlation ID
479: * @param isOutbound true if message is outbound
480: * @param context the message context
481: */
482: public static void storeCorrelationID(RelatesToType id,
483: boolean isOutbound, MessageContext context) {
484: storeCorrelationID(id.getValue(), isOutbound, context);
485: }
486:
487: /**
488: * Store correlation ID in the context
489: *
490: * @param id the correlation ID
491: * @param isOutbound true if message is outbound
492: * @param context the message context
493: */
494: public static void storeCorrelationID(AttributedURIType id,
495: boolean isOutbound, MessageContext context) {
496: storeCorrelationID(id.getValue(), isOutbound, context);
497: }
498:
499: /**
500: * Store correlation ID in the context
501: *
502: * @param id the correlation ID
503: * @param isOutbound true if message is outbound
504: * @param context the message context
505: */
506: protected static void storeCorrelationID(String id,
507: boolean isOutbound, MessageContext context) {
508: context.put(getCorrelationIDProperty(isOutbound), id);
509: context.setScope(getCorrelationIDProperty(isOutbound),
510: MessageContext.Scope.APPLICATION);
511: }
512:
513: /**
514: * Retrieve correlation ID from the context.
515: *
516: * @param context the message context
517: * @param isOutbound true if message is outbound
518: * @returned the retrieved correlation ID
519: */
520: public static String retrieveCorrelationID(MessageContext context,
521: boolean isOutbound) {
522: return (String) context
523: .get(getCorrelationIDProperty(isOutbound));
524: }
525:
526: /**
527: * Retrieve a JAXBContext for marshalling and unmarshalling JAXB generated
528: * types.
529: *
530: * @return a JAXBContext
531: */
532: public static JAXBContext getJAXBContext() throws JAXBException {
533: synchronized (ContextUtils.class) {
534: if (jaxbContext == null) {
535: jaxbContext = JAXBContext
536: .newInstance(WS_ADDRESSING_PACKAGE);
537: }
538: }
539: return jaxbContext;
540: }
541:
542: /**
543: * Set the encapsulated JAXBContext (used by unit tests).
544: *
545: * @param ctx JAXBContext
546: */
547: public static void setJAXBContext(JAXBContext ctx)
548: throws JAXBException {
549: synchronized (ContextUtils.class) {
550: jaxbContext = ctx;
551: }
552: }
553:
554: /**
555: * @return a generated UUID
556: */
557: public static String generateUUID() {
558: return URN_UUID + UUID.randomUUID();
559: }
560:
561: /**
562: * Construct the Action URI.
563: *
564: * @param context the message context
565: * @return the Action URI
566: */
567: public static AttributedURIType getAction(MessageContext context) {
568: String action = null;
569: // REVISIT: add support for @{Fault}Action annotation (generated
570: // from the wsaw:Action WSDL element)
571: LOG.fine("Determining action");
572: Throwable fault = (Throwable) context
573: .get(ObjectMessageContext.METHOD_FAULT);
574: Method method = (Method) context
575: .get(ObjectMessageContext.METHOD_OBJ);
576: LOG.fine("method: " + method + ", fault: " + fault);
577: if (method != null) {
578: if (fault != null) {
579: WebFault webFault = fault.getClass().getAnnotation(
580: WebFault.class);
581: action = getAction(webFault.targetNamespace(), method,
582: webFault.name(), true);
583: } else {
584: if (ContextUtils.isRequestor(context)) {
585: RequestWrapper requestWrapper = method
586: .getAnnotation(RequestWrapper.class);
587: if (requestWrapper != null) {
588: action = getAction(requestWrapper
589: .targetNamespace(), method,
590: requestWrapper.localName(), false);
591: } else {
592: //TODO: What if the WSDL is RPC-Literal encoded.
593: // We need to get action out of available annotations?
594: //
595:
596: WebService wsAnnotation = method
597: .getDeclaringClass().getAnnotation(
598: WebService.class);
599: WebMethod wmAnnotation = method
600: .getAnnotation(WebMethod.class);
601:
602: action = getAction(wsAnnotation
603: .targetNamespace(), method,
604: wmAnnotation.operationName(), false);
605: }
606:
607: } else {
608: ResponseWrapper responseWrapper = method
609: .getAnnotation(ResponseWrapper.class);
610: if (responseWrapper != null) {
611: action = getAction(responseWrapper
612: .targetNamespace(), method,
613: responseWrapper.localName(), false);
614: } else {
615: //RPC-Literal case.
616: WebService wsAnnotation = method
617: .getDeclaringClass().getAnnotation(
618: WebService.class);
619: WebMethod wmAnnotation = method
620: .getAnnotation(WebMethod.class);
621:
622: action = getAction(wsAnnotation
623: .targetNamespace(), method,
624: wmAnnotation.operationName(), false);
625: }
626: }
627: }
628: }
629: return action != null ? getAttributedURI(action) : null;
630: }
631:
632: /**
633: * Construct the Action string.
634: *
635: * @param targetNamespace the target namespace
636: * @param method the method
637: * @param localName the local name
638: * @param isFault true if a fault
639: * @return action string
640: */
641: private static String getAction(String targetNamespace,
642: Method method, String localName, boolean isFault) {
643: String action = null;
644: action = targetNamespace;
645: action += Names.WSA_ACTION_DELIMITER;
646: action += method.getDeclaringClass().getSimpleName();
647: if (isFault) {
648: action += method.getName();
649: action += Names.WSA_FAULT_DELIMITER;
650: }
651: action += Names.WSA_ACTION_DELIMITER;
652: action += localName;
653: return action;
654: }
655:
656: /**
657: * Get a DataBindingCallback (for use with an outgoing partial response).
658: *
659: * @return a DataBindingCallback
660: */
661: private static DataBindingCallback getDataBindingCallback()
662: throws JAXBException {
663: return new JAXBDataBindingCallback(null,
664: DataBindingCallback.Mode.PARTS, getJAXBContext());
665: }
666: }
|