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}