/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.jaxws; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import javax.xml.ws.AsyncHandler; import javax.xml.ws.Provider; import javax.xml.ws.Response; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.MessageContext.Scope; import javax.xml.ws.soap.SOAPFaultException; import org.apache.cxf.annotations.UseAsyncMethod; import org.apache.cxf.binding.soap.SoapFault; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.continuations.Continuation; import org.apache.cxf.continuations.ContinuationProvider; import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.headers.Header; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.jaxws.context.WrappedMessageContext; import org.apache.cxf.message.Exchange; import org.apache.cxf.message.FaultMode; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageContentsList; import org.apache.cxf.message.MessageImpl; import org.apache.cxf.message.MessageUtils; import org.apache.cxf.service.factory.ServiceConstructionException; import org.apache.cxf.service.invoker.Factory; import org.apache.cxf.service.invoker.FactoryInvoker; import org.apache.cxf.service.invoker.SingletonFactory; import org.apache.cxf.service.model.BindingOperationInfo; public abstract class AbstractJAXWSMethodInvoker extends FactoryInvoker { private static final String ASYNC_METHOD = "org.apache.cxf.jaxws.async.method"; private static final String PARTIAL_RESPONSE_SENT_PROPERTY = "org.apache.cxf.ws.addressing.partial.response.sent"; public AbstractJAXWSMethodInvoker(final Object bean) { super(new SingletonFactory(bean)); } public AbstractJAXWSMethodInvoker(Factory factory) { super(factory); } protected SOAPFaultException findSoapFaultException(Throwable ex) { if (ex instanceof SOAPFaultException) { return (SOAPFaultException)ex; } if (ex.getCause() != null) { return findSoapFaultException(ex.getCause()); } return null; } @Override protected Method adjustMethodAndParams(Method mOriginal, Exchange ex, List<Object> params, Class<?> serviceObjectClass) { // If class implements Provider<T> interface, use overriden method from service object class // to check UseAsyncMethod annotation Method mso = getProviderServiceObjectMethod(mOriginal, serviceObjectClass); UseAsyncMethod uam = mso.getAnnotation(UseAsyncMethod.class); if (uam != null) { BindingOperationInfo bop = ex.getBindingOperationInfo(); Method ret = bop.getProperty(ASYNC_METHOD, Method.class); if (ret == null) { Class<?> ptypes[] = new Class<?>[mso.getParameterTypes().length + 1]; System.arraycopy(mso.getParameterTypes(), 0, ptypes, 0, mso.getParameterTypes().length); ptypes[mso.getParameterTypes().length] = AsyncHandler.class; try { ret = mso.getDeclaringClass().getMethod(mso.getName() + "Async", ptypes); bop.setProperty(ASYNC_METHOD, ret); } catch (Throwable t) { //ignore } } if (ret != null) { JaxwsServerHandler h = ex.get(JaxwsServerHandler.class); if (h != null) { return ret; } ContinuationProvider cp = ex.getInMessage().get(ContinuationProvider.class); // Check for decoupled endpoints: if partial response already was sent, ignore continuation boolean decoupledEndpoints = MessageUtils .getContextualBoolean(ex.getInMessage(), PARTIAL_RESPONSE_SENT_PROPERTY, false); if ((cp == null) && uam.always() || decoupledEndpoints) { JaxwsServerHandler handler = new JaxwsServerHandler(null); ex.put(JaxwsServerHandler.class, handler); params.add(handler); return ret; } else if (cp != null && cp.getContinuation() != null) { final Continuation c = cp.getContinuation(); c.suspend(0); JaxwsServerHandler handler = new JaxwsServerHandler(c); ex.put(JaxwsServerHandler.class, handler); params.add(handler); return ret; } } } return mOriginal; } private Method getProviderServiceObjectMethod(Method m, Class<?> serviceObjectClass) { if (!Provider.class.isAssignableFrom(serviceObjectClass)) { return m; } Class<?> currentSvcClass = serviceObjectClass; Class<?> genericType = null; while (currentSvcClass != null) { genericType = getProviderGenericType(currentSvcClass); if (genericType != null) { break; } // Check superclass until top currentSvcClass = currentSvcClass.getSuperclass(); } // Should never happens if (genericType == null) { return m; } try { return serviceObjectClass.getMethod("invoke", genericType); } catch (Exception e) { throw new ServiceConstructionException(e); } } private Class<?> getProviderGenericType(Class<?> svcClass) { Type[] interfaces = svcClass.getGenericInterfaces(); for (Type interfaceType : interfaces) { if (interfaceType instanceof ParameterizedType) { ParameterizedType paramInterface = (ParameterizedType)interfaceType; if (!paramInterface.getRawType().equals(Provider.class)) { continue; } Type[] typeArgs = paramInterface.getActualTypeArguments(); if (typeArgs.length > 0) { return (Class<?>)typeArgs[0]; } } } return null; } class JaxwsServerHandler implements AsyncHandler<Object> { Response<Object> r; Continuation continuation; boolean done; JaxwsServerHandler(Continuation c) { continuation = c; } public synchronized void handleResponse(Response<Object> res) { r = res; done = true; if (continuation != null) { continuation.resume(); } notifyAll(); } public boolean hasContinuation() { return continuation != null; } public synchronized void waitForDone() { while (!done) { try { wait(); } catch (InterruptedException e) { //ignore } } } public boolean isDone() { return done; } public Object getObject() throws Exception { return r.get(); } } @Override protected Object invoke(Exchange exchange, final Object serviceObject, Method m, List<Object> params) { JaxwsServerHandler h = exchange.get(JaxwsServerHandler.class); if (h != null && h.isDone()) { BindingOperationInfo bop = exchange.getBindingOperationInfo(); if (bop.isUnwrapped()) { exchange.put(BindingOperationInfo.class, bop.getWrappedOperation()); } try { return new MessageContentsList(h.getObject()); } catch (ExecutionException ex) { exchange.getInMessage().put(FaultMode.class, FaultMode.CHECKED_APPLICATION_FAULT); throw createFault(ex.getCause(), m, params, true); } catch (Exception ex) { throw createFault(ex.getCause(), m, params, false); } } Object o = super.invoke(exchange, serviceObject, m, params); if (h != null && !h.hasContinuation()) { h.waitForDone(); BindingOperationInfo bop = exchange.getBindingOperationInfo(); if (bop.isUnwrapped()) { exchange.put(BindingOperationInfo.class, bop.getWrappedOperation()); } try { return new MessageContentsList(h.getObject()); } catch (ExecutionException ex) { exchange.getInMessage().put(FaultMode.class, FaultMode.CHECKED_APPLICATION_FAULT); throw createFault(ex.getCause(), m, params, true); } catch (Exception ex) { throw createFault(ex.getCause(), m, params, false); } } return o; } @Override protected Fault createFault(Throwable ex, Method m, List<Object> params, boolean checked) { //map the JAX-WS faults SOAPFaultException sfe = findSoapFaultException(ex); if (sfe != null) { SoapFault fault = new SoapFault(sfe.getFault().getFaultString(), ex, sfe.getFault().getFaultCodeAsQName()); fault.setRole(sfe.getFault().getFaultActor()); if (sfe.getFault().hasDetail()) { fault.setDetail(sfe.getFault().getDetail()); } return fault; } return super.createFault(ex, m, params, checked); } protected Map<String, Object> removeHandlerProperties(WrappedMessageContext ctx) { Map<String, Scope> scopes = CastUtils.cast((Map<?, ?>)ctx.get(WrappedMessageContext.SCOPES)); Map<String, Object> handlerScopedStuff = new HashMap<>(); if (scopes != null) { for (Map.Entry<String, Scope> scope : scopes.entrySet()) { if (scope.getValue() == Scope.HANDLER) { handlerScopedStuff.put(scope.getKey(), ctx.get(scope.getKey())); } } for (String key : handlerScopedStuff.keySet()) { ctx.remove(key); } } return handlerScopedStuff; } protected void addHandlerProperties(WrappedMessageContext ctx, Map<String, Object> handlerScopedStuff) { for (Map.Entry<String, Object> key : handlerScopedStuff.entrySet()) { ctx.put(key.getKey(), key.getValue(), Scope.HANDLER); } } private Message createResponseMessage(Exchange exchange) { if (exchange == null) { return null; } Message m = exchange.getOutMessage(); if (m == null && !exchange.isOneWay()) { Endpoint ep = exchange.getEndpoint(); m = new MessageImpl(); m.setExchange(exchange); m = ep.getBinding().createMessage(m); exchange.setOutMessage(m); } return m; } protected void updateWebServiceContext(Exchange exchange, MessageContext ctx) { // Guard against wrong type associated with header list. // Need to copy header only if the message is going out. if (ctx.containsKey(Header.HEADER_LIST) && ctx.get(Header.HEADER_LIST) instanceof List<?>) { List<?> list = (List<?>) ctx.get(Header.HEADER_LIST); if (list != null && !list.isEmpty()) { SoapMessage sm = (SoapMessage) createResponseMessage(exchange); if (sm != null) { Iterator<?> iter = list.iterator(); while (iter.hasNext()) { sm.getHeaders().add((Header) iter.next()); } } } } if (exchange.getOutMessage() != null) { Message out = exchange.getOutMessage(); if (out.containsKey(Message.PROTOCOL_HEADERS)) { Map<String, List<String>> heads = CastUtils .cast((Map<?, ?>)exchange.getOutMessage().get(Message.PROTOCOL_HEADERS)); if (heads.containsKey("Content-Type")) { List<String> ct = heads.get("Content-Type"); exchange.getOutMessage().put(Message.CONTENT_TYPE, ct.get(0)); heads.remove("Content-Type"); } } } } protected void updateHeader(Exchange exchange, MessageContext ctx) { if (ctx.containsKey(Header.HEADER_LIST) && ctx.get(Header.HEADER_LIST) instanceof List<?>) { List<?> list = (List<?>) ctx.get(Header.HEADER_LIST); if (list != null && !list.isEmpty()) { SoapMessage sm = (SoapMessage) createResponseMessage(exchange); if (sm != null) { Iterator<?> iter = list.iterator(); while (iter.hasNext()) { Header header = (Header) iter.next(); if (header.getDirection() != Header.Direction.DIRECTION_IN && !header.getName().getNamespaceURI(). equals("http://docs.oasis-open.org/wss/2004/01/" + "oasis-200401-wss-wssecurity-secext-1.0.xsd") && !header.getName().getNamespaceURI(). equals("http://docs.oasis-open.org/" + "wss/oasis-wss-wssecurity-secext-1.1.xsd")) { //don't copy over security header, out interceptor chain will take care of it. sm.getHeaders().add(header); } } } } } } }