001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2012 GRANITE DATA SERVICES S.A.S.
004    
005      This file is part of Granite Data Services.
006    
007      Granite Data Services is free software; you can redistribute it and/or modify
008      it under the terms of the GNU Library General Public License as published by
009      the Free Software Foundation; either version 2 of the License, or (at your
010      option) any later version.
011    
012      Granite Data Services is distributed in the hope that it will be useful, but
013      WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014      FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015      for more details.
016    
017      You should have received a copy of the GNU Library General Public License
018      along with this library; if not, see <http://www.gnu.org/licenses/>.
019    */
020    
021    package org.granite.client.tide.impl;
022    
023    import java.util.Map;
024    
025    import org.granite.client.messaging.events.FaultEvent;
026    import org.granite.client.messaging.messages.responses.FaultMessage;
027    import org.granite.client.tide.Context;
028    import org.granite.client.tide.server.ComponentListener;
029    import org.granite.client.tide.server.ExceptionHandler;
030    import org.granite.client.tide.server.Fault;
031    import org.granite.client.tide.server.ServerSession;
032    import org.granite.client.tide.server.TideFaultEvent;
033    import org.granite.client.tide.server.TideResponder;
034    import org.granite.logging.Logger;
035    
036    /**
037     *  @private
038     *  Implementation of fault handler
039     *  
040     *  @param sourceContext source context of remote call
041     *  @param info fault object
042     *  @param componentName component name
043     *  @param op remote operation
044     *  @param tideResponder Tide responder for the remote call
045     *  
046     * @author William DRAI
047     */
048    public class FaultHandler<T> implements Runnable {
049            
050            private static final Logger log = Logger.getLogger(FaultHandler.class);
051            
052            private final ServerSession serverSession;
053            private final Context sourceContext;
054            private final String componentName;
055            private final String operation;
056            private final FaultEvent event;
057            @SuppressWarnings("unused")
058            private final Object info;
059            private final TideResponder<T> tideResponder;
060            private final ComponentListener<T> componentResponder;
061            private boolean executed = false;
062            
063            
064            public FaultHandler(ServerSession serverSession, Context sourceContext, String componentName, String operation, FaultEvent event, Object info, 
065                            TideResponder<T> tideResponder, ComponentListener<T> componentResponder) {
066                    this.serverSession = serverSession;
067                    this.sourceContext = sourceContext;
068                    this.componentName = componentName;
069                    this.operation = operation;
070                    this.event = event;
071                    this.info = info;
072                    this.tideResponder = tideResponder;
073                    this.componentResponder = componentResponder;
074            }
075    
076            public void run() {
077                    if (executed)
078                            return;
079                    executed = true;
080                    
081            log.error("fault %s", event.toString());
082           
083            // TODO: conversation contexts
084    //        var sessionId:String = faultEvent.message.headers[Tide.SESSION_ID_TAG];
085    //        var conversationId:String = null;
086    //        if (faultEvent.message.headers[Tide.IS_LONG_RUNNING_CONVERSATION_TAG])
087    //            conversationId = faultEvent.message.headers[Tide.CONVERSATION_TAG];
088    //        var wasConversationCreated:Boolean = faultEvent.message.headers[Tide.WAS_LONG_RUNNING_CONVERSATION_CREATED_TAG] != null;
089    //        var wasConversationEnded:Boolean = faultEvent.message.headers[Tide.WAS_LONG_RUNNING_CONVERSATION_ENDED_TAG] != null;
090    //        
091    //        var context:Context = _contextManager.retrieveContext(sourceContext, conversationId, wasConversationCreated, wasConversationEnded);
092            
093            Context context = sourceContext.getContextManager().retrieveContext(sourceContext, null, false, false);
094            
095            FaultMessage emsg = event.getMessage();
096            FaultMessage m = emsg;
097            Map<String, Object> extendedData = emsg != null ? emsg.getExtended() : null;
098            do {
099                if (m != null && m.getCode() != null && m.isSecurityFault()) {
100                    emsg = m;
101                    extendedData = emsg != null ? emsg.getExtended() : null;
102                    break;
103                }
104                // TODO: check WTF we should do here
105                if (m != null && m.getCause() instanceof FaultEvent)
106                    m = (FaultMessage)((FaultEvent)m.getCause()).getCause();
107                else if (m.getCause() instanceof FaultMessage)
108                    m = (FaultMessage)m.getCause();
109                else
110                    m = null;
111            }
112            while (m != null);
113            
114            serverSession.handleFaultEvent(event, emsg);
115            
116            serverSession.handleFault(context, componentName, operation, emsg);
117            
118            boolean handled = false;
119            Fault fault = new Fault(emsg.getCode(), emsg.getDescription(), emsg.getDetails());
120            fault.setContent(event.getMessage());
121            fault.setCause(event.getCause());
122            
123            TideFaultEvent faultEvent = new TideFaultEvent(context, serverSession, componentResponder, fault, extendedData);
124            if (tideResponder != null) {
125                tideResponder.fault(faultEvent);
126                if (faultEvent.isDefaultPrevented())
127                    handled = true;
128            }
129            
130            if (!handled) {
131                ExceptionHandler[] exceptionHandlers = context.getContextManager().getContext(null).allByType(ExceptionHandler.class);
132                if (exceptionHandlers != null && emsg != null) {
133                    // Lookup for a suitable exception handler
134                    for (ExceptionHandler handler : exceptionHandlers) {
135                        if (handler.accepts(emsg)) {
136                            handler.handle(context, emsg, faultEvent);
137                            handled = true;
138                            break;
139                        }
140                    }
141                    if (!handled)
142                        log.error("Unhandled fault: " + emsg.getCode() + ": " + emsg.getDescription());
143                }
144                else if (exceptionHandlers != null && exceptionHandlers.length > 0 && event.getMessage() instanceof FaultMessage) {
145                    // Handle fault with default exception handler
146                    exceptionHandlers[0].handle(context, (FaultMessage)event.getMessage(), faultEvent);
147                }
148                else {
149                    log.error("Unknown fault: " + event.toString());
150                }
151            }
152            
153            if (!handled && !serverSession.isLogoutInProgress())
154                    context.getEventBus().raiseEvent(context, ServerSession.CONTEXT_FAULT, event.getMessage());
155            
156            serverSession.tryLogout();
157        }
158    }