package org.activityinfo.ui.client.local.command;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program 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 program 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/gpl-3.0.html>.
* #L%
*/
import com.bedatadriven.rebar.async.ChainedCallback;
import com.bedatadriven.rebar.sql.client.SqlDatabase;
import com.bedatadriven.rebar.sql.client.SqlException;
import com.bedatadriven.rebar.sql.client.SqlTransaction;
import com.bedatadriven.rebar.sql.client.SqlTransactionCallback;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import org.activityinfo.legacy.client.Dispatcher;
import org.activityinfo.legacy.client.remote.AbstractDispatcher;
import org.activityinfo.legacy.client.remote.Remote;
import org.activityinfo.legacy.shared.Log;
import org.activityinfo.legacy.shared.command.Command;
import org.activityinfo.legacy.shared.command.MutatingCommand;
import org.activityinfo.legacy.shared.command.result.CommandResult;
import org.activityinfo.legacy.shared.util.Collector;
import org.activityinfo.model.auth.AuthenticatedUser;
import org.activityinfo.ui.client.EventBus;
import org.activityinfo.ui.client.local.sync.ServerStateChangeEvent;
/**
* Dispatches commands to local handlers
*/
public class LocalDispatcher extends AbstractDispatcher {
private final AuthenticatedUser auth;
private final HandlerRegistry registry;
private final SqlDatabase database;
private final CommandQueue commandQueue;
private final Dispatcher remoteDispatcher;
private final EventBus eventBus;
@Inject
public LocalDispatcher(EventBus eventBus,
AuthenticatedUser auth,
SqlDatabase database,
HandlerRegistry registry,
@Remote Dispatcher remoteDispatcher,
CommandQueue commandQueue) {
Log.trace("LocalDispatcher constructor starting...");
this.eventBus = eventBus;
this.auth = auth;
this.registry = registry;
this.database = database;
this.commandQueue = commandQueue;
this.remoteDispatcher = remoteDispatcher;
}
@Override
public <R extends CommandResult> void execute(Command<R> command, final AsyncCallback<R> callback) {
if (registry.hasHandler(command)) {
executeOffline(command, callback);
} else {
executeRemotely(command, callback);
}
}
/**
* Begins a new transaction, initializes a new ExecutionContext, and
* executes the root command.
*
* @param command
* @param callback
*/
private <R extends CommandResult> void executeOffline(final Command<R> command, final AsyncCallback<R> callback) {
Log.debug("Executing command " + command + " OFFLINE.");
try {
final Collector<R> commandResult = Collector.newCollector();
database.transaction(new SqlTransactionCallback() {
@Override
public void begin(SqlTransaction tx) {
LocalExecutionContext context = new LocalExecutionContext(auth, tx, registry, commandQueue);
context.execute(command, commandResult);
}
@Override
public void onError(SqlException e) {
Log.error("OFFLINE EXECUTION FAILED: " + command, e);
callback.onFailure(e);
}
@Override
public void onSuccess() {
callback.onSuccess(commandResult.getResult());
}
});
} catch (Exception e) {
callback.onFailure(e);
}
}
private <R extends CommandResult> void executeRemotely(final Command<R> command, final AsyncCallback<R> callback) {
Log.debug("No handler for " + command + ", executing remotely.");
remoteDispatcher.execute(command, new ChainedCallback<R>(callback) {
@Override
public void onSuccess(R result) {
if (command instanceof MutatingCommand) {
// This will lead to an inconstent state where the user's
// local copy of the database is out of sync
// this will disappear as we implement more functionality
// offline
// but in the meantime we need to trigger synchronization
// and
// let the user they should expect some screwy stuff.
if (command instanceof MutatingCommand) {
eventBus.fireEvent(new ServerStateChangeEvent());
}
}
callback.onSuccess(result);
}
});
}
}