revert "unsent messages into SQLite DB" - will be included later

This commit is contained in:
Peter Siegmund committed 2014-11-12 08:39:36 +01:00
1 parent 7edab447bd
commit 75b49650fd
2 files changed
+159 -90

No files matched your search

@@ -1,36 +1,41 @@
package com.sap.sailing.android.shared.services.sending;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.provider.AnalyticsContract;
import com.sap.sailing.android.shared.util.FileHandlerUtils;
public class MessagePersistenceManager {
private final static String TAG = MessagePersistenceManager.class.getName();
protected Context context;
protected ContentResolver resolver;
private final static String delayedMessagesFileName = "delayedMessages.txt";
private final MessageRestorer restorer;
protected Context context;
protected List<String> persistedMessages;
private final MessageRestorer messageRestorer;
public MessagePersistenceManager(Context context, MessageRestorer messageRestorer) {
this.context = context;
this.restorer = messageRestorer;
this.resolver = context.getContentResolver();
persistedMessages = new ArrayList<String>();
this.messageRestorer = messageRestorer;
initializeFileAndPersistedMessages();
}
public boolean areIntentsDelayed() {
return getMessageCount() != 0;
return !persistedMessages.isEmpty();
}
public void persistIntent(Intent intent) {
@@ -43,15 +48,23 @@ public class MessagePersistenceManager {
}
private void persistMessage(String url, String callbackPayload, String payload, String callbackClass) {
ContentValues values = new ContentValues();
values.put(AnalyticsContract.Message.MESSAGE_URL, url);
values.put(AnalyticsContract.Message.MESSAGE_CALLBACK_PAYLOAD, callbackPayload);
values.put(AnalyticsContract.Message.MESSAGE_PAYLOAD, payload);
values.put(AnalyticsContract.Message.MESSAGE_CALLBACK_CLASS_STRING, callbackClass);
Uri result = resolver.insert(AnalyticsContract.Message.CONTENT_URI, values);
if (result != null) {
ExLog.i(context, TAG, "Message saved.");
String messageLine = getSerializedIntentForPersistence(url, callbackPayload, payload, callbackClass);
ExLog.i(context, TAG, String.format("Persisting message \"%s\".", messageLine));
if (persistedMessages.contains(messageLine)) {
ExLog.i(context, TAG, "The message already exists. Ignoring.");
return;
}
saveMessage(messageLine);
}
/**
* @param payload will be URL-encoded to ensure that the resulting string does not contain newlines
*/
private String getSerializedIntentForPersistence(String url, String callbackPayload,
String payload, String callbackClass) {
String messageLine = String.format("%s;%s;%s;%s", callbackPayload, URLEncoder.encode(payload),
url, callbackClass);
return messageLine;
}
public void removeIntent(Intent intent) {
@@ -64,35 +77,35 @@ public class MessagePersistenceManager {
}
private void removeMessage(String url, String callbackPayload, String payload, String callbackClass) {
if (!persistedMessages.isEmpty()) {
ExLog.i(context, TAG, String.format("Removing message \"%s\".", payload));
String where = AnalyticsContract.Message.MESSAGE_URL + " = ? AND "
+ AnalyticsContract.Message.MESSAGE_CALLBACK_PAYLOAD + " = ? AND "
+ AnalyticsContract.Message.MESSAGE_PAYLOAD + " = ? AND "
+ AnalyticsContract.Message.MESSAGE_CALLBACK_CLASS_STRING + " = ?";
int count = resolver.delete(AnalyticsContract.Message.CONTENT_URI, where, new String[] { url, callbackPayload,
payload, callbackClass });
if (count != 0) {
String messageLine = getSerializedIntentForPersistence(url, callbackPayload, payload, callbackClass);
removePersistedMessage(messageLine);
}
}
/**
* Removes all pending messages and clears the persistence file.
*/
public synchronized void removeAllMessages() {
persistedMessages.clear();
writePersistedMessagesToFile();
}
private void removePersistedMessage(String messageLine) {
if (persistedMessages.contains(messageLine)) {
persistedMessages.remove(messageLine);
writePersistedMessagesToFile();
ExLog.i(context, TAG, "Message removed.");
}
}
public synchronized void removeAllMessages() {
resolver.delete(AnalyticsContract.Message.CONTENT_URI, null, null);
}
public int getMessageCount() {
int result = 0;
Cursor cursor = resolver.query(AnalyticsContract.Message.CONTENT_URI, null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
result = cursor.getCount();
cursor.close();
}
return result;
return persistedMessages.size();
}
public Cursor getContent() {
return resolver.query(AnalyticsContract.Message.CONTENT_URI, null, null, null, null);
public List<String> getContent() {
return persistedMessages;
}
public static interface MessageRestorer {
@@ -101,22 +114,19 @@ public class MessagePersistenceManager {
public List<Intent> restoreMessages() {
List<Intent> delayedIntents = new ArrayList<Intent>();
Cursor cursor = resolver.query(AnalyticsContract.Message.CONTENT_URI, null, null, null, null);
if (cursor != null) {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
String url = cursor.getString(cursor.getColumnIndex(AnalyticsContract.Message.MESSAGE_URL));
String callbackPayload = cursor.getString(cursor
.getColumnIndex(AnalyticsContract.Message.MESSAGE_CALLBACK_PAYLOAD));
String payload = cursor.getString(cursor.getColumnIndex(AnalyticsContract.Message.MESSAGE_PAYLOAD));
String callbackClassString = cursor.getString(cursor
.getColumnIndex(AnalyticsContract.Message.MESSAGE_CALLBACK_CLASS_STRING));
for (String persistedMessage : persistedMessages) {
String[] lineParts = persistedMessage.split(";");
String url = lineParts[2];
String callbackPayload = lineParts[0];
String payload = URLDecoder.decode(lineParts[1]);
String callbackClassString = lineParts[3];
Class<? extends ServerReplyCallback> callbackClass = null;
if (callbackClassString != null) {
if (! "null".equals(callbackClassString)) {
try {
@SuppressWarnings("unchecked")
Class<? extends ServerReplyCallback> tmp = (Class<? extends ServerReplyCallback>) Class
.forName(callbackClassString);
Class<? extends ServerReplyCallback> tmp =
(Class<? extends ServerReplyCallback>) Class.forName(callbackClassString);
callbackClass = tmp;
} catch (ClassNotFoundException e) {
ExLog.e(context, TAG, "Could not find class for callback name: " + callbackClassString);
@@ -125,20 +135,84 @@ public class MessagePersistenceManager {
// We are passing no message id, because we know it used to suppress message sending and
// we want this message to be sent.
Intent messageIntent = MessageSendingService.createMessageIntent(context, url, callbackPayload, null,
payload, callbackClass);
Intent messageIntent = MessageSendingService.createMessageIntent(context, url, callbackPayload,
null, payload, callbackClass);
if (restorer != null) {
restorer.restoreMessage(context, messageIntent);
if (messageRestorer != null) {
messageRestorer.restoreMessage(context, messageIntent);
}
if (messageIntent != null) {
delayedIntents.add(messageIntent);
}
}
cursor.close();
}
ExLog.i(context, TAG, "Restored " + delayedIntents.size() + " messages");
return delayedIntents;
}
/**
* @return
* @throws FileNotFoundException
*/
private String getFileContent() throws FileNotFoundException {
String fileContent = "";
FileInputStream inputStream;
try {
inputStream = context.openFileInput(delayedMessagesFileName);
fileContent = FileHandlerUtils.convertStreamToString(inputStream, context);
inputStream.close();
} catch (IOException e) {
ExLog.w(context, TAG, "In Method getFileContent(): " + e.getMessage() + " fileContent is empty");
}
return fileContent;
}
private void saveMessage(String messageLine) {
persistedMessages.add(messageLine);
writePersistedMessagesToFile();
ExLog.i(context, TAG, "Wrote message to file: " + messageLine);
}
private void writePersistedMessagesToFile() {
StringBuilder newFileContent = new StringBuilder();
for (String persistedMessage : persistedMessages) {
newFileContent.append(persistedMessage);
newFileContent.append('\n');
}
writeToFile(newFileContent.toString(), Context.MODE_PRIVATE);
ExLog.i(context, TAG, "Wrote file content to file: " + newFileContent);
}
private void initializeFileAndPersistedMessages() {
try {
String fileContent = getFileContent();
String[] messageLines = fileContent.split("\n");
for (String messageLine : messageLines) {
if (!messageLine.isEmpty()) {
persistedMessages.add(messageLine);
}
}
} catch (FileNotFoundException e) {
ExLog.w(context, TAG, "persistence file not found in internal storage. The file will be created.");
clearPersistedMessages();
}
ExLog.i(context, TAG, "Initialized file");
}
private void clearPersistedMessages() {
persistedMessages.clear();
writePersistedMessagesToFile();
}
private void writeToFile(String content, int mode) {
try {
FileOutputStream outputStream = context.openFileOutput(delayedMessagesFileName, mode);
outputStream.write(content.getBytes());
outputStream.close();
} catch (IOException e) {
ExLog.e(context, TAG, "In Method writeToFile: " + e.getMessage() + " with content " + content + " and mode " + mode);
}
}
}
@@ -15,7 +15,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Binder;
@@ -31,34 +30,30 @@ import com.sap.sailing.android.shared.util.PrefUtils;
import com.sap.sailing.domain.common.racelog.RaceLogServletConstants;
/**
* Service that handles sending messages to a webservice. Deals with an offline setting by buffering the messages in a
* file, so that they can be sent when the connection is re-established.<br>
* Service that handles sending messages to a webservice. Deals with an offline setting
* by buffering the messages in a file, so that they can be sent when the connection is
* re-established.<br>
*
* <b>Use in the following way:</b> Add the service declaration to your {@code AndroidManifest.xml}, and also specify
* your class implementing the {@link MessagePersistenceManager.MessageRestorer} as a meta-data tag with the key
* {@code com.sap.sailing.android.shared.services.sending.messageRestorer}. Also refer to
* {@link ConnectivityChangedReceiver}, which has to be registered as well. For example:
*
* <pre>
* {@code
* <b>Use in the following way:</b> Add the service declaration to your {@code AndroidManifest.xml},
* and also specify your class implementing the {@link MessagePersistenceManager.MessageRestorer}
* as a meta-data tag with the key {@code com.sap.sailing.android.shared.services.sending.messageRestorer}.
* Also refer to {@link ConnectivityChangedReceiver}, which has to be registered as well.
* For example:
* <pre>{@code
* <service
* android:name="com.sap.sailing.android.shared.services.sending.MessageSendingService"
* android:exported="false" >
* <meta-data android:name="com.sap.sailing.android.shared.services.sending.messageRestorer"
* android:value="com.sap.sailing.racecommittee.app.services.sending.EventRestorer" />
* </service>
* }
* </pre>
* }</pre>
*
*
* Message sending example:
*
* <pre>
* {@code
* <pre>{@code
* context.startService(MessageSendingService.createMessageIntent(
* context, url, race.getId(), eventId, serializedEventAsJson, callbackClass));
* }
* </pre>
* }</pre>
*/
public class MessageSendingService extends Service implements MessageSendingListener {
public final static String URL = "url";
@@ -114,7 +109,7 @@ public class MessageSendingService extends Service implements MessageSendingList
private Date lastSuccessfulSend;
public Cursor getDelayedIntensContent() {
public List<String> getDelayedIntensContent() {
return persistenceManager.getContent();
}
@@ -132,8 +127,8 @@ public class MessageSendingService extends Service implements MessageSendingList
serviceLogger.onMessageSentSuccessful();
}
public static Intent createMessageIntent(Context context, String url, Serializable callbackPayload,
Serializable messageId, String payload, Class<? extends ServerReplyCallback> callbackClass) {
public static Intent createMessageIntent(Context context, String url, Serializable callbackPayload, Serializable messageId, String payload,
Class<? extends ServerReplyCallback> callbackClass) {
Intent messageIntent = new Intent(context, MessageSendingService.class);
messageIntent.setAction(context.getString(R.string.intent_send_message));
messageIntent.putExtra(CALLBACK_PAYLOAD, callbackPayload);
@@ -166,11 +161,10 @@ public class MessageSendingService extends Service implements MessageSendingList
Bundle data = getPackageManager().getServiceInfo(thisService, PackageManager.GET_META_DATA).metaData;
String className = data.getString("com.sap.sailing.android.shared.services.sending.messageRestorer");
Class<?> clazz = Class.forName(className);
if (!MessageRestorer.class.isAssignableFrom(clazz)) {
if (! MessageRestorer.class.isAssignableFrom(clazz)) {
throw new Exception("Class does not conform to expected type.");
}
@SuppressWarnings("unchecked")
// checked above
@SuppressWarnings("unchecked") //checked above
Class<MessageRestorer> castedClass = (Class<MessageRestorer>) clazz;
restorer = castedClass.getConstructor().newInstance();
} catch (Exception e) {
@@ -253,7 +247,7 @@ public class MessageSendingService extends Service implements MessageSendingList
private void sendMessage(Intent intent) {
boolean sendingActive = PrefUtils.getBoolean(this, R.string.preference_isSendingActive_key,
R.bool.preference_isSendingActive_default);
if (!sendingActive) {
if (! sendingActive) {
ExLog.i(this, TAG, "Sending deactivated. Message will not be sent to server.");
} else {
Serializable messageId = getMessageId(intent);
@@ -319,19 +313,20 @@ public class MessageSendingService extends Service implements MessageSendingList
}
/**
* a UUID that identifies this client session; can be used, e.g., to let the server identify subsequent requests
* coming from the same client
* a UUID that identifies this client session; can be used, e.g., to let the server identify subsequent requests coming from the same client
*/
public final static UUID uuid = UUID.randomUUID();
public static String getRaceLogEventSendAndReceiveUrl(Context context, final String raceGroupName,
final String raceName, final String fleetName) {
String url = String.format("%s/sailingserver/rc/racelog?" + RaceLogServletConstants.PARAMS_LEADERBOARD_NAME
+ "=%s&" + RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME + "=%s&"
+ RaceLogServletConstants.PARAMS_RACE_FLEET_NAME + "=%s&" + RaceLogServletConstants.PARAMS_CLIENT_UUID
+ "=%s", PrefUtils.getString(context, R.string.preference_server_url_key,
R.string.preference_server_url_default), URLEncoder.encode(raceGroupName), URLEncoder.encode(raceName),
URLEncoder.encode(fleetName), uuid);
String url = String.format("%s/sailingserver/rc/racelog?"+
RaceLogServletConstants.PARAMS_LEADERBOARD_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_RACE_FLEET_NAME+"=%s&"+
RaceLogServletConstants.PARAMS_CLIENT_UUID+"=%s",
PrefUtils.getString(context, R.string.preference_server_url_key, R.string.preference_server_url_default),
URLEncoder.encode(raceGroupName),
URLEncoder.encode(raceName), URLEncoder.encode(fleetName), uuid);
return url;
}
}