001: package org.objectweb.celtix.bus.ws.addressing;
002:
003: import java.io.IOException;
004: import java.text.MessageFormat;
005: import java.util.HashMap;
006: import java.util.Iterator;
007: import java.util.Map;
008: import java.util.ResourceBundle;
009: import java.util.concurrent.atomic.AtomicBoolean;
010: import java.util.logging.Level;
011: import java.util.logging.Logger;
012:
013: import javax.annotation.PostConstruct;
014: import javax.annotation.Resource;
015: import javax.wsdl.Port;
016: import javax.wsdl.extensions.ExtensibilityElement;
017: import javax.xml.ws.handler.LogicalHandler;
018: import javax.xml.ws.handler.LogicalMessageContext;
019: import javax.xml.ws.handler.MessageContext;
020:
021: import org.objectweb.celtix.bindings.AbstractBindingBase;
022: import org.objectweb.celtix.bindings.ClientBinding;
023: import org.objectweb.celtix.bindings.JAXWSConstants;
024: import org.objectweb.celtix.bindings.ServerBinding;
025: import org.objectweb.celtix.bus.jaxws.EndpointImpl;
026: import org.objectweb.celtix.bus.jaxws.ServiceImpl;
027: import org.objectweb.celtix.common.logging.LogUtils;
028: import org.objectweb.celtix.configuration.Configuration;
029: import org.objectweb.celtix.configuration.ConfigurationBuilder;
030: import org.objectweb.celtix.configuration.ConfigurationBuilderFactory;
031: import org.objectweb.celtix.transports.ClientTransport;
032: import org.objectweb.celtix.transports.ServerTransport;
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.wsdl.EndpointReferenceUtils;
037:
038: /**
039: * Logical Handler responsible for aggregating the Message Addressing
040: * Properties for outgoing messages.
041: */
042: public class MAPAggregator implements
043: LogicalHandler<LogicalMessageContext> {
044:
045: public static final String WSA_CONFIGURATION_URI = "http://celtix.objectweb.org/bus/ws/addressing/wsa-config";
046: public static final String WSA_CONFIGURATION_ID = "wsa-handler";
047:
048: private static final Logger LOG = LogUtils
049: .getL7dLogger(MAPAggregator.class);
050: private static final ResourceBundle BUNDLE = LOG
051: .getResourceBundle();
052:
053: protected final Map<String, String> messageIDs = new HashMap<String, String>();
054:
055: /**
056: * resources injected by client/server endpoints
057: */
058: @Resource(name=JAXWSConstants.SERVER_BINDING_PROPERTY)
059: protected ServerBinding serverBinding;
060: @Resource(name=JAXWSConstants.CLIENT_BINDING_PROPERTY)
061: protected ClientBinding clientBinding;
062: @Resource(name=JAXWSConstants.CLIENT_TRANSPORT_PROPERTY)
063: protected ClientTransport clientTransport;
064: @Resource(name=JAXWSConstants.SERVER_TRANSPORT_PROPERTY)
065: protected ServerTransport serverTransport;
066:
067: /**
068: * Whether the endpoint supports WS-Addressing.
069: */
070: private final AtomicBoolean usingAddressingDetermined = new AtomicBoolean(
071: false);
072: private final AtomicBoolean usingAddressing = new AtomicBoolean(
073: false);
074:
075: private Configuration configuration;
076:
077: /**
078: * Constructor.
079: */
080: public MAPAggregator() {
081: }
082:
083: @PostConstruct
084: protected synchronized void initConfiguration() {
085: AbstractBindingBase binding = (AbstractBindingBase) (clientBinding == null ? serverBinding
086: : clientBinding);
087: Configuration busCfg = binding.getBus().getConfiguration();
088: ConfigurationBuilder builder = ConfigurationBuilderFactory
089: .getBuilder();
090: Configuration parent;
091: org.objectweb.celtix.ws.addressing.EndpointReferenceType ref = binding
092: .getEndpointReference();
093:
094: if (null != clientBinding) {
095: String id = EndpointReferenceUtils.getServiceName(ref)
096: .toString()
097: + "/" + EndpointReferenceUtils.getPortName(ref);
098: parent = builder.getConfiguration(
099: ServiceImpl.PORT_CONFIGURATION_URI, id, busCfg);
100: } else {
101: parent = builder.getConfiguration(
102: EndpointImpl.ENDPOINT_CONFIGURATION_URI,
103: EndpointReferenceUtils.getServiceName(ref)
104: .toString(), busCfg);
105: }
106:
107: configuration = builder.getConfiguration(WSA_CONFIGURATION_URI,
108: WSA_CONFIGURATION_ID, parent);
109: if (null == configuration) {
110: configuration = builder
111: .buildConfiguration(WSA_CONFIGURATION_URI,
112: WSA_CONFIGURATION_ID, parent);
113:
114: }
115: }
116:
117: /**
118: * Initialize the handler.
119: */
120: public void init(Map<String, Object> map) {
121: }
122:
123: /**
124: * Invoked for normal processing of inbound and outbound messages.
125: *
126: * @param context the messsage context
127: */
128: public boolean handleMessage(LogicalMessageContext context) {
129: return mediate(context);
130: }
131:
132: /**
133: * Invoked for fault processing.
134: *
135: * @param context the messsage context
136: */
137: public boolean handleFault(LogicalMessageContext context) {
138: return mediate(context);
139: }
140:
141: /**
142: * Called at the conclusion of a message exchange pattern just prior to
143: * the JAX-WS runtime dispatching a message, fault or exception.
144: *
145: * @param context the message context
146: */
147: public void close(MessageContext context) {
148: }
149:
150: /**
151: * Release handler resources.
152: */
153: public void destroy() {
154: }
155:
156: /**
157: * Determine if addressing is being used
158: *
159: * @param context the messsage context
160: * @pre message is outbound
161: */
162: private boolean usingAddressing(LogicalMessageContext context) {
163: boolean ret = false;
164: if (ContextUtils.isRequestor(context)) {
165: if (!usingAddressingDetermined.get()) {
166: Port port = clientTransport == null ? null
167: : clientTransport.getPort();
168: if (port != null) {
169: Iterator<?> portExts = port
170: .getExtensibilityElements().iterator();
171: Iterator<?> bindingExts = port.getBinding()
172: .getExtensibilityElements().iterator();
173: ret = hasUsingAddressing(portExts)
174: || hasUsingAddressing(bindingExts);
175: } else {
176: ret = ContextUtils.retrieveUsingAddressing(context);
177: }
178: setUsingAddressing(ret);
179: } else {
180: ret = usingAddressing.get();
181: }
182: } else {
183: ret = getMAPs(context, false, false) != null;
184: }
185: return ret;
186: }
187:
188: /**
189: * @param extensionElements iterator over extension elements
190: * @return true iff the UsingAddressing element is found
191: */
192: private boolean hasUsingAddressing(Iterator<?> extensionElements) {
193: boolean found = false;
194: while (extensionElements.hasNext() && !found) {
195: ExtensibilityElement ext = (ExtensibilityElement) extensionElements
196: .next();
197: found = Names.WSAW_USING_ADDRESSING_QNAME.equals(ext
198: .getElementType());
199:
200: }
201: return found;
202: }
203:
204: /**
205: * Mediate message flow.
206: *
207: * @param context the messsage context
208: * @return true if processing should continue on dispatch path
209: */
210: private boolean mediate(LogicalMessageContext context) {
211: boolean continueProcessing = true;
212: if (ContextUtils.isOutbound(context)) {
213: if (usingAddressing(context)) {
214: // request/response MAPs must be aggregated
215: aggregate(context);
216: }
217: } else if (!ContextUtils.isRequestor(context)) {
218: // responder validates incoming MAPs
219: AddressingPropertiesImpl maps = getMAPs(context, false,
220: false);
221: setUsingAddressing(true);
222: continueProcessing = validateIncomingMAPs(maps, context);
223: if (continueProcessing) {
224: if (ContextUtils.isOneway(context)
225: || !ContextUtils.isGenericAddress(maps
226: .getReplyTo())) {
227: ContextUtils.rebaseTransport(maps, context,
228: serverBinding, serverTransport);
229: }
230: } else {
231: // validation failure => dispatch is aborted, response MAPs
232: // must be aggregated
233: aggregate(context);
234: }
235: }
236: return continueProcessing;
237: }
238:
239: /**
240: * Perform MAP aggregation.
241: *
242: * @param context the messsage context
243: */
244: private void aggregate(LogicalMessageContext context) {
245: AddressingPropertiesImpl maps = assembleGeneric(context);
246: boolean isRequestor = ContextUtils.isRequestor(context);
247: addRoleSpecific(maps, isRequestor, context);
248: // outbound property always used to store MAPs, as this handler
249: // aggregates only when either:
250: // a) message really is outbound
251: // b) message is currently inbound, but we are about to abort dispatch
252: // due to an incoming MAPs validation failure, so the dispatch
253: // will shortly traverse the outbound path
254: ContextUtils.storeMAPs(maps, context, true, isRequestor, true);
255: }
256:
257: /**
258: * Assemble the generic MAPs (for both requests and responses).
259: *
260: * @param context the messsage context
261: * @return AddressingProperties containing the generic MAPs
262: */
263: private AddressingPropertiesImpl assembleGeneric(
264: MessageContext context) {
265: AddressingPropertiesImpl maps = getMAPs(context, true, true);
266: // MessageID
267: if (maps.getMessageID() == null) {
268: String messageID = ContextUtils.generateUUID();
269: maps.setMessageID(ContextUtils.getAttributedURI(messageID));
270: }
271: // To
272: if (maps.getTo() == null) {
273: // To cached in context by transport
274: EndpointReferenceType reference = clientTransport == null ? null
275: : clientTransport.getTargetEndpoint();
276: maps.setTo(reference != null ? reference.getAddress()
277: : ContextUtils
278: .getAttributedURI(Names.WSA_NONE_ADDRESS));
279: }
280: // Action
281: if (ContextUtils.hasEmptyAction(maps)) {
282: maps.setAction(ContextUtils.getAction(context));
283: }
284: return maps;
285: }
286:
287: /**
288: * Add MAPs which are specific to the requestor or responder role.
289: *
290: * @param maps the MAPs being assembled
291: * @param isRequestor true iff the current messaging role is that of
292: * requestor
293: * @param context the messsage context
294: */
295: private void addRoleSpecific(AddressingPropertiesImpl maps,
296: boolean isRequestor, MessageContext context) {
297: if (isRequestor) {
298: // add request-specific MAPs
299: boolean isOneway = ContextUtils.isOneway(context);
300: // ReplyTo, set if null in MAPs or if set to a generic address
301: // (anonymous or none) that may not be appropriate for the
302: // current invocation
303: EndpointReferenceType replyTo = maps.getReplyTo();
304: if (ContextUtils.isGenericAddress(replyTo)) {
305:
306: try {
307: replyTo = clientTransport == null ? null
308: : clientTransport.getDecoupledEndpoint();
309: } catch (IOException ex) {
310: // ignore
311: replyTo = null;
312: }
313:
314: if (replyTo == null || isOneway) {
315: AttributedURIType address = ContextUtils
316: .getAttributedURI(isOneway ? Names.WSA_NONE_ADDRESS
317: : Names.WSA_ANONYMOUS_ADDRESS);
318: replyTo = ContextUtils.WSA_OBJECT_FACTORY
319: .createEndpointReferenceType();
320: replyTo.setAddress(address);
321: }
322: maps.setReplyTo(replyTo);
323: }
324: if (!isOneway) {
325: // REVISIT FaultTo if cached by transport in context
326: }
327: // cache correlation ID
328: if (ContextUtils.isOutbound(context)) {
329: ContextUtils.storeCorrelationID(maps.getMessageID(),
330: true, context);
331: }
332: } else {
333: // add response-specific MAPs
334: AddressingPropertiesImpl inMAPs = getMAPs(context, false,
335: false);
336: maps.exposeAs(inMAPs.getNamespaceURI());
337: // To taken from ReplyTo in incoming MAPs
338: if (inMAPs.getReplyTo() != null) {
339: maps.setTo(inMAPs.getReplyTo().getAddress());
340: }
341: // RelatesTo taken from MessageID in incoming MAPs
342: if (inMAPs.getMessageID() != null) {
343: String inMessageID = inMAPs.getMessageID().getValue();
344: maps.setRelatesTo(ContextUtils
345: .getRelatesTo(inMessageID));
346: }
347: }
348: }
349:
350: /**
351: * Get the starting point MAPs (either empty or those set explicitly
352: * by the application on the binding provider request context).
353: *
354: * @param context the messsage context
355: * @param isProviderContext true if the binding provider request context
356: * available to the client application as opposed to the message context
357: * visible to handlers
358: * @param isOutbound true iff the message is outbound
359: * @return AddressingProperties retrieved MAPs
360: */
361: private AddressingPropertiesImpl getMAPs(MessageContext context,
362: boolean isProviderContext, boolean isOutbound) {
363:
364: AddressingPropertiesImpl maps = null;
365: maps = ContextUtils.retrieveMAPs(context, isProviderContext,
366: isOutbound);
367: LOG.log(Level.INFO, "MAPs retrieved from context {0}", maps);
368:
369: if (maps == null && isProviderContext) {
370: maps = new AddressingPropertiesImpl();
371: }
372: return maps;
373: }
374:
375: /**
376: * Validate incoming MAPs
377: * @param maps the incoming MAPs
378: * @param context the messsage context
379: * @return true if incoming MAPs are valid
380: * @pre inbound message, not requestor
381: */
382: private boolean validateIncomingMAPs(AddressingProperties maps,
383: MessageContext context) {
384: if (null != configuration
385: && configuration.getBoolean("allowDuplicates")) {
386: return true;
387: }
388: boolean valid = true;
389: if (maps != null) {
390: AttributedURIType messageID = maps.getMessageID();
391: if (messageID != null
392: && messageIDs.put(messageID.getValue(), messageID
393: .getValue()) != null) {
394: LOG.log(Level.WARNING, "DUPLICATE_MESSAGE_ID_MSG",
395: messageID.getValue());
396: String reason = BUNDLE
397: .getString("DUPLICATE_MESSAGE_ID_MSG");
398: String l7dReason = MessageFormat.format(reason,
399: messageID.getValue());
400: ContextUtils.storeMAPFaultName(
401: Names.DUPLICATE_MESSAGE_ID_NAME, context);
402: ContextUtils.storeMAPFaultReason(l7dReason, context);
403: valid = false;
404: }
405: }
406: return valid;
407: }
408:
409: /**
410: * Set using addressing flag.
411: *
412: * @param using true if addressing in use.
413: */
414: private void setUsingAddressing(boolean using) {
415: usingAddressing.set(using);
416: usingAddressingDetermined.set(true);
417: }
418: }
|