001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.jaxws.server.dispatcher;
020:
021: import org.apache.axis2.jaxws.ExceptionFactory;
022: import org.apache.axis2.jaxws.core.MessageContext;
023: import org.apache.axis2.jaxws.core.util.MessageContextUtils;
024: import org.apache.axis2.jaxws.description.EndpointDescription;
025: import org.apache.axis2.jaxws.i18n.Messages;
026: import org.apache.axis2.jaxws.marshaller.impl.alt.MethodMarshallerUtils;
027: import org.apache.axis2.jaxws.message.Block;
028: import org.apache.axis2.jaxws.message.Message;
029: import org.apache.axis2.jaxws.message.Protocol;
030: import org.apache.axis2.jaxws.message.XMLFault;
031: import org.apache.axis2.jaxws.message.factory.BlockFactory;
032: import org.apache.axis2.jaxws.message.factory.MessageFactory;
033: import org.apache.axis2.jaxws.message.factory.SOAPEnvelopeBlockFactory;
034: import org.apache.axis2.jaxws.message.factory.SourceBlockFactory;
035: import org.apache.axis2.jaxws.message.factory.XMLStringBlockFactory;
036: import org.apache.axis2.jaxws.registry.FactoryRegistry;
037: import org.apache.axis2.jaxws.server.EndpointController;
038: import org.apache.axis2.jaxws.utility.ClassUtils;
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041:
042: import javax.activation.DataSource;
043: import javax.xml.bind.JAXBContext;
044: import javax.xml.soap.SOAPMessage;
045: import javax.xml.transform.Source;
046: import javax.xml.ws.Provider;
047: import javax.xml.ws.Service;
048: import javax.xml.ws.soap.SOAPBinding;
049: import java.lang.reflect.ParameterizedType;
050: import java.lang.reflect.Type;
051: import java.security.PrivilegedExceptionAction;
052:
053: /**
054: * The ProviderDispatcher is used to invoke instances of a target endpoint that implement the {@link
055: * javax.xml.ws.Provider} interface.
056: * <p/>
057: * The Provider<T> is a generic class, with certain restrictions on the parameterized type T. This
058: * implementation supports the following types:
059: * <p/>
060: * java.lang.String javax.activation.DataSource javax.xml.soap.SOAPMessage
061: * javax.xml.transform.Source
062: */
063: public class ProviderDispatcher extends JavaDispatcher {
064:
065: private static Log log = LogFactory
066: .getLog(ProviderDispatcher.class);
067:
068: private BlockFactory blockFactory = null;
069: private Class providerType = null;
070: private Provider providerInstance = null;
071: private Service.Mode providerServiceMode = null;
072: private Message message = null;
073: private Protocol messageProtocol;
074:
075: /**
076: * Constructor
077: *
078: * @param _class
079: * @param serviceInstance
080: */
081: public ProviderDispatcher(Class _class, Object serviceInstance) {
082: super (_class, serviceInstance);
083: }
084:
085: /* (non-Javadoc)
086: * @see org.apache.axis2.jaxws.server.EndpointDispatcher#execute()
087: */
088: public MessageContext invoke(final MessageContext mc)
089: throws Exception {
090: if (log.isDebugEnabled()) {
091: log
092: .debug("Preparing to invoke javax.xml.ws.Provider based endpoint");
093: }
094:
095: providerInstance = getProviderInstance();
096:
097: // First we need to know what kind of Provider instance we're going
098: // to be invoking against
099: providerType = getProviderType();
100:
101: // REVIEW: This assumes there is only one endpoint description on the service. Is that always the case?
102: EndpointDescription endpointDesc = mc.getEndpointDescription();
103:
104: // Now that we know what kind of Provider we have, we can create the
105: // right type of Block for the request parameter data
106: Object requestParamValue = null;
107: Message message = mc.getMessage();
108: if (message != null) {
109:
110: // Enable MTOM if indicated by the binding
111: String bindingType = endpointDesc.getBindingType();
112: if (bindingType != null) {
113: if (bindingType
114: .equals(SOAPBinding.SOAP11HTTP_MTOM_BINDING)
115: || bindingType
116: .equals(SOAPBinding.SOAP12HTTP_MTOM_BINDING)) {
117: message.setMTOMEnabled(true);
118: }
119: }
120:
121: // Save off the protocol info so we can use it when creating the response message.
122: messageProtocol = message.getProtocol();
123: // Determine what type blocks we want to create (String, Source, etc) based on Provider Type
124: BlockFactory factory = createBlockFactory(providerType);
125:
126: providerServiceMode = endpointDesc.getServiceMode();
127:
128: if (providerServiceMode != null
129: && providerServiceMode == Service.Mode.MESSAGE) {
130: if (providerType.equals(SOAPMessage.class)) {
131: // We can get the SOAPMessage directly from the message itself
132: if (log.isDebugEnabled()) {
133: log.debug("Provider Type is SOAPMessage.");
134: log.debug("Number Message attachments="
135: + message.getAttachmentIDs().size());
136: }
137: }
138:
139: requestParamValue = message.getValue(null, factory);
140: if (requestParamValue == null) {
141: if (log.isDebugEnabled()) {
142: log
143: .debug("There are no elements to unmarshal. ProviderDispatch will pass a null as input");
144: }
145: }
146: } else {
147: // If it is not MESSAGE, then it is PAYLOAD (which is the default); only work with the body
148: Block block = message.getBodyBlock(null, factory);
149: if (block != null) {
150: requestParamValue = block.getBusinessObject(true);
151: } else {
152: if (log.isDebugEnabled()) {
153: log
154: .debug("No body blocks in SOAPMessage, Calling provider method with null input parameters");
155: }
156: requestParamValue = null;
157: }
158: }
159: }
160:
161: if (log.isDebugEnabled())
162: log.debug("Provider Type = " + providerType
163: + "; parameter type = " + requestParamValue);
164:
165: final Object input = providerType.cast(requestParamValue);
166: if (input != null && log.isDebugEnabled()) {
167: log.debug("Invoking Provider<" + providerType.getName()
168: + "> with " + "parameter of type "
169: + input.getClass().getName());
170: }
171: if (input == null && log.isDebugEnabled()) {
172: log.debug("Invoking Provider<" + providerType.getName()
173: + "> with " + "NULL input parameter");
174: }
175:
176: // Invoke the actual Provider.invoke() method
177: boolean faultThrown = false;
178: XMLFault fault = null;
179: Object responseParamValue = null;
180: Throwable t = null;
181: try {
182: responseParamValue = (Object) org.apache.axis2.java.security.AccessController
183: .doPrivileged(new PrivilegedExceptionAction() {
184: public Object run() throws Exception {
185: return invokeProvider(mc, providerInstance,
186: input);
187: }
188: });
189: } catch (Exception e) {
190: t = ClassUtils.getRootCause(e);
191: faultThrown = true;
192: fault = MethodMarshallerUtils
193: .createXMLFaultFromSystemException(t);
194:
195: if (log.isDebugEnabled()) {
196: log.debug("Marshal Throwable ="
197: + e.getClass().getName());
198: log.debug(" rootCause =" + t.getClass().getName());
199: log.debug(" exception=" + t.toString());
200: }
201: }
202:
203: // Create the response MessageContext
204: MessageContext responseMsgCtx = null;
205: if (!EndpointController.isOneWay(mc.getAxisMessageContext())) {
206: if (faultThrown) {
207: // If a fault was thrown, we need to create a slightly different
208: // MessageContext, than in the response path.
209: Message responseMsg = createMessageFromValue(fault);
210: responseMsgCtx = MessageContextUtils
211: .createFaultMessageContext(mc);
212: responseMsgCtx.setMessage(responseMsg);
213: } else {
214: Message responseMsg = createMessageFromValue(responseParamValue);
215:
216: // Enable MTOM if indicated by the binding
217: String bindingType = endpointDesc.getBindingType();
218: if (bindingType != null) {
219: if (bindingType
220: .equals(SOAPBinding.SOAP11HTTP_MTOM_BINDING)
221: || bindingType
222: .equals(SOAPBinding.SOAP12HTTP_MTOM_BINDING)) {
223: responseMsg.setMTOMEnabled(true);
224: }
225: }
226:
227: responseMsgCtx = MessageContextUtils
228: .createResponseMessageContext(mc);
229: responseMsgCtx.setMessage(responseMsg);
230: }
231: } else {
232: // If we have a one-way operation, then we cannot create a MessageContext for the response.
233: return null;
234: }
235:
236: return responseMsgCtx;
237: }
238:
239: protected Object invokeProvider(MessageContext ctx,
240: Provider provider, Object input) throws Exception {
241: return provider.invoke(input);
242: }
243:
244: /**
245: * Get the endpoint provider instance
246: *
247: * @return Provider
248: * @throws Exception
249: */
250: public Provider getProvider() throws Exception {
251: Provider p = getProviderInstance();
252: setProvider(p);
253: return p;
254: }
255:
256: /**
257: * Set the endpoint provider instance
258: *
259: * @param _provider
260: */
261: public void setProvider(Provider _provider) {
262: this .providerInstance = _provider;
263: }
264:
265: /**
266: * Get the parameter for a given endpoint invocation
267: *
268: * @return
269: * @throws Exception
270: */
271: public Message getMessage() throws Exception {
272: return message;
273: }
274:
275: /**
276: * Set the parameter for a given endpoint invocation
277: *
278: * @param msg
279: */
280: public void setMessage(Message msg) {
281: this .message = msg;
282: }
283:
284: /*
285: * Create a Message object out of the value object that was returned.
286: */
287: private Message createMessageFromValue(Object value)
288: throws Exception {
289: MessageFactory msgFactory = (MessageFactory) FactoryRegistry
290: .getFactory(MessageFactory.class);
291: Message message = null;
292:
293: if (value != null) {
294: BlockFactory factory = createBlockFactory(providerType);
295:
296: if (value instanceof XMLFault) {
297: message = msgFactory.create(messageProtocol);
298: message.setXMLFault((XMLFault) value);
299: } else if (providerServiceMode != null
300: && providerServiceMode == Service.Mode.MESSAGE) {
301: // For MESSAGE mode, work with the entire message, Headers and Body
302: // This is based on logic in org.apache.axis2.jaxws.client.XMLDispatch.createMessageFromBundle()
303: if (value instanceof SOAPMessage) {
304: message = msgFactory
305: .createFrom((SOAPMessage) value);
306: } else {
307: Block block = factory.createFrom(value, null, null);
308: message = msgFactory.createFrom(block, null,
309: messageProtocol);
310: }
311: } else {
312: // PAYLOAD mode deals only with the body of the message.
313: Block block = factory.createFrom(value, null, null);
314: message = msgFactory.create(messageProtocol);
315: message.setBodyBlock(block);
316: }
317: }
318:
319: if (message == null)
320: // If we didn't create a message above (because there was no value), create one here
321: message = msgFactory.create(messageProtocol);
322:
323: return message;
324: }
325:
326: /*
327: * Determine the Provider type for this instance
328: */
329: private Provider getProviderInstance() throws Exception {
330: Class<?> clazz = getProviderType();
331:
332: if (!isValidProviderType(clazz)) {
333: //TODO This will change once deployment code it in place
334: throw new Exception(Messages.getMessage("InvalidProvider",
335: clazz.getName()));
336: }
337:
338: Provider provider = null;
339: if (clazz == String.class) {
340: provider = (Provider<String>) serviceInstance;
341: } else if (clazz == Source.class) {
342: provider = (Provider<Source>) serviceInstance;
343: } else if (clazz == SOAPMessage.class) {
344: provider = (Provider<SOAPMessage>) serviceInstance;
345: } else if (clazz == JAXBContext.class) {
346: provider = (Provider<JAXBContext>) serviceInstance;
347: }
348:
349: if (provider == null) {
350: throw ExceptionFactory.makeWebServiceException(Messages
351: .getMessage("InvalidProviderCreate", clazz
352: .getName()));
353: }
354:
355: return provider;
356:
357: }
358:
359: /*
360: * Get the provider type from a given implemention class instance
361: */
362: private Class<?> getProviderType() throws Exception {
363:
364: Class providerType = null;
365:
366: Type[] giTypes = serviceImplClass.getGenericInterfaces();
367: for (Type giType : giTypes) {
368: ParameterizedType paramType = null;
369: try {
370: paramType = (ParameterizedType) giType;
371: } catch (ClassCastException e) {
372: //TODO NLS
373: throw new Exception(
374: "Provider based SEI Class has to implement javax.xml.ws.Provider as javax.xml.ws.Provider<String>, javax.xml.ws.Provider<SOAPMessage>, javax.xml.ws.Provider<Source> or javax.xml.ws.Provider<JAXBContext>");
375: }
376: Class interfaceName = (Class) paramType.getRawType();
377:
378: if (interfaceName == javax.xml.ws.Provider.class) {
379: if (paramType.getActualTypeArguments().length > 1) {
380: //TODO NLS
381: throw new Exception(
382: "Provider cannot have more than one Generic Types defined as Per JAX-WS Specification");
383: }
384: providerType = (Class) paramType
385: .getActualTypeArguments()[0];
386: }
387: }
388: return providerType;
389: }
390:
391: /*
392: * Validate whether or not the Class passed in is a valid type of
393: * javax.xml.ws.Provider<T>. Per the JAX-WS 2.0 specification, the
394: * parameterized type of a Provider can only be:
395: *
396: * javax.xml.transform.Source
397: * javax.xml.soap.SOAPMessage
398: * javax.activation.DataSource
399: *
400: * We've also added support for String types which is NOT dictated
401: * by the spec.
402: */
403: private boolean isValidProviderType(Class clazz) {
404: boolean valid = clazz == String.class
405: || clazz == SOAPMessage.class || clazz == Source.class
406: || clazz == DataSource.class;
407:
408: if (!valid) {
409: if (log.isDebugEnabled()) {
410: log.debug("Class " + clazz.getName()
411: + " is not a valid Provider<T> type");
412: }
413: }
414:
415: return valid;
416: }
417:
418: /*
419: * Given a target class type for a payload, load the appropriate BlockFactory.
420: */
421: private BlockFactory createBlockFactory(Class type) {
422: if (blockFactory != null)
423: return blockFactory;
424:
425: if (type.equals(String.class)) {
426: blockFactory = (XMLStringBlockFactory) FactoryRegistry
427: .getFactory(XMLStringBlockFactory.class);
428: } else if (type.equals(Source.class)) {
429: blockFactory = (SourceBlockFactory) FactoryRegistry
430: .getFactory(SourceBlockFactory.class);
431: } else if (type.equals(SOAPMessage.class)) {
432: blockFactory = (SOAPEnvelopeBlockFactory) FactoryRegistry
433: .getFactory(SOAPEnvelopeBlockFactory.class);
434: } else {
435: ExceptionFactory
436: .makeWebServiceException("Unable to find BlockFactory "
437: + "for type: " + type.getClass().getName());
438: }
439:
440: return blockFactory;
441: }
442:
443: }
|