/**
* Filename: RemoteEntityHome.java (in org.repin.android.net.home)
* This file is part of the Redpin project.
*
* Redpin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Redpin 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Redpin. If not, see <http://www.gnu.org/licenses/>.
*
* (c) Copyright ETH Zurich, Pascal Brogle, Philipp Bolliger, 2010, ALL RIGHTS RESERVED.
*
* www.redpin.org
*/
package org.redpin.android.net.home;
import java.util.HashMap;
import org.redpin.android.net.PerformRequestTask;
import org.redpin.android.net.PerformRequestTaskCallback;
import org.redpin.android.net.Request;
import org.redpin.android.net.Response;
import org.redpin.android.net.Request.RequestType;
import org.redpin.android.net.Response.Status;
import android.os.AsyncTask;
import android.util.Log;
/**
* {@link RemoteEntityHome} handles request to server and calls the responsible
* Entity RemoteEntityHomes to synchronize the local database after the request
* was performed
*
* @author Pascal Brogle (broglep@student.ethz.ch)
*
*/
public class RemoteEntityHome implements PerformRequestTaskCallback {
protected static RemoteEntityHome instance = new RemoteEntityHome();
protected HashMap<PerformRequestTask, Request<?>> pending = new HashMap<PerformRequestTask, Request<?>>();
protected HashMap<Request<?>, Integer> retryCount = new HashMap<Request<?>, Integer>();
protected HashMap<Request<?>, RemoteEntityHomeCallback> callbacks = new HashMap<Request<?>, RemoteEntityHomeCallback>();
private static final String TAG = RemoteEntityHome.class.getName();
public static final Integer MAX_TRIES = 3;
/**
* Performs an server request. This method must be invoked on the UI thread.
*
* @param action
* Action to be performed
* @param callback
* {@link RemoteEntityHomeCallback}
*/
public static void performRequest(RequestType action,
RemoteEntityHomeCallback callback) {
performRequest(action, null, callback);
}
/**
* Performs an server request . This method must be invoked on the UI
* thread.
*
* @param action
* Action to be performed
*/
public static void performRequest(RequestType action) {
performRequest(action, null);
}
/**
* Performs an server request. This method must be invoked on the UI thread.
*
* @param <D>
* Data type the request contains
* @param action
* Action to be performed
* @param data
* Data to be submitted with the request
*/
public static <D> void performRequest(RequestType action, D data) {
performRequest(action, data, null);
}
/**
* Performs an server request. This method must be invoked on the UI thread
* (as it invokes {@link AsyncTask#execute(Object...)}
*
* @param <D>
* Data type the request contains
* @param action
* Action to be performed
* @param data
* Data to be submitted with the request
* @param callback
* {@link RemoteEntityHomeCallback} to be called after the
* request
*/
public static <D> void performRequest(RequestType action, D data,
RemoteEntityHomeCallback callback) {
PerformRequestTask task;
Request<D> request = new Request<D>(action, data);
task = new PerformRequestTask(instance);
startTask(task, request, callback);
}
/**
* Starts an asynchronous server request and adds it to the pending list.
*
* @param task
* {@link PerformRequestTask} to be started
* @param request
* {@link Request} to be performed
* @param callback
* {@link RemoteEntityHomeCallback}
*/
protected static void startTask(PerformRequestTask task,
Request<?> request, RemoteEntityHomeCallback callback) {
instance.pending.put(task, request);
if (callback != null) {
instance.callbacks.put(request, callback);
}
task.execute(request);
}
/**
* Removes a task form the pending list
*
* @param task
* {@link PerformRequestTask} to be removed
*/
protected static void removeTask(PerformRequestTask task) {
Request<?> r = instance.pending.remove(task);
if (r == null) {
Log.e(TAG, "removeTask tried to remove task which was not present");
return;
}
instance.callbacks.remove(r);
}
/**
* Restarts a failed server request.
*
* @param task
* {@link PerformRequestTask} to be restarted
*/
protected static void restartTask(PerformRequestTask task) {
Request<?> r = instance.pending.remove(task);
if (r == null) {
Log
.e(TAG,
"restartTask tried to remove task which was not present");
return;
}
PerformRequestTask newTask = new PerformRequestTask(task);
instance.pending.put(newTask, r);
newTask.execute(r);
}
private static MapRemoteHome mapHome;
private static LocationRemoteHome locationHome;
private static FingerprintRemoteHome fingerprintHome;
/**
* Returns the Entity RemoteEntityHome that is responsible for the request.
*
* @param request
* {@link Request}
* @return RemoteEntityHome for specific request
*/
public static IRemoteEntityHome getRemoteEntityHome(Request<?> request) {
return getRemoteEntityHome(request.getAction());
}
/**
* Returns the Entity RemoteEntityHome that is responsible for an action.
*
* @param type
* Action
* @return RemoteEntityHome for specific request type
*/
public static IRemoteEntityHome getRemoteEntityHome(RequestType type) {
switch (type) {
case setMap:
case getMapList:
case removeMap:
if (mapHome == null) {
mapHome = new MapRemoteHome();
}
return mapHome;
case getLocation:
case getLocationList:
case removeLocation:
case updateLocation:
if (locationHome == null) {
locationHome = new LocationRemoteHome();
}
return locationHome;
case setFingerprint:
if (fingerprintHome == null) {
fingerprintHome = new FingerprintRemoteHome();
}
return fingerprintHome;
default:
throw new IllegalArgumentException("No RemoteEntityHome for type "
+ type);
}
}
/**
*
* @param response
* {@link Response} received from the server
* @return <code>true</code> if request was successful
*/
private static boolean isSuccess(Response<?> response) {
return response.getStatus() == Status.ok;
}
/**
* Hands over the response to the responsible Entity RemoteEntityHome.
*
* @see PerformRequestTaskCallback#onPerformedBackground(Request, Response,
* PerformRequestTask)
*/
public void onPerformedBackground(Request<?> request, Response<?> response,
PerformRequestTask task) {
if (isSuccess(response)) {
getRemoteEntityHome(request.getAction()).onRequestPerformed(
request, response, this);
}
}
/**
* Calls the {@link RemoteEntityHomeCallback} if request was successful,
* otherwise retries to perform the request.
*
* @see PerformRequestTaskCallback#onPerformedForeground(Request, Response,
* PerformRequestTask)
*/
public void onPerformedForeground(Request<?> request, Response<?> response,
PerformRequestTask task) {
RemoteEntityHomeCallback cb = callbacks.get(request);
if (isSuccess(response)) {
if (cb != null) {
cb.onResponse(response);
}
} else {
Integer i = retryCount.get(request);
if (i != null) {
i++;
} else {
i = 1;
}
if (i < MAX_TRIES) {
retryCount.put(request, i);
restartTask(task);
return;
} else {
retryCount.remove(request);
if (cb != null) {
cb.onFailure(response);
}
}
}
removeTask(task);
}
/**
* Retries to perform the canceled request
*
* @see PerformRequestTaskCallback#onCanceledForeground(Request,
* PerformRequestTask)
*/
public void onCanceledForeground(Request<?> request, PerformRequestTask task) {
restartTask(task);
}
/*
* For testing only TODO: remove
*/
// TODO: remove
public static HashMap<PerformRequestTask, Request<?>> getPending() {
return instance.pending;
}
// TODO: remove
public static HashMap<Request<?>, RemoteEntityHomeCallback> getCallbacks() {
return instance.callbacks;
}
// TODO: remove
public static HashMap<Request<?>, Integer> getRetryCount() {
return instance.retryCount;
}
// TODO: remove
public static void clear() {
instance.pending.clear();
instance.retryCount.clear();
instance.callbacks.clear();
}
// TODO: remove
public static RemoteEntityHome getInstance() {
return instance;
}
}