001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *                               ***
008 *
009 *   Community License: GPL 3.0
010 *
011 *   This file is free software: you can redistribute it and/or modify
012 *   it under the terms of the GNU General Public License as published
013 *   by the Free Software Foundation, either version 3 of the License,
014 *   or (at your option) any later version.
015 *
016 *   This file is distributed in the hope that it will be useful, but
017 *   WITHOUT ANY WARRANTY; without even the implied warranty of
018 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019 *   GNU General Public License for more details.
020 *
021 *   You should have received a copy of the GNU General Public License
022 *   along with this program. If not, see <http://www.gnu.org/licenses/>.
023 *
024 *                               ***
025 *
026 *   Available Commercial License: GraniteDS SLA 1.0
027 *
028 *   This is the appropriate option if you are creating proprietary
029 *   applications and you are not prepared to distribute and share the
030 *   source code of your application under the GPL v3 license.
031 *
032 *   Please visit http://www.granitedataservices.com/license for more
033 *   details.
034 */
035package org.granite.client.tide.impl;
036
037import java.lang.reflect.ParameterizedType;
038import java.lang.reflect.Type;
039import java.util.ArrayList;
040import java.util.List;
041
042import org.granite.client.messaging.events.Event;
043import org.granite.client.messaging.events.IncomingMessageEvent;
044import org.granite.client.messaging.events.ResultEvent;
045import org.granite.client.tide.Context;
046import org.granite.client.tide.data.EntityManager;
047import org.granite.client.tide.data.spi.MergeContext;
048import org.granite.client.tide.server.ComponentListener;
049import org.granite.client.tide.server.ServerSession;
050import org.granite.client.tide.server.TideMergeResponder;
051import org.granite.client.tide.server.TideResponder;
052import org.granite.client.tide.server.TideResultEvent;
053import org.granite.logging.Logger;
054import org.granite.tide.invocation.InvocationResult;
055
056/**
057 * @author William DRAI
058 */
059public class ResultHandler<T> implements Runnable {
060
061        private final ServerSession serverSession;
062        private final Context sourceContext;
063        @SuppressWarnings("unused")
064        private final String componentName;
065        @SuppressWarnings("unused")
066        private final String operation;
067        private final Event event;
068        @SuppressWarnings("unused")
069        private final Object info;
070        private final TideResponder<T> tideResponder;
071        private final ComponentListener<T> componentListener;
072        private boolean executed = false;
073
074
075    public ResultHandler(ServerSession serverSession, String componentName, String operation) {
076        this.serverSession = serverSession;
077        this.sourceContext = null;
078        this.componentName = componentName;
079        this.operation = operation;
080        this.event = null;
081        this.info = null;
082        this.tideResponder = null;
083        this.componentListener = null;
084    }
085
086        public ResultHandler(ServerSession serverSession, Context sourceContext, String componentName, String operation,
087                        Event event, Object info, TideResponder<T> tideResponder, ComponentListener<T> componentListener) {
088                this.serverSession = serverSession;
089                this.sourceContext = sourceContext;
090                this.componentName = componentName;
091                this.operation = operation;
092                this.event = event;
093                this.info = info;
094                this.tideResponder = tideResponder;
095                this.componentListener = componentListener;
096        }
097        
098        @SuppressWarnings("unchecked")
099        public void run() {
100                if (executed)
101                        return;
102                executed = true;
103        InvocationResult invocationResult = null;
104        Object result = null; 
105        if (event instanceof ResultEvent)
106            result = ((ResultEvent)event).getResult();
107        else if (event instanceof IncomingMessageEvent<?>)
108            result = ((IncomingMessageEvent<?>)event).getMessage();
109        
110        if (result instanceof InvocationResult) {
111            invocationResult = (InvocationResult)result;
112            result = invocationResult.getResult();
113        }
114        
115        if (tideResponder != null) {
116                for (Type type : tideResponder.getClass().getGenericInterfaces()) {
117                        if (type instanceof ParameterizedType && ((ParameterizedType)type).getRawType().equals(TideResponder.class)) {
118                                Type expectedReturnType = ((ParameterizedType)type).getActualTypeArguments()[0];
119                                result = serverSession.convert(result, expectedReturnType);
120                                if (invocationResult != null)
121                                        invocationResult.setResult(result);
122                                break;
123                        }
124                }
125        }
126        
127//        var conversationId:String = null;
128//        if (event.message.headers[Tide.IS_LONG_RUNNING_CONVERSATION_TAG])
129//            conversationId = event.message.headers[Tide.CONVERSATION_TAG];
130//        var wasConversationCreated:Boolean = event.message.headers[Tide.WAS_LONG_RUNNING_CONVERSATION_CREATED_TAG] != null;
131//        var wasConversationEnded:Boolean = event.message.headers[Tide.WAS_LONG_RUNNING_CONVERSATION_ENDED_TAG] != null;
132//        
133//        var context:Context = _contextManager.retrieveContext(sourceContext, conversationId, wasConversationCreated, wasConversationEnded);           
134        
135        Context context = sourceContext.getContextManager().retrieveContext(sourceContext, null, false, false); // conversationId, wasConversationCreated, wasConversationEnded);
136        
137        serverSession.onResultEvent(event);
138        
139        handleResult(context, invocationResult, result,
140                tideResponder instanceof TideMergeResponder<?> ? ((TideMergeResponder<T>) tideResponder).getMergeResultWith() : null);
141        if (invocationResult != null)
142            result = invocationResult.getResult();
143        
144                boolean handled = false;
145        if (tideResponder != null) {
146            TideResultEvent<T> resultEvent = new TideResultEvent<T>(context, serverSession, componentListener, (T)result);
147            tideResponder.result(resultEvent);
148            if (resultEvent.isDefaultPrevented())
149                handled = true;
150        }
151        
152        componentListener.setResult((T)result);
153        
154//              context.clearData();
155//              
156//              // Should be after event result handling and responder: previous could trigger other remote calls
157//              if (context.isFinished())
158//                  context.scheduleDestroy();
159//              
160        if (!handled && !serverSession.isLogoutInProgress())
161            context.getEventBus().raiseEvent(context, ServerSession.CONTEXT_RESULT, event instanceof ResultEvent ? ((ResultEvent)event).getMessage() : null);
162
163        serverSession.tryLogout();
164    }
165
166
167    private static final Logger log = Logger.getLogger(ResultHandler.class);
168
169    public void handleResult(Context context, InvocationResult invocationResult, Object result, Object mergeWith) {
170        log.debug("result {0}", result);
171
172        List<EntityManager.Update> updates = null;
173        EntityManager entityManager = context.getEntityManager();
174
175        try {
176            // Clear flash context variable for Grails/Spring MVC
177            context.remove("flash");
178
179            MergeContext mergeContext = entityManager.initMerge();
180            mergeContext.setServerSession(serverSession);
181
182            boolean mergeExternal = true;
183            if (invocationResult != null) {
184                mergeExternal = invocationResult.getMerge();
185
186                if (invocationResult.getUpdates() != null && invocationResult.getUpdates().length > 0) {
187                    updates = new ArrayList<EntityManager.Update>(invocationResult.getUpdates().length);
188                    for (Object[] u : invocationResult.getUpdates())
189                        updates.add(EntityManager.Update.forUpdate((String) u[0], u[1]));
190                    entityManager.handleUpdates(mergeContext, null, updates);
191                }
192            }
193
194            // Merges final result object
195            if (result != null) {
196                if (mergeExternal)
197                    result = entityManager.mergeExternal(mergeContext, result, mergeWith, null, null, false);
198                else
199                    log.debug("skipped merge of remote result");
200                if (invocationResult != null)
201                    invocationResult.setResult(result);
202            }
203        }
204        finally {
205            MergeContext.destroy(entityManager);
206        }
207
208        // Dispatch received data update events
209        if (invocationResult != null) {
210            // Dispatch received data update events
211            if (updates != null)
212                entityManager.raiseUpdateEvents(context, updates);
213
214            // TODO: dispatch received context events
215//            List<ContextEvent> events = invocationResult.getEvents();
216//            if (events != null && events.size() > 0) {
217//                for (ContextEvent event : events) {
218//                    if (event.params[0] is Event)
219//                        meta_dispatchEvent(event.params[0] as Event);
220//                    else if (event.isTyped())
221//                        meta_internalRaiseEvent("$TideEvent$" + event.eventType, event.params);
222//                    else
223//                        _tide.invokeObservers(this, TideModuleContext.currentModulePrefix, event.eventType, event.params);
224//                }
225//            }
226        }
227
228        log.debug("result merged into local context");
229    }
230}