/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* ***
*
* Community License: GPL 3.0
*
* This file is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* ***
*
* Available Commercial License: GraniteDS SLA 1.0
*
* This is the appropriate option if you are creating proprietary
* applications and you are not prepared to distribute and share the
* source code of your application under the GPL v3 license.
*
* Please visit http://www.granitedataservices.com/license for more
* details.
*/
package org.granite.client.tide.impl;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import org.granite.client.messaging.events.Event;
import org.granite.client.messaging.events.IncomingMessageEvent;
import org.granite.client.messaging.events.ResultEvent;
import org.granite.client.tide.Context;
import org.granite.client.tide.data.EntityManager;
import org.granite.client.tide.data.EntityManager.UpdateKind;
import org.granite.client.tide.data.impl.ChangeEntity;
import org.granite.client.tide.data.impl.ChangeEntityRef;
import org.granite.client.tide.data.spi.MergeContext;
import org.granite.client.tide.server.ComponentListener;
import org.granite.client.tide.server.ServerSession;
import org.granite.client.tide.server.TideMergeResponder;
import org.granite.client.tide.server.TideResponder;
import org.granite.client.tide.server.TideResultEvent;
import org.granite.logging.Logger;
import org.granite.tide.data.Change;
import org.granite.tide.data.ChangeRef;
import org.granite.tide.invocation.InvocationResult;
/**
* @author William DRAI
*/
public class ResultHandler<T> implements Runnable {
private final ServerSession serverSession;
private final Context sourceContext;
@SuppressWarnings("unused")
private final String componentName;
@SuppressWarnings("unused")
private final String operation;
private final Event event;
@SuppressWarnings("unused")
private final Object info;
private final TideResponder<T> tideResponder;
private final ComponentListener<T> componentListener;
private boolean executed = false;
public ResultHandler(ServerSession serverSession, String componentName, String operation) {
this.serverSession = serverSession;
this.sourceContext = null;
this.componentName = componentName;
this.operation = operation;
this.event = null;
this.info = null;
this.tideResponder = null;
this.componentListener = null;
}
public ResultHandler(ServerSession serverSession, Event event) {
this.serverSession = serverSession;
this.sourceContext = null;
this.componentName = null;
this.operation = null;
this.event = event;
this.info = null;
this.tideResponder = null;
this.componentListener = null;
}
public ResultHandler(ServerSession serverSession, Context sourceContext, String componentName, String operation,
Event event, Object info, TideResponder<T> tideResponder, ComponentListener<T> componentListener) {
this.serverSession = serverSession;
this.sourceContext = sourceContext;
this.componentName = componentName;
this.operation = operation;
this.event = event;
this.info = info;
this.tideResponder = tideResponder;
this.componentListener = componentListener;
}
@SuppressWarnings("unchecked")
public void run() {
if (executed)
return;
executed = true;
InvocationResult invocationResult = null;
Object result = null;
if (event instanceof ResultEvent)
result = ((ResultEvent)event).getResult();
else if (event instanceof IncomingMessageEvent<?>)
result = ((IncomingMessageEvent<?>)event).getMessage();
if (result instanceof InvocationResult) {
invocationResult = (InvocationResult)result;
result = invocationResult.getResult();
}
if (tideResponder != null) {
for (Type type : tideResponder.getClass().getGenericInterfaces()) {
if (type instanceof ParameterizedType && ((ParameterizedType)type).getRawType().equals(TideResponder.class)) {
Type expectedReturnType = ((ParameterizedType)type).getActualTypeArguments()[0];
result = serverSession.convert(result, expectedReturnType);
if (invocationResult != null)
invocationResult.setResult(result);
break;
}
}
}
// var conversationId:String = null;
// if (event.message.headers[Tide.IS_LONG_RUNNING_CONVERSATION_TAG])
// conversationId = event.message.headers[Tide.CONVERSATION_TAG];
// var wasConversationCreated:Boolean = event.message.headers[Tide.WAS_LONG_RUNNING_CONVERSATION_CREATED_TAG] != null;
// var wasConversationEnded:Boolean = event.message.headers[Tide.WAS_LONG_RUNNING_CONVERSATION_ENDED_TAG] != null;
//
// var context:Context = _contextManager.retrieveContext(sourceContext, conversationId, wasConversationCreated, wasConversationEnded);
Context context = sourceContext.getContextManager().retrieveContext(sourceContext, null, false, false); // conversationId, wasConversationCreated, wasConversationEnded);
serverSession.onResultEvent(event);
boolean handled = handleResult(context, invocationResult, result);
if (invocationResult != null)
result = invocationResult.getResult();
componentListener.setResult((T)result);
// context.clearData();
//
// // Should be after event result handling and responder: previous could trigger other remote calls
// if (context.isFinished())
// context.scheduleDestroy();
//
if (!handled && !serverSession.isLogoutInProgress())
context.getEventBus().raiseEvent(context, ServerSession.CONTEXT_RESULT, event instanceof ResultEvent ? ((ResultEvent)event).getMessage() : null);
serverSession.tryLogout();
}
private static final Logger log = Logger.getLogger(ResultHandler.class);
public boolean handleResult(Context context, InvocationResult invocationResult, Object result) {
log.debug("result %s", result);
List<EntityManager.Update> updates = null;
EntityManager entityManager = context.getEntityManager();
try {
// Clear flash context variable for Grails/Spring MVC
context.remove("flash");
MergeContext mergeContext = entityManager.initMerge(serverSession);
boolean mergeExternal = true;
if (invocationResult != null) {
mergeExternal = invocationResult.getMerge();
if (invocationResult.getUpdates() != null && invocationResult.getUpdates().length > 0) {
updates = new ArrayList<EntityManager.Update>(invocationResult.getUpdates().length);
for (Object[] update : invocationResult.getUpdates()) {
String updateType = update[0].toString().toUpperCase();
Object entity = update[1];
if (UpdateKind.REFRESH.toString().toLowerCase().equals(updateType) && entity instanceof String)
entity = serverSession.getAliasRegistry().getAliasForType((String)entity);
else if (entity instanceof Change)
entity = new ChangeEntity((Change)entity, serverSession.getAliasRegistry());
else if (entity instanceof ChangeRef)
entity = new ChangeEntityRef(entity, serverSession.getAliasRegistry());
updates.add(EntityManager.Update.forUpdate(updateType, entity));
}
entityManager.handleUpdates(mergeContext, null, updates);
}
}
// Merges final result object
if (result != null) {
if (mergeExternal) {
Object mergeWith = tideResponder instanceof TideMergeResponder<?> ? ((TideMergeResponder<T>)tideResponder).getMergeResultWith() : null;
result = entityManager.mergeExternal(mergeContext, result, mergeWith, null, null, false);
}
else
log.debug("skipped merge of remote result");
if (invocationResult != null)
invocationResult.setResult(result);
}
}
finally {
MergeContext.destroy(entityManager);
}
// Dispatch received data update events
if (invocationResult != null) {
// Dispatch received data update events
if (updates != null)
entityManager.raiseUpdateEvents(context, updates);
// TODO: dispatch received context events
// List<ContextEvent> events = invocationResult.getEvents();
// if (events != null && events.size() > 0) {
// for (ContextEvent event : events) {
// if (event.params[0] is Event)
// meta_dispatchEvent(event.params[0] as Event);
// else if (event.isTyped())
// meta_internalRaiseEvent("$TideEvent$" + event.eventType, event.params);
// else
// _tide.invokeObservers(this, TideModuleContext.currentModulePrefix, event.eventType, event.params);
// }
// }
}
log.debug("result merged into local context");
if (invocationResult != null)
result = invocationResult.getResult();
boolean handled = false;
if (tideResponder != null) {
@SuppressWarnings("unchecked")
TideResultEvent<T> resultEvent = new TideResultEvent<T>(context, serverSession, componentListener, (T)result);
tideResponder.result(resultEvent);
if (resultEvent.isDefaultPrevented())
handled = true;
}
return handled;
}
}