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;
020:
021: import org.apache.axiom.om.util.StAXUtils;
022: import org.apache.axis2.description.AxisOperation;
023: import org.apache.axis2.description.AxisService;
024: import org.apache.axis2.description.Parameter;
025: import org.apache.axis2.description.WSDL2Constants;
026: import org.apache.axis2.java.security.AccessController;
027: import org.apache.axis2.jaxws.ExceptionFactory;
028: import org.apache.axis2.jaxws.binding.SOAPBinding;
029: import org.apache.axis2.jaxws.core.InvocationContext;
030: import org.apache.axis2.jaxws.core.MessageContext;
031: import org.apache.axis2.jaxws.core.util.MessageContextUtils;
032: import org.apache.axis2.jaxws.description.DescriptionFactory;
033: import org.apache.axis2.jaxws.description.EndpointDescription;
034: import org.apache.axis2.jaxws.description.ServiceDescription;
035: import org.apache.axis2.jaxws.handler.HandlerChainProcessor;
036: import org.apache.axis2.jaxws.handler.HandlerInvokerUtils;
037: import org.apache.axis2.jaxws.handler.HandlerResolverImpl;
038: import org.apache.axis2.jaxws.handler.MEPContext;
039: import org.apache.axis2.jaxws.i18n.Messages;
040: import org.apache.axis2.jaxws.message.Message;
041: import org.apache.axis2.jaxws.message.Protocol;
042: import org.apache.axis2.jaxws.message.XMLFault;
043: import org.apache.axis2.jaxws.message.XMLFaultCode;
044: import org.apache.axis2.jaxws.message.XMLFaultReason;
045: import org.apache.axis2.jaxws.message.factory.MessageFactory;
046: import org.apache.axis2.jaxws.registry.FactoryRegistry;
047: import org.apache.axis2.jaxws.server.dispatcher.EndpointDispatcher;
048: import org.apache.axis2.jaxws.server.dispatcher.factory.EndpointDispatcherFactory;
049: import org.apache.axis2.jaxws.server.endpoint.lifecycle.EndpointLifecycleManager;
050: import org.apache.axis2.jaxws.server.endpoint.lifecycle.factory.EndpointLifecycleManagerFactory;
051: import org.apache.axis2.jaxws.spi.Constants;
052: import org.apache.axis2.wsdl.WSDLConstants.WSDL20_2004_Constants;
053: import org.apache.axis2.wsdl.WSDLConstants.WSDL20_2006Constants;
054: import org.apache.commons.logging.Log;
055: import org.apache.commons.logging.LogFactory;
056:
057: import javax.xml.stream.XMLStreamException;
058: import javax.xml.stream.XMLStreamReader;
059: import javax.xml.ws.http.HTTPBinding;
060: import java.io.StringReader;
061: import java.security.PrivilegedActionException;
062: import java.security.PrivilegedExceptionAction;
063: import java.util.Collection;
064:
065: /**
066: * The EndpointController is the server side equivalent to the InvocationController on the client
067: * side. It is an abstraction of the server side endpoint invocation that encapsulates all of the
068: * Axis2 semantics.
069: * <p/>
070: * Like the InvocationController, this class is responsible for invoking the JAX-WS application
071: * handler chain along with taking all of the provided information and setting up what's needed to
072: * perform the actual invocation of the endpoint.
073: */
074: public class EndpointController {
075:
076: private static final Log log = LogFactory
077: .getLog(EndpointController.class);
078:
079: private static final String PARAM_SERVICE_CLASS = "ServiceClass";
080:
081: public EndpointController() {
082: //do nothing
083: }
084:
085: /**
086: * This method is used to start the JAX-WS invocation of a target endpoint. It takes an
087: * InvocationContext, which must have a MessageContext specied for the request. Once the
088: * invocation is complete, the information will be stored
089: */
090: public InvocationContext invoke(InvocationContext ic) {
091: MessageContext requestMsgCtx = ic.getRequestMessageContext();
092:
093: String implClassName = getServiceImplClassName(requestMsgCtx);
094:
095: Class implClass = loadServiceImplClass(implClassName,
096: requestMsgCtx.getClassLoader());
097:
098: EndpointDescription endpointDesc = getEndpointDescription(
099: requestMsgCtx, implClass);
100: requestMsgCtx.setEndpointDescription(endpointDesc);
101:
102: /*
103: * TODO: review: make sure the handlers are set on the InvocationContext
104: * This implementation of the JAXWS runtime does not use Endpoint, which
105: * would normally be the place to initialize and store the handler list.
106: * In lieu of that, we will have to intialize and store them on the
107: * InvocationContext. also see the InvocationContextFactory. On the client
108: * side, the binding is not yet set when we call into that factory, so the
109: * handler list doesn't get set on the InvocationContext object there. Thus
110: * we gotta do it here.
111: *
112: * Since we're on the server, and there apparently is no Binding object
113: * anywhere to be found...
114: */
115: if (ic.getHandlers() == null) {
116: ic.setHandlers(new HandlerResolverImpl(endpointDesc
117: .getServiceDescription())
118: .getHandlerChain(endpointDesc.getPortInfo()));
119: }
120:
121: if (!bindingTypesMatch(requestMsgCtx, endpointDesc
122: .getServiceDescription())) {
123: Protocol protocol = requestMsgCtx.getMessage()
124: .getProtocol();
125: // only if protocol is soap12 and MISmatches the endpoint do we halt processing
126: if (protocol.equals(Protocol.soap12)) {
127: ic
128: .setResponseMessageContext(createMismatchFaultMsgCtx(
129: requestMsgCtx,
130: "Incoming SOAP message protocol is version 1.2, but endpoint is configured for SOAP 1.1"));
131: return ic;
132: } else if (protocol.equals(Protocol.soap11)) {
133: // SOAP 1.1 message and SOAP 1.2 binding
134:
135: // The canSupport flag indicates that we can support this scenario.
136: // Possible Examples of canSupport: JAXB impl binding, JAXB Provider
137: // Possible Example of !canSupport: Application handler usage, non-JAXB Provider
138: // Initially I vote to hard code this as false.
139: boolean canSupport = false;
140: if (canSupport) {
141: // TODO: Okay, but we need to scrub the Message create code to make sure that the response message
142: // is always built from the receiver protocol...not the binding protocol
143: } else {
144: ic
145: .setResponseMessageContext(createMismatchFaultMsgCtx(
146: requestMsgCtx,
147: "Incoming SOAP message protocol is version 1.1, but endpoint is configured for SOAP 1.2. This is not supported."));
148: return ic;
149: }
150: } else {
151: ic
152: .setResponseMessageContext(createMismatchFaultMsgCtx(
153: requestMsgCtx,
154: "Incoming message protocol does not match endpoint protocol."));
155: return ic;
156: }
157: }
158:
159: MessageContext responseMsgContext = null;
160:
161: try {
162: // Get the service instance. This will run the @PostConstruct code.
163: EndpointLifecycleManager elm = createEndpointlifecycleManager();
164: Object serviceInstance = elm.createServiceInstance(
165: requestMsgCtx, implClass);
166:
167: // The application handlers and dispatcher invoke will
168: // modify/destroy parts of the message. Make sure to save
169: // the request message if appropriate.
170: saveRequestMessage(requestMsgCtx);
171:
172: // Invoke inbound application handlers. It's safe to use the first object on the iterator because there is
173: // always exactly one EndpointDescription on a server invoke
174: boolean success = HandlerInvokerUtils
175: .invokeInboundHandlers(requestMsgCtx
176: .getMEPContext(), ic.getHandlers(),
177: HandlerChainProcessor.MEP.REQUEST,
178: isOneWay(requestMsgCtx
179: .getAxisMessageContext()));
180:
181: if (success) {
182:
183: // Dispatch to the
184: EndpointDispatcher dispatcher = getEndpointDispatcher(
185: implClass, serviceInstance);
186: try {
187: responseMsgContext = dispatcher
188: .invoke(requestMsgCtx);
189: } finally {
190: // Passed pivot point
191: requestMsgCtx.getMessage().setPostPivot();
192: }
193:
194: // Invoke the outbound response handlers.
195: // If the message is one way, we should not invoke the response handlers. There is no response
196: // MessageContext since a one way invocation is considered to have a "void" return.
197: if (!isOneWay(requestMsgCtx.getAxisMessageContext())) {
198: responseMsgContext.setMEPContext(requestMsgCtx
199: .getMEPContext());
200:
201: HandlerInvokerUtils.invokeOutboundHandlers(
202: responseMsgContext.getMEPContext(), ic
203: .getHandlers(),
204: HandlerChainProcessor.MEP.RESPONSE, false);
205: }
206: } else { // the inbound handler chain must have had a problem, and we've reversed directions
207: responseMsgContext = MessageContextUtils
208: .createResponseMessageContext(requestMsgCtx);
209: // since we've reversed directions, the message has "become a response message" (section 9.3.2.1, footnote superscript 2)
210: responseMsgContext.setMessage(requestMsgCtx
211: .getMessage());
212: }
213:
214: } catch (Exception e) {
215: // TODO for now, throw it. We probably should try to make an XMLFault object and set it on the message
216: throw ExceptionFactory.makeWebServiceException(e);
217: } finally {
218: restoreRequestMessage(requestMsgCtx);
219: }
220:
221: // The response MessageContext should be set on the InvocationContext
222: ic.setResponseMessageContext(responseMsgContext);
223:
224: return ic;
225: }
226:
227: /*
228: * Get the appropriate EndpointDispatcher for a given service endpoint.
229: */
230: protected EndpointDispatcher getEndpointDispatcher(
231: Class serviceImplClass, Object serviceInstance)
232: throws Exception {
233: EndpointDispatcherFactory factory = (EndpointDispatcherFactory) FactoryRegistry
234: .getFactory(EndpointDispatcherFactory.class);
235: return factory.createEndpointDispatcher(serviceImplClass,
236: serviceInstance);
237: }
238:
239: /*
240: * Tries to load the implementation class that was specified for the
241: * target endpoint
242: */
243: private Class loadServiceImplClass(String className, ClassLoader cl) {
244: if (log.isDebugEnabled()) {
245: log.debug("Attempting to load service impl class: "
246: + className);
247: }
248:
249: try {
250: //TODO: What should be done if the supplied ClassLoader is null?
251: Class _class = forName(className, true, cl);
252: return _class;
253: //Catch Throwable as ClassLoader can throw an NoClassDefFoundError that
254: //does not extend Exception, so lets catch everything that extends Throwable
255: //rather than just Exception.
256: } catch (Throwable cnf) {
257: throw ExceptionFactory.makeWebServiceException(Messages
258: .getMessage("EndpointControllerErr4", className));
259: }
260: }
261:
262: /**
263: * Return the class for this name
264: *
265: * @return Class
266: */
267: private static Class forName(final String className,
268: final boolean initialize, final ClassLoader classloader)
269: throws ClassNotFoundException {
270: // NOTE: This method must remain private because it uses AccessController
271: Class cl = null;
272: try {
273: cl = (Class) AccessController
274: .doPrivileged(new PrivilegedExceptionAction() {
275: public Object run()
276: throws ClassNotFoundException {
277: return Class.forName(className, initialize,
278: classloader);
279: }
280: });
281: } catch (PrivilegedActionException e) {
282: if (log.isDebugEnabled()) {
283: log.debug("Exception thrown from AccessController: "
284: + e);
285: }
286: throw (ClassNotFoundException) e.getException();
287: }
288:
289: return cl;
290: }
291:
292: private String getServiceImplClassName(MessageContext mc) {
293: // The PARAM_SERVICE_CLASS property that is set on the AxisService
294: // will tell us what the service implementation class is.
295: org.apache.axis2.context.MessageContext axisMsgContext = mc
296: .getAxisMessageContext();
297: AxisService as = axisMsgContext.getAxisService();
298: Parameter param = as.getParameter(PARAM_SERVICE_CLASS);
299:
300: // If there was no implementation class, we should not go any further
301: if (param == null) {
302: throw ExceptionFactory.makeWebServiceException(Messages
303: .getMessage("EndpointControllerErr2"));
304: }
305:
306: String className = ((String) param.getValue()).trim();
307: return className;
308: }
309:
310: /*
311: * Gets the ServiceDescription associated with the request that is currently
312: * being processed.
313: */
314: private EndpointDescription getEndpointDescription(
315: MessageContext mc, Class implClass) {
316: AxisService axisSvc = mc.getAxisMessageContext()
317: .getAxisService();
318:
319: //Check to see if we've already created a ServiceDescription for this
320: //service before trying to create a new one.
321:
322: if (axisSvc
323: .getParameter(EndpointDescription.AXIS_SERVICE_PARAMETER) != null) {
324: Parameter param = axisSvc
325: .getParameter(EndpointDescription.AXIS_SERVICE_PARAMETER);
326:
327: EndpointDescription ed = (EndpointDescription) param
328: .getValue();
329: return ed;
330: } else {
331: // TODO: This is using a deprecated factory method to create the ServiceDescription.
332: // The correct way to fix this is to create the ServiceDescriptions (and the AxisService
333: // and associated descritpion hierahcy) at startup. However, that is currently not done
334: // in the Axis2 testing environment. So, for testing, we create a Description hierachy
335: // on the fly and attach the AxisService to it. This should be changed to not used the
336: // deprecated factory method. HOWEVER doing so currently causes testcase failures in
337: // JAXWS and or Metadata
338: // ServiceDescription sd = DescriptionFactory.createServiceDescription(implClass);
339: ServiceDescription sd = DescriptionFactory
340: .createServiceDescriptionFromServiceImpl(implClass,
341: axisSvc);
342: EndpointDescription ed = sd
343: .getEndpointDescriptions_AsCollection().iterator()
344: .next();
345: return ed;
346: }
347: }
348:
349: private EndpointLifecycleManager createEndpointlifecycleManager() {
350: EndpointLifecycleManagerFactory elmf = (EndpointLifecycleManagerFactory) FactoryRegistry
351: .getFactory(EndpointLifecycleManagerFactory.class);
352: return elmf.createEndpointLifecycleManager();
353: }
354:
355: private boolean bindingTypesMatch(MessageContext requestMsgCtx,
356: ServiceDescription serviceDesc) {
357: // compare soap versions and respond appropriately under SOAP 1.2 Appendix 'A'
358: Collection<EndpointDescription> eds = serviceDesc
359: .getEndpointDescriptions_AsCollection();
360: // dispatch endpoints do not have SEIs, so watch out for null or empty array
361: if ((eds != null) && (eds.size() > 0)) {
362: EndpointDescription ed = eds.iterator().next();
363: Protocol protocol = requestMsgCtx.getMessage()
364: .getProtocol();
365: String endpointBindingType = ed.getBindingType();
366: if (protocol.equals(Protocol.soap11)) {
367: return (SOAPBinding.SOAP11HTTP_BINDING
368: .equalsIgnoreCase(endpointBindingType))
369: || (SOAPBinding.SOAP11HTTP_MTOM_BINDING
370: .equalsIgnoreCase(endpointBindingType));
371: } else if (protocol.equals(Protocol.soap12)) {
372: return (SOAPBinding.SOAP12HTTP_BINDING
373: .equalsIgnoreCase(endpointBindingType))
374: || (SOAPBinding.SOAP12HTTP_MTOM_BINDING
375: .equalsIgnoreCase(endpointBindingType));
376: } else if (protocol.equals(Protocol.rest)) {
377: return HTTPBinding.HTTP_BINDING
378: .equalsIgnoreCase(endpointBindingType);
379: }
380: }
381: // safe to assume?
382: return true;
383: }
384:
385: private MessageContext createMismatchFaultMsgCtx(
386: MessageContext requestMsgCtx, String errorMsg) {
387: try {
388: XMLFault xmlfault = new XMLFault(
389: XMLFaultCode.VERSIONMISMATCH, new XMLFaultReason(
390: errorMsg));
391: Message msg = ((MessageFactory) FactoryRegistry
392: .getFactory(MessageFactory.class))
393: .create(Protocol.soap11); // always soap11 according to the spec
394: msg.setXMLFault(xmlfault);
395: MessageContext responseMsgCtx = MessageContextUtils
396: .createFaultMessageContext(requestMsgCtx);
397: responseMsgCtx.setMessage(msg);
398: return responseMsgCtx;
399: } catch (XMLStreamException e) {
400: // Need to fix this ! At least provide logging
401: // TODO for now, throw it. We probably should try to make an XMLFault object and set it on the message
402: throw ExceptionFactory.makeWebServiceException(e);
403: }
404: }
405:
406: /**
407: * Save the request message if indicated by the SAVE_REQUEST_MSG property
408: *
409: * @param requestMsgContext
410: */
411: private void saveRequestMessage(MessageContext requestMsgContext) {
412:
413: // TODO: TESTING...FORCE SAVING THE REQUEST MESSAGE
414: // requestMsgContext.getAxisMessageContext().setProperty(Constants.SAVE_REQUEST_MSG, Boolean.TRUE);
415: // END TESTING
416:
417: Boolean value = (Boolean) requestMsgContext
418: .getAxisMessageContext().getProperty(
419: Constants.SAVE_REQUEST_MSG);
420: if (value != null && value == Boolean.TRUE) {
421: // REVIEW: This does not properly account for attachments.
422: Message m = requestMsgContext.getMessage();
423: String savedMsg = m.getAsOMElement().toString();
424: requestMsgContext.getAxisMessageContext().setProperty(
425: Constants.SAVED_REQUEST_MSG_TEXT, savedMsg);
426: }
427: }
428:
429: /**
430: * Restore the request message from the saved message text
431: *
432: * @param requestMsgContext
433: */
434: private void restoreRequestMessage(MessageContext requestMsgContext) {
435:
436: Boolean value = (Boolean) requestMsgContext
437: .getAxisMessageContext().getProperty(
438: Constants.SAVE_REQUEST_MSG);
439: if (value != null && value == Boolean.TRUE) {
440: // REVIEW: This does not properly account for attachments.
441: String savedMsg = (String) requestMsgContext
442: .getAxisMessageContext().getProperty(
443: Constants.SAVED_REQUEST_MSG_TEXT);
444: if (savedMsg != null && savedMsg.length() > 0) {
445: try {
446: StringReader sr = new StringReader(savedMsg);
447: XMLStreamReader xmlreader = StAXUtils
448: .createXMLStreamReader(sr);
449: MessageFactory mf = (MessageFactory) FactoryRegistry
450: .getFactory(MessageFactory.class);
451: Protocol protocol = requestMsgContext
452: .getAxisMessageContext().isDoingREST() ? Protocol.rest
453: : null;
454: Message msg = mf.createFrom(xmlreader, protocol);
455: requestMsgContext.setMessage(msg);
456: } catch (Throwable e) {
457: ExceptionFactory.makeWebServiceException(e);
458: }
459: }
460: }
461:
462: // TESTING....SIMULATE A PERSIST OF THE REQUEST MESSAGE
463: // String text = requestMsgContext.getMessage().getAsOMElement().toString();
464: // System.out.println("Persist Message" + text);
465: // END TESTING
466: }
467:
468: /*
469: * Determine if this is a one-way invocation or not.
470: */
471: public static boolean isOneWay(
472: org.apache.axis2.context.MessageContext mc) {
473: if (mc != null) {
474: AxisOperation op = mc.getAxisOperation();
475: String mep = op.getMessageExchangePattern();
476:
477: if (mep
478: .equals(WSDL20_2004_Constants.MEP_URI_ROBUST_IN_ONLY)
479: || mep
480: .equals(WSDL20_2004_Constants.MEP_URI_IN_ONLY)
481: || mep
482: .equals(WSDL20_2006Constants.MEP_URI_ROBUST_IN_ONLY)
483: || mep.equals(WSDL20_2006Constants.MEP_URI_IN_ONLY)
484: || mep
485: .equals(WSDL2Constants.MEP_URI_ROBUST_IN_ONLY)
486: || mep.equals(WSDL2Constants.MEP_URI_IN_ONLY)) {
487: return true;
488: }
489: }
490: return false;
491: }
492: }
|