1/*
2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.xml.internal.ws.handler;
27
28import com.sun.istack.internal.Nullable;
29import com.sun.xml.internal.ws.api.WSBinding;
30import com.sun.xml.internal.ws.api.message.Packet;
31import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
32import com.sun.xml.internal.ws.api.pipe.*;
33import com.sun.xml.internal.ws.api.pipe.helper.AbstractFilterTubeImpl;
34import com.sun.xml.internal.ws.binding.BindingImpl;
35import com.sun.xml.internal.ws.client.HandlerConfiguration;
36
37import javax.xml.ws.handler.MessageContext;
38import javax.xml.ws.handler.Handler;
39import java.util.List;
40
41/**
42 * @author WS Development team
43 */
44
45public abstract class HandlerTube extends AbstractFilterTubeImpl {
46    /**
47     * handle hold reference to other Tube for inter-tube communication
48     */
49    HandlerTube cousinTube;
50    protected List<Handler> handlers;
51    HandlerProcessor processor;
52    boolean remedyActionTaken = false;
53    protected final @Nullable WSDLPort port;
54    // flag used to decide whether to call close on cousinTube
55    boolean requestProcessingSucessful = false;
56    private WSBinding binding;
57    private HandlerConfiguration hc;
58
59    public HandlerTube(Tube next, WSDLPort port, WSBinding binding) {
60        super(next);
61        this.port = port;
62        this.binding = binding;
63    }
64
65    public HandlerTube(Tube next, HandlerTube cousinTube, WSBinding binding) {
66        super(next);
67        this.cousinTube = cousinTube;
68        this.binding = binding;
69        if(cousinTube != null) {
70            this.port = cousinTube.port;
71        } else {
72            this.port = null;
73        }
74    }
75
76    /**
77     * Copy constructor for {@link Tube#copy(TubeCloner)}.
78     */
79    protected HandlerTube(HandlerTube that, TubeCloner cloner) {
80        super(that,cloner);
81        if(that.cousinTube != null) {
82            this.cousinTube = cloner.copy(that.cousinTube);
83        }
84        this.port = that.port;
85        this.binding = that.binding;
86    }
87
88    protected WSBinding getBinding() {
89        return binding;
90    }
91
92    @Override
93    public NextAction processRequest(Packet request) {
94        setupExchange();
95        // This check is done to cover handler returning false in Oneway request
96        if (isHandleFalse()) {
97            // Cousin HandlerTube returned false during Oneway Request processing.
98            // Don't call handlers and dispatch the message.
99            remedyActionTaken = true;
100            return doInvoke(super.next, request);
101        }
102
103        // This is done here instead of the constructor, since User can change
104        // the roles and handlerchain after a stub/proxy is created.
105        setUpProcessorInternal();
106
107        MessageUpdatableContext context = getContext(request);
108        boolean isOneWay = checkOneWay(request);
109        try {
110            if (!isHandlerChainEmpty()) {
111                // Call handlers on Request
112                boolean handlerResult = callHandlersOnRequest(context, isOneWay);
113                //Update Packet with user modifications
114                context.updatePacket();
115                // two-way case where no message is sent
116                if (!isOneWay && !handlerResult) {
117                    return doReturnWith(request);
118                }
119            }
120            requestProcessingSucessful = true;
121            // Call next Tube
122            return doInvoke(super.next, request);
123        } catch (RuntimeException re) {
124            if(isOneWay) {
125                //Eat the exception, its already logged and close the transportBackChannel
126                if(request.transportBackChannel != null ) {
127                        request.transportBackChannel.close();
128                }
129                request.setMessage(null);
130                return doReturnWith(request);
131            } else
132                throw re;
133        } finally {
134            if(!requestProcessingSucessful) {
135                initiateClosing(context.getMessageContext());
136            }
137        }
138
139    }
140
141    @Override
142    public NextAction processResponse(Packet response) {
143        setupExchange();
144        MessageUpdatableContext context = getContext(response);
145        try {
146            if (isHandleFalse() || (response.getMessage() == null)) {
147                // Cousin HandlerTube returned false during Response processing.
148                // or it is oneway request
149                // or handler chain is empty
150                // Don't call handlers.
151                return doReturnWith(response);
152            }
153
154            setUpProcessorInternal();
155
156            boolean isFault = isHandleFault(response);
157            if (!isHandlerChainEmpty()) {
158                // Call handlers on Response
159                callHandlersOnResponse(context, isFault);
160            }
161        } finally {
162            initiateClosing(context.getMessageContext());
163        }
164        //Update Packet with user modifications
165        context.updatePacket();
166
167        return doReturnWith(response);
168
169    }
170
171    @Override
172    public NextAction processException(Throwable t) {
173        try {
174            return doThrow(t);
175        } finally {
176                Packet packet = Fiber.current().getPacket();
177            MessageUpdatableContext context = getContext(packet);
178            initiateClosing(context.getMessageContext());
179            /* TODO revisit: commented this out as the modified packet is no longer used
180                    In future if the message is propagated even when an exception
181                    occurs, then uncomment context.updatePacket();
182            */
183            //Update Packet with user modifications
184            //context.updatePacket();
185
186
187        }
188    }
189
190    /**
191     * Must be overridden by HandlerTube that drives other handler tubes for processing a message.
192     * On Client-side: ClientLogicalHandlerTube drives the Handler Processing.
193     * On Server-side: In case SOAP Binding, ServerMessageHandlerTube drives the Handler Processing.
194     *                 In case XML/HTTP Binding, ServerLogicalHandlerTube drives the Handler Processing.
195     *
196     *
197     * If its a top HandlerTube, should override by calling #close(MessaggeContext);
198     *
199     */
200
201    protected void initiateClosing(MessageContext mc) {
202        // Do nothing
203
204    }
205
206    /**
207     * Calls close on previously invoked handlers.
208     * Also, Cleans up any state left over in the Tube instance from the current
209     * invocation, as Tube instances can be reused after the completion of MEP.
210     *
211     * On Client, SOAPHandlers are closed first and then LogicalHandlers
212     * On Server, LogicalHandlers are closed first and then SOAPHandlers
213     */
214    final public void close(MessageContext msgContext) {
215        //assuming cousinTube is called if requestProcessingSucessful is true
216        if (requestProcessingSucessful) {
217            if (cousinTube != null) {
218                cousinTube.close(msgContext);
219            }
220
221        }
222        if (processor != null)
223            closeHandlers(msgContext);
224
225        // Clean up the exchange for next invocation.
226        exchange = null;
227        requestProcessingSucessful = false;
228
229    }
230
231    /**
232     * On Client, Override by calling #closeClientHandlers(MessageContext mc)
233     * On Server, Override by calling #closeServerHandlers(MessageContext mc)
234     *      The difference is the order in which they are closed.
235     * @param mc
236     */
237    abstract void closeHandlers(MessageContext mc);
238
239    /**
240     * Called by close(MessageContext mc) in a Client Handlertube
241     */
242    protected void closeClientsideHandlers(MessageContext msgContext) {
243         if (processor == null)
244            return;
245        if (remedyActionTaken) {
246            //Close only invoked handlers in the chain
247
248            //CLIENT-SIDE
249            processor.closeHandlers(msgContext, processor.getIndex(), 0);
250            processor.setIndex(-1);
251            //reset remedyActionTaken
252            remedyActionTaken = false;
253        } else {
254            //Close all handlers in the chain
255
256            //CLIENT-SIDE
257            processor.closeHandlers(msgContext, handlers.size() - 1, 0);
258
259        }
260    }
261
262    /**
263     * Called by close(MessageContext mc) in a Server Handlertube
264     */
265    protected void closeServersideHandlers(MessageContext msgContext) {
266        if (processor == null)
267            return;
268        if (remedyActionTaken) {
269            //Close only invoked handlers in the chain
270
271            //SERVER-SIDE
272            processor.closeHandlers(msgContext, processor.getIndex(), handlers.size() - 1);
273            processor.setIndex(-1);
274            //reset remedyActionTaken
275            remedyActionTaken = false;
276        } else {
277            //Close all handlers in the chain
278
279            //SERVER-SIDE
280            processor.closeHandlers(msgContext, 0, handlers.size() - 1);
281
282        }
283    }
284
285    abstract void callHandlersOnResponse(MessageUpdatableContext context, boolean handleFault);
286
287    abstract boolean callHandlersOnRequest(MessageUpdatableContext context, boolean oneWay);
288
289    private boolean checkOneWay(Packet packet) {
290        if (port != null) {
291            /* we can determine this value from WSDL */
292            return packet.getMessage().isOneWay(port);
293        } else {
294            /*
295              otherwise use this value as an approximation, since this carries
296              the application's intention --- whether it was invokeOneway vs invoke,etc.
297             */
298            return !(packet.expectReply != null && packet.expectReply);
299        }
300    }
301
302    private void setUpProcessorInternal() {
303        HandlerConfiguration hc = ((BindingImpl) binding).getHandlerConfig();
304        if (hc != this.hc)
305                resetProcessor();
306        this.hc = hc;
307
308        setUpProcessor();
309    }
310
311    abstract void setUpProcessor();
312
313    protected void resetProcessor() {
314        handlers = null;
315    }
316
317    final public boolean isHandlerChainEmpty() {
318        return handlers.isEmpty();
319    }
320    abstract MessageUpdatableContext getContext(Packet p);
321
322    private boolean isHandleFault(Packet packet) {
323        if (cousinTube != null) {
324            return exchange.isHandleFault();
325        } else {
326            boolean isFault = packet.getMessage().isFault();
327            exchange.setHandleFault(isFault);
328            return isFault;
329        }
330    }
331
332    final void setHandleFault() {
333        exchange.setHandleFault(true);
334    }
335
336    private boolean isHandleFalse() {
337        return exchange.isHandleFalse();
338    }
339
340    final void setHandleFalse() {
341        exchange.setHandleFalse();
342    }
343
344    private void setupExchange() {
345        if(exchange == null) {
346            exchange = new HandlerTubeExchange();
347            if(cousinTube != null) {
348                cousinTube.exchange = exchange;
349            }
350        } else {
351            if(cousinTube != null) {
352                cousinTube.exchange = exchange;
353            }
354
355        }
356    }
357    private HandlerTubeExchange exchange;
358
359    /**
360     * This class is used primarily to exchange information or status between
361     * LogicalHandlerTube and SOAPHandlerTube
362     */
363    static final class HandlerTubeExchange {
364        private boolean handleFalse;
365        private boolean handleFault;
366
367        boolean isHandleFault() {
368            return handleFault;
369        }
370
371        void setHandleFault(boolean isFault) {
372            this.handleFault = isFault;
373        }
374
375        public boolean isHandleFalse() {
376            return handleFalse;
377        }
378
379        void setHandleFalse() {
380            this.handleFalse = true;
381        }
382    }
383
384}
385