| /* |
| * 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.axis2.jaxws.handler; |
| |
| import org.apache.axis2.jaxws.Constants; |
| import org.apache.axis2.jaxws.ExceptionFactory; |
| import org.apache.axis2.jaxws.context.factory.MessageContextFactory; |
| import org.apache.axis2.jaxws.handler.factory.HandlerPostInvokerFactory; |
| import org.apache.axis2.jaxws.handler.factory.HandlerPreInvokerFactory; |
| import org.apache.axis2.jaxws.i18n.Messages; |
| import org.apache.axis2.jaxws.marshaller.impl.alt.MethodMarshallerUtils; |
| import org.apache.axis2.jaxws.message.Message; |
| import org.apache.axis2.jaxws.message.Protocol; |
| import org.apache.axis2.jaxws.message.XMLFault; |
| import org.apache.axis2.jaxws.message.factory.MessageFactory; |
| import org.apache.axis2.jaxws.message.util.XMLFaultUtils; |
| import org.apache.axis2.jaxws.registry.FactoryRegistry; |
| import org.apache.axis2.jaxws.utility.SAAJFactory; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import javax.xml.soap.SOAPBody; |
| import javax.xml.soap.SOAPConstants; |
| import javax.xml.soap.SOAPFault; |
| import javax.xml.soap.SOAPMessage; |
| import javax.xml.ws.ProtocolException; |
| import javax.xml.ws.WebServiceException; |
| import javax.xml.ws.handler.Handler; |
| import javax.xml.ws.handler.LogicalHandler; |
| import javax.xml.ws.handler.soap.SOAPHandler; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| public class HandlerChainProcessor { |
| |
| private static final Log log = LogFactory.getLog(HandlerChainProcessor.class); |
| private HandlerPreInvoker handlerPreInvoker = null; |
| private HandlerPostInvoker handlerPostInvoker = null; |
| |
| public enum Direction { |
| IN, OUT |
| }; |
| |
| // the type of message, not indicative of one-way vs. request-response |
| public enum MEP { |
| REQUEST, RESPONSE |
| }; |
| |
| private javax.xml.ws.handler.MessageContext currentMC; // just a pointer |
| private LogicalMessageContext logicalMC = null; |
| private SoapMessageContext soapMC = null; |
| |
| private MEPContext mepCtx; |
| |
| private List<Handler> handlers = null; |
| |
| // track start/end of logical and protocol handlers in the list |
| // The two scenarios are: 1) run logical handlers only, 2) run all handlers |
| // logical start is always 0 |
| // protocol start is always logicalLength + 1 |
| // list end is always handlers.size()-1 |
| private int logicalLength = 0; |
| |
| private final static int SUCCESSFUL = 0; |
| private final static int FAILED = 1; |
| private final static int PROTOCOL_EXCEPTION = 2; |
| private final static int OTHER_EXCEPTION = 3; |
| // save it if Handler.handleMessage throws one in |
| // HandlerChainProcessor.handleMessage |
| private RuntimeException savedException; |
| private Protocol proto; // need to save it incase we have to make a fault message |
| |
| /* |
| * HandlerChainProcess expects null, empty list, or an already-sorted |
| * list. If the chain passed into here came from our HandlerChainResolver, |
| * it is sorted already. If a client app created or manipulated the list, |
| * it may not be sorted. The processChain and processFault methods check |
| * for this by calling verifyChain. |
| */ |
| public HandlerChainProcessor(List<Handler> chain, Protocol proto) { |
| if (chain == null) { |
| handlers = new ArrayList<Handler>(); |
| } |
| else |
| handlers = chain; |
| this.proto = proto; |
| } |
| |
| /* |
| * sortChain will properly sort the chain, logical then protocol, since it may be |
| * a chain built or modified by a client application. Also keep track of |
| * start/end for each type of handler. |
| */ |
| private void sortChain() throws WebServiceException { |
| |
| ArrayList<Handler> logicalHandlers = new ArrayList<Handler>(); |
| ArrayList<Handler> protocolHandlers = new ArrayList<Handler>(); |
| |
| Iterator handlerIterator = handlers.iterator(); |
| |
| while (handlerIterator.hasNext()) { |
| // this is a safe cast since the handlerResolver and binding.setHandlerChain |
| // and InvocationContext.setHandlerChain verifies it before we get here |
| Handler handler = (Handler)handlerIterator.next(); |
| // JAXWS 9.2.1.2 sort them by Logical, then SOAP |
| if (LogicalHandler.class.isAssignableFrom(handler.getClass())) |
| logicalHandlers.add((LogicalHandler) handler); |
| else if (SOAPHandler.class.isAssignableFrom(handler.getClass())) |
| // instanceof ProtocolHandler |
| protocolHandlers.add((SOAPHandler) handler); |
| else if (Handler.class.isAssignableFrom(handler.getClass())) { |
| throw ExceptionFactory.makeWebServiceException(Messages |
| .getMessage("handlerChainErr1", handler.getClass().getName())); |
| } else { |
| throw ExceptionFactory.makeWebServiceException(Messages |
| .getMessage("handlerChainErr2", handler.getClass().getName())); |
| } |
| } |
| |
| logicalLength = logicalHandlers.size(); |
| |
| // JAXWS 9.2.1.2 sort them by Logical, then SOAP |
| handlers.clear(); |
| handlers.addAll(logicalHandlers); |
| handlers.addAll(protocolHandlers); |
| } |
| |
| |
| |
| /** |
| * @param mc |
| * By the time processChain method is called, we already have the sorted chain, |
| * and now we have the direction, MEP, MessageContext, and if a response is expected. We should |
| * be able to handle everything from here, no pun intended. |
| * |
| * Two things a user of processChain should check when the method completes: |
| * 1. Has the MessageContext.MESSAGE_OUTBOUND_PROPERTY changed, indicating reversal of message direction |
| * 2. Has the message been converted to a fault message? (indicated by a flag in the message) |
| */ |
| public boolean processChain(MEPContext mepCtx, Direction direction, MEP mep, |
| boolean expectResponse) { |
| |
| if (handlers.size() == 0) |
| return true; |
| |
| this.mepCtx = mepCtx; |
| sortChain(); |
| initContext(direction); |
| boolean result = true; |
| |
| if (direction == Direction.OUT) { // 9.3.2 outbound |
| currentMC.put(javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY, |
| (direction == Direction.OUT)); |
| result = callGenericHandlers(mep, expectResponse, 0, handlers.size() - 1, direction); |
| } else { // IN case - 9.3.2 inbound |
| currentMC.put(javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY, |
| (direction == Direction.OUT)); |
| result = callGenericHandlers(mep, expectResponse, handlers.size() - 1, 0, direction); |
| } |
| |
| // message context may have been changed to be response, and message |
| // converted |
| // according to the JAXWS spec 9.3.2.1 footnote 2 |
| if ((Boolean) (currentMC.get(javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY)) != (direction == Direction.OUT)) |
| return false; |
| return result; |
| |
| } |
| |
| |
| /* |
| * This is the implementation of JAX-WS 2.0 section 9.3.2.1 |
| */ |
| private boolean callGenericHandlers(MEP mep, boolean expectResponse, int start, int end, |
| Direction direction) throws RuntimeException { |
| |
| // if this is a response message, expectResponse should always be false |
| if (mep == MEP.RESPONSE) |
| expectResponse = false; |
| |
| int i = start; |
| int result = SUCCESSFUL; |
| |
| // declared and initialized just in case we need them |
| // in a reverse flow situation |
| int newStart = 0, newStart_inclusive = 0, newEnd = 0; |
| Direction newDirection = direction; |
| |
| if (direction == Direction.OUT) { |
| while ((i <= end) && (result == SUCCESSFUL)) { |
| result = handleMessage(((Handler)handlers.get(i)), direction, expectResponse); |
| newStart = i - 1; |
| newStart_inclusive = i; |
| newEnd = 0; |
| newDirection = Direction.IN; |
| i++; |
| if (result == SUCCESSFUL) // don't switch if failed, since we'll be reversing directions |
| switchContext(direction, i); |
| } |
| } else { // IN case |
| while ((i >= end) && (result == SUCCESSFUL)) { |
| result = handleMessage(((Handler)handlers.get(i)), direction, expectResponse); |
| newStart = i + 1; |
| newStart_inclusive = i; |
| newEnd = handlers.size() - 1; |
| newDirection = Direction.OUT; |
| i--; |
| if (result == SUCCESSFUL) // don't switch if failed, since we'll be reversing directions |
| switchContext(direction, i); |
| } |
| } |
| |
| if (newDirection == direction) // we didn't actually process anything, probably due to empty list |
| return true; // no need to continue |
| |
| // 9.3.2.3 in all situations, we want to close as many handlers as |
| // were invoked prior to completion or exception throwing |
| if (expectResponse) { |
| if (result == FAILED) { |
| // we should only use callGenericHandlers_avoidRecursion in this case |
| callGenericHandlers_avoidRecursion(newStart, newEnd, newDirection); |
| callCloseHandlers(newStart_inclusive, newEnd, newDirection); |
| } else if (result == PROTOCOL_EXCEPTION) { |
| try { |
| callGenericHandleFault(newStart, newEnd, newDirection); |
| callCloseHandlers(newStart_inclusive, newEnd, newDirection); |
| } catch (RuntimeException re) { |
| callCloseHandlers(newStart_inclusive, newEnd, newDirection); |
| throw re; |
| } |
| } else if (result == OTHER_EXCEPTION) { |
| callCloseHandlers(newStart_inclusive, newEnd, newDirection); |
| // savedException initialized in HandlerChainProcessor.handleMessage |
| throw savedException; |
| } |
| } else { // everything was successful OR finished processing handlers |
| /* |
| * This is a little confusing. There are several cases we should be |
| * aware of. An incoming request with false expectResponse is |
| * equivalent to server inbound one-way, for example. |
| * |
| * An outgoing response is server outbound, and is always marked |
| * with a false expectResponse. The problem, however, is that the |
| * direction for the call to closehandlers will be incorrect. In |
| * this case, the handlers should be closed in the opposite order of |
| * the ORIGINAL invocation. |
| */ |
| if (mep.equals(MEP.REQUEST)) { |
| // a request that requires no response is a one-way message |
| // and we should only close whomever got invoked |
| callCloseHandlers(newStart_inclusive, newEnd, newDirection); |
| |
| // As according to the Sun "experts", exceptions raised by |
| // handlers in one way invocation are discarded. They |
| // are NOT propagated to the user code. |
| if (savedException != null) { |
| log.warn("Exception thrown by a handler in one way invocation", |
| savedException); |
| //But do return failure so that we know not to send to server |
| return false; |
| } |
| } |
| else { |
| // it's a response, so we can safely assume that |
| // ALL the handlers were invoked on the request, |
| // so we need to close ALL of them |
| if (direction.equals(Direction.IN)) { |
| callCloseHandlers(handlers.size() - 1, 0, direction); |
| } else { |
| callCloseHandlers(0, handlers.size() - 1, direction); |
| } |
| |
| if (savedException != null) { |
| // we have a saved exception, throw it (JAX-WS 9.3.2.1 "Throw |
| // ProtocolException or any other runtime exception --> No |
| // response" case. |
| throw savedException; |
| } |
| } |
| } |
| // If we've failed before this, we would have already thrown exception |
| // or returned false, so just return true here ... don't need to check result again. |
| return true; |
| } |
| |
| /* |
| * callGenericHandlers_avoidRecursion should ONLY be called from one place. |
| * TODO: We cannot necessarily assume no false returns and no exceptions will be |
| * thrown from here even though the handlers we will be calling have all already |
| * succeeded in callGenericHandlers. |
| */ |
| private void callGenericHandlers_avoidRecursion(int start, |
| int end, Direction direction) { |
| int i = start; |
| |
| if (direction == Direction.OUT) { |
| for (; i <= end; i++) { |
| switchContext(direction, i); |
| Handler handler = (Handler) handlers.get(i); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Invoking handleMessage on: " + handler.getClass().getName()); |
| } |
| handler.handleMessage(currentMC); |
| } |
| } else { // IN case |
| for (; i >= end; i--) { |
| switchContext(direction, i); |
| Handler handler = (Handler) handlers.get(i); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Invoking handleMessage on: " + handler.getClass().getName()); |
| } |
| handler.handleMessage(currentMC); |
| } |
| } |
| } |
| |
| |
| /** |
| * Calls handleMessage on the Handler. If an exception is thrown and a response is expected, the |
| * MessageContext is updated with the handler information |
| * |
| * @returns SUCCESSFUL if successfully, UNSUCCESSFUL if false, EXCEPTION if exception thrown |
| */ |
| private int handleMessage(Handler handler, Direction direction, |
| boolean expectResponse) throws RuntimeException { |
| try { |
| if (log.isDebugEnabled()) { |
| log.debug("Invoking handleMessage on: " + handler.getClass().getName()); |
| } |
| |
| // The pre and post invokers will likely need more than just the handler message context. |
| // They may need access to the axis service object or description objects. |
| currentMC.put(Constants.MEP_CONTEXT, mepCtx); |
| |
| getPreInvoker().preInvoke(currentMC); |
| boolean success = handler.handleMessage(currentMC); |
| getPostInvoker().postInvoke(currentMC); |
| if (success) { |
| if (log.isDebugEnabled()) { |
| log.debug("handleMessage() returned true"); |
| } |
| return SUCCESSFUL; |
| } |
| else { |
| if (log.isDebugEnabled()) { |
| log.debug("handleMessage() returned false"); |
| } |
| if (expectResponse) |
| currentMC.put(javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY, |
| (direction != Direction.OUT)); |
| return FAILED; |
| } |
| } |
| catch (RuntimeException re) { |
| // RuntimeException and ProtocolException |
| if(log.isDebugEnabled()) { |
| log.debug("An exception was thrown during the handleMessage() invocation"); |
| log.debug("Exception: ", re); |
| } |
| |
| savedException = re; |
| if (expectResponse) |
| // mark it as reverse direction |
| currentMC.put(javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY, |
| (direction != Direction.OUT)); |
| if (ProtocolException.class.isAssignableFrom(re.getClass())) { |
| convertToFaultMessage(mepCtx, re, proto); |
| // just re-initialize the current handler message context since |
| // that will pick up the now-changed message |
| return PROTOCOL_EXCEPTION; |
| } |
| return OTHER_EXCEPTION; |
| } |
| |
| } |
| |
| |
| /* |
| * start and end should be INclusive of the handlers that have already been |
| * invoked on Handler.handleMessage or Handler.handleFault |
| */ |
| private void callCloseHandlers(int start, int end, |
| Direction direction) { |
| int i = start; |
| |
| if (direction == Direction.OUT) { |
| for (; i <= end; i++) { |
| try { |
| switchContext(direction, i); |
| Handler handler = (Handler) handlers.get(i); |
| if (log.isDebugEnabled()) { |
| log.debug("Invoking close on: " + handler.getClass().getName()); |
| } |
| handler.close(currentMC); |
| |
| // TODO when we close, are we done with the handler instance, and thus |
| // may call the PreDestroy annotated method? I don't think so, especially |
| // if we've cached the handler list somewhere. |
| } catch (Exception e) { |
| if (log.isDebugEnabled()) { |
| log.debug("An Exception occurred while calling handler.close()"); |
| log.debug("Exception: " + e.getClass().getName() + ":" + e.getMessage()); |
| } |
| } |
| } |
| } else { // IN case |
| for (; i >= end; i--) { |
| try { |
| switchContext(direction, i); |
| Handler handler = (Handler) handlers.get(i); |
| if (log.isDebugEnabled()) { |
| log.debug("Invoking close on: " + handler.getClass().getName()); |
| } |
| handler.close(currentMC); |
| |
| // TODO when we close, are we done with the handler instance, and thus |
| // may call the PreDestroy annotated method? I don't think so, especially |
| // if we've cached the handler list somewhere. |
| } catch (Exception e) { |
| if (log.isDebugEnabled()) { |
| log.debug("An Exception occurred while calling handler.close()"); |
| log.debug("Exception: " + e.getClass().getName() + ":" + e.getMessage()); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * processFault is available for a server to use when the endpoint |
| * throws an exception or a client when it gets a fault response message |
| * |
| * In both cases, all of the handlers have run successfully in the |
| * opposite direction as this call to callHandleFault, and thus |
| * should be closed. |
| */ |
| public void processFault(MEPContext mepCtx, Direction direction) { |
| |
| // direction.IN = client |
| // direction.OUT = server |
| if (handlers.size() == 0) |
| return; |
| |
| this.mepCtx = mepCtx; |
| sortChain(); |
| initContext(direction); |
| currentMC.put(javax.xml.ws.handler.MessageContext.MESSAGE_OUTBOUND_PROPERTY, (direction == Direction.OUT)); |
| |
| try { |
| if (direction == Direction.OUT) { |
| callGenericHandleFault(0, handlers.size() - 1, direction); |
| } else { // IN case |
| callGenericHandleFault(handlers.size() - 1, 0, direction); |
| } |
| } catch (RuntimeException re) { |
| // TODO: log it |
| throw re; |
| } finally { |
| // we can close all the Handlers in reverse order |
| if (direction == Direction.OUT) { |
| initContext(Direction.IN); |
| callCloseHandlers(0, handlers.size() - 1, Direction.OUT); |
| } else { // IN case |
| initContext(Direction.IN); |
| callCloseHandlers(handlers.size() - 1, 0, Direction.IN); |
| } |
| } |
| } |
| |
| |
| /* |
| * The callGenericHandleFault caller is responsible for closing any invoked |
| * Handlers. We don't know how far the Handler.handleMessage calls got |
| * before a failure may have occurred. |
| * |
| * Regardless of the Handler.handleFault result, the flow is the same (9.3.2.2) |
| */ |
| private void callGenericHandleFault(int start, int end, |
| Direction direction) throws RuntimeException { |
| |
| int i = start; |
| |
| // we may be starting in the middle of the list, and therefore may need to switch contexts |
| switchContext(direction, i); |
| |
| if (direction == Direction.OUT) { |
| for (; i <= end; i++) { |
| Handler handler = (Handler) handlers.get(i); |
| if (log.isDebugEnabled()) { |
| log.debug("Invoking handleFault on: " + handler.getClass().getName()); |
| } |
| boolean success = handler.handleFault(currentMC); |
| |
| if (!success) |
| break; |
| switchContext(direction, i + 1); |
| } |
| } else { // IN case |
| for (; i >= end; i--) { |
| Handler handler = (Handler) handlers.get(i); |
| if (log.isDebugEnabled()) { |
| log.debug("Invoking handleFault on: " + handler.getClass().getName()); |
| } |
| boolean success = handler.handleFault(currentMC); |
| |
| if (!success) |
| break; |
| switchContext(direction, i - 1); |
| } |
| } |
| } |
| |
| |
| public static void convertToFaultMessage(MEPContext mepCtx, Exception e, Protocol protocol) { |
| |
| // need to check if message is already a fault message or not, |
| // probably by way of a flag (isFault) in the MessageContext or Message |
| if (log.isDebugEnabled()) { |
| log.debug("Creating a fault Message object for the exception: " + e.getClass().getName()); |
| } |
| |
| try { |
| /* TODO TODO TODO |
| * There has GOT to be a better way to do this. |
| */ |
| if (protocol == Protocol.soap11 || protocol == Protocol.soap12) { |
| String protocolNS = (protocol == Protocol.soap11) ? |
| SOAPConstants.URI_NS_SOAP_1_1_ENVELOPE : |
| SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE; |
| |
| // The following set of instructions is used to avoid |
| // some unimplemented methods in the Axis2 SAAJ implementation |
| XMLFault xmlFault = MethodMarshallerUtils.createXMLFaultFromSystemException(e); |
| javax.xml.soap.MessageFactory mf = SAAJFactory.createMessageFactory(protocolNS); |
| SOAPMessage message = mf.createMessage(); |
| SOAPBody body = message.getSOAPBody(); |
| SOAPFault soapFault = XMLFaultUtils.createSAAJFault(xmlFault, body); |
| |
| // TODO something is wrong here. The message should be a response message, not |
| // a request message. I don't see how to change that. (see the debugger...) |
| // TODO probably also need to turn on message.WRITE_XML_DECLARATION |
| MessageFactory msgFactory = (MessageFactory) FactoryRegistry.getFactory(MessageFactory.class); |
| Message msg = msgFactory.createFrom(message); |
| mepCtx.setMessage(msg); |
| |
| } else { |
| throw ExceptionFactory.makeWebServiceException(Messages.getMessage("cFaultMsgErr")); |
| } |
| |
| } catch (Exception ex) { |
| throw ExceptionFactory.makeWebServiceException(ex); |
| } |
| |
| } |
| |
| |
| private void initContext(Direction direction) { |
| soapMC = MessageContextFactory.createSoapMessageContext(mepCtx.getMessageContext()); |
| logicalMC = MessageContextFactory.createLogicalMessageContext(mepCtx.getMessageContext()); |
| if (direction == Direction.OUT) { |
| // logical context, then SOAP |
| if ((logicalLength == 0) && (handlers.size() > 0)) // we only have soap handlers |
| currentMC = soapMC; //MessageContextFactory.createSoapMessageContext(mepCtx.getMessageContext()); |
| else |
| currentMC = logicalMC; //MessageContextFactory.createLogicalMessageContext(mepCtx.getMessageContext()); |
| } else { |
| // SOAP context, then logical |
| if ((logicalLength == handlers.size()) && (handlers.size() > 0)) // we only have logical handlers |
| currentMC = logicalMC; //MessageContextFactory.createLogicalMessageContext(mepCtx.getMessageContext()); |
| else |
| currentMC = soapMC; //MessageContextFactory.createSoapMessageContext(mepCtx.getMessageContext()); |
| } |
| } |
| |
| private void switchContext(Direction direction, int index) { |
| |
| if ((logicalLength == handlers.size()) || (logicalLength == 0)) |
| return; // all handlers must be the same type, so no context switch |
| |
| if (((direction == Direction.OUT) && (index == logicalLength)) |
| || ((direction == Direction.IN) && (index == (logicalLength - 1)))) { |
| //if (currentMC.getClass().isAssignableFrom(LogicalMessageContext.class)) |
| if (currentMC == logicalMC) // object check, not .equals() |
| currentMC = soapMC; //MessageContextFactory.createSoapMessageContext(mepCtx.getMessageContext()); |
| else |
| currentMC = logicalMC; //MessageContextFactory.createLogicalMessageContext(mepCtx.getMessageContext()); |
| } |
| } |
| |
| private HandlerPreInvoker getPreInvoker() { |
| if (handlerPreInvoker == null) { |
| HandlerPreInvokerFactory preInvokerFactory = (HandlerPreInvokerFactory)FactoryRegistry.getFactory(HandlerPreInvokerFactory.class); |
| handlerPreInvoker = (HandlerPreInvoker)preInvokerFactory.createHandlerPreInvoker(); |
| } |
| return handlerPreInvoker; |
| } |
| |
| private HandlerPostInvoker getPostInvoker() { |
| if (handlerPostInvoker == null) { |
| HandlerPostInvokerFactory postInvokerFactory = (HandlerPostInvokerFactory)FactoryRegistry.getFactory(HandlerPostInvokerFactory.class); |
| handlerPostInvoker = (HandlerPostInvoker)postInvokerFactory.createHandlerPostInvoker(); |
| } |
| return handlerPostInvoker; |
| } |
| |
| |
| } |