Newer
Older
package cz.nic.tablexia;
import com.badlogic.gdx.Gdx;
import com.getsentry.raven.DefaultRavenFactory;
import com.getsentry.raven.Raven;
import com.getsentry.raven.connection.Connection;
import com.getsentry.raven.connection.EventSendFailureCallback;
import com.getsentry.raven.dsn.Dsn;
import com.getsentry.raven.event.Event;
import com.getsentry.raven.event.EventBuilder;

Drahomír Karchňák
committed
import com.getsentry.raven.event.helper.EventBuilderHelper;
import com.getsentry.raven.event.interfaces.ExceptionInterface;
import com.getsentry.raven.event.interfaces.SentryInterface;
import com.getsentry.raven.event.interfaces.StackTraceInterface;

Drahomír Karchňák
committed
import net.engio.mbassy.listener.Handler;

Drahomír Karchňák
committed
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

Drahomír Karchňák
committed
import java.util.HashMap;
import java.util.HashSet;

Drahomír Karchňák
committed
import java.util.Map;
import java.util.Set;

Drahomír Karchňák
committed
import cz.nic.tablexia.bus.ApplicationBus;
import cz.nic.tablexia.game.AbstractTablexiaGame;

Drahomír Karchňák
committed
import cz.nic.tablexia.loader.TablexiaAbstractFileManager;

Drahomír Karchňák
committed
import cz.nic.tablexia.screen.AbstractTablexiaScreen;

Drahomír Karchňák
committed
/**
* Created by drahomir on 7/28/16.
*/
public class TablexiaRaven {
private static final String EXCEPTION_TYPE_TAG_NAME = "ExceptionType";
public enum ExceptionType {
JavaException,
IOSException
}
/**
* Custom raven factory which allows to set EventSendFailureCallback
*/
private class TablexiaRavenFactory extends DefaultRavenFactory {
private EventSendFailureCallback eventSendFailureCallback;
public Raven ravenInstance(String DSN, EventSendFailureCallback callback) {
eventSendFailureCallback = callback;
return createRavenInstance(new Dsn(DSN));
}
@Override
protected Connection createConnection(Dsn dsn) {
Connection connection = super.createConnection(dsn);
if(eventSendFailureCallback != null) connection.addEventSendFailureCallback(eventSendFailureCallback);
return connection;
}
}
/**
* Type of information to send via raven
*/
private enum InfoType {
TAG {
@Override

Drahomír Karchňák
committed
protected void insertInfo(TablexiaEventBuilderHelper tablexiaEventBuilderHelper, String name, String value) {
tablexiaEventBuilderHelper.addTag(name, value);
}
},
EXTRA {
@Override

Drahomír Karchňák
committed
protected void insertInfo(TablexiaEventBuilderHelper tablexiaEventBuilderHelper, String name, String value) {
tablexiaEventBuilderHelper.addExtra(name, value);

Drahomír Karchňák
committed
protected void insertInfo(TablexiaEventBuilderHelper tablexiaEventBuilderHelper, String name, String value) {}
}
/**
* Actual info values to send via raven
*/
private enum Info {
Platform(InfoType.TAG, "Platform", new StringRunnable() {
@Override
public String run() {
return Gdx.app.getType().toString();
}
}),
Version(InfoType.TAG, "Version", new StringRunnable() {
@Override
public String run() {
return TablexiaBuildConfig.VERSION_NAME;
}
}),

Drahomír Karchňák
committed
BuildType(InfoType.TAG, "BuildType", new StringRunnable() {
@Override
public String run() {
return TablexiaSettings.getInstance().getBuildType().toString();
}
}),
ConnectionType(InfoType.TAG, "ConnectionType", new StringRunnable() {
@Override
public String run() {
return Tablexia.getConnectionManager().getConnectionType().toString();
}
}),
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
Language(InfoType.EXTRA, "Language", new StringRunnable() {
@Override
public String run() {
return TablexiaSettings.getInstance().getLocale().toString();
}
}),
UserInfo(InfoType.EXTRA, "UserInfo", new StringRunnable() {
@Override
public String run() {
return TablexiaSettings.getInstance().getSelectedUser().toString();
}
}),
UserUUID(InfoType.EXTRA, "UserUUID", new StringRunnable() {
@Override
public String run() {
return TablexiaSettings.getInstance().getSelectedUser().getUuid();
}
});
private static final String DEFAULT_FALLBACK_VALUE = "UNDEFINED";
private final InfoType type;
private final String name;
private final String fallBackValue;
private final StringRunnable stringRunnable;
Info(InfoType type, String name, StringRunnable stringRunnable) {
this(type, name, DEFAULT_FALLBACK_VALUE, stringRunnable);
}
Info(InfoType type, String name, String fallbackValue, StringRunnable stringRunnable) {
this.type = type;
this.name = name;
this.fallBackValue = fallbackValue;
this.stringRunnable = stringRunnable;
}

Drahomír Karchňák
committed
public void insert(TablexiaEventBuilderHelper eventBuilderHelper) {
type.insertInfo(eventBuilderHelper, name, getSafeValue(stringRunnable, fallBackValue));
}
private String getSafeValue(StringRunnable runnable, String defaultValue) {
try {
return runnable.run();
}
catch (Exception e) {
return defaultValue;
}
}
}
private interface StringRunnable {

Drahomír Karchňák
committed
//Method which obtains the actual value of Info
String run();
}

Drahomír Karchňák
committed
/**
* Reports Manager - Saves and Resends reports later
*/
private static class ReportsManager extends TablexiaAbstractFileManager {
private static final String REPORT_FILE_EXTENSION = ".TablexiaReport";
private static final boolean HIDE_REPORT_FILES = true;

Drahomír Karchňák
committed
public ReportsManager() {
super(ReportStorageType.EXTERNAL);
}
public void storeRavenEvent(Event event) {
try {
String fileName = (HIDE_REPORT_FILES ? "." : "") + event.getId().toString() + REPORT_FILE_EXTENSION;

Drahomír Karchňák
committed
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
File dir = TablexiaAbstractFileManager.getFileStoragePathFileHandle(ReportStorageType.EXTERNAL).file();
if(!dir.exists()) dir.mkdir();
File file = new File(dir, fileName);
if(file.exists()) {
file.delete();
file.createNewFile();
}
FileOutputStream fileOut = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOut);
objectOutputStream.writeObject(event);
objectOutputStream.close();
fileOut.close();
} catch (Exception e) {
e.printStackTrace();
return;
}
}
private void sendSavedEvents(TablexiaRaven tablexiaRaven) {
try {
File dir = TablexiaAbstractFileManager.getFileStoragePathFileHandle(ReportStorageType.EXTERNAL).file();
if(!dir.exists()) return;
File[] files = dir.listFiles();
for(File file : files) {
Event e = deserializeEvent(file);
if(e != null) {
file.delete();
tablexiaRaven.sendEvent(e);

Drahomír Karchňák
committed
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
}
}
}
catch (Exception e) {
e.printStackTrace();
return;
}
}
private Event deserializeEvent(File file) {
Event event = null;
try {
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
event = (Event) ois.readObject();
ois.close();
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
return event;
}
}

Drahomír Karchňák
committed
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
public class TablexiaEventBuilderHelper implements EventBuilderHelper {
private HashMap<String, String> tablexiaTags;
private HashMap<String, String> tablexiaExtras;
public TablexiaEventBuilderHelper() {
tablexiaTags = new HashMap<String, String>();
tablexiaExtras = new HashMap<String, String>();
}
public void addTag(String key, String value) {
tablexiaTags.put(key, value);
}
public void addExtra(String key, String value) {
tablexiaExtras.put(key, value);
}
public void removeTag(String key) {
if(tablexiaTags.containsKey(key))
tablexiaTags.remove(key);
}
public void removeExtra(String key) {
if(tablexiaExtras.containsKey(key))
tablexiaExtras.remove(key);
}
@Override
public void helpBuildingEvent(EventBuilder eventBuilder) {
for(Map.Entry<String, String> tag : tablexiaTags.entrySet()) {
eventBuilder.withTag(tag.getKey(), tag.getValue());
}
for(Map.Entry<String, String> extra : tablexiaExtras.entrySet()) {
eventBuilder.withExtra(extra.getKey(), extra.getValue());
}
}
}
/**
* Actual Raven client
*/

Drahomír Karchňák
committed
private static final String[] SCREEN_INFO_EVENT_KEYS = new String [] {
AbstractTablexiaGame.RANDOM_SEED_SCREEN_INFO_LABEL,
AbstractTablexiaGame.GAME_DIFFICULTY_SCREEN_INFO_LABEL
};
private static TablexiaRaven instance;
private final EventSendFailureCallback DEFAULT_SEND_EVENT_FAILURE_CALLBACK = new EventSendFailureCallback() {
@Override
public void onFailure(Event event, Exception exception) {
//Calls all registered callbacks
for(EventSendFailureCallback eventSendFailureCallback : sendFailureCallbacks) eventSendFailureCallback.onFailure(event, exception);
}
};
private Set<EventSendFailureCallback> sendFailureCallbacks;
private Raven raven;

Drahomír Karchňák
committed
private ReportsManager reportsManager;

Drahomír Karchňák
committed
private TablexiaEventBuilderHelper tablexiaEventBuilderHelper;

Drahomír Karchňák
committed
private TablexiaRaven(String DSN) {
this.raven = new TablexiaRavenFactory().ravenInstance(DSN, DEFAULT_SEND_EVENT_FAILURE_CALLBACK);
this.sendFailureCallbacks = new HashSet<EventSendFailureCallback>();

Drahomír Karchňák
committed
this.reportsManager = new ReportsManager();

Drahomír Karchňák
committed
this.tablexiaEventBuilderHelper = new TablexiaEventBuilderHelper();
raven.addBuilderHelper(tablexiaEventBuilderHelper);
ApplicationBus.getInstance().subscribe(this);

Drahomír Karchňák
committed
private static boolean isStarted() {
return instance != null;
}
public static void start(String DSN) {
if(DSN == null || instance != null) return;
instance = new TablexiaRaven(DSN);
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
instance.sendExceptionEvent(t, e);
}
});

Drahomír Karchňák
committed
instance.addEventSendFailureCallback(new EventSendFailureCallback() {
@Override
public void onFailure(Event event, Exception exception) {
instance.reportsManager.storeRavenEvent(event);
}
});

Drahomír Karchňák
committed
public static void sendSavedReports() {
if(isStarted()) instance.reportsManager.sendSavedEvents(instance);

Drahomír Karchňák
committed
private void addEventSendFailureCallback(EventSendFailureCallback eventSendFailureCallback) {
if(isStarted()) {
if (eventSendFailureCallback != null) {
if (!sendFailureCallbacks.contains(eventSendFailureCallback))
sendFailureCallbacks.add(eventSendFailureCallback);
}
}
}
private void buildAndSendExceptionEvent(String message, SentryInterface sentryInterface, ExceptionType exceptionType) {
EventBuilder eventBuilder = new EventBuilder()
.withMessage(message)
.withSentryInterface(sentryInterface)
.withLevel(Event.Level.ERROR)
.withTag(EXCEPTION_TYPE_TAG_NAME, exceptionType.toString());
for(Info info : Info.values()) {

Drahomír Karchňák
committed
info.insert(tablexiaEventBuilderHelper);
raven.runBuilderHelpers(eventBuilder);
raven.sendEvent(eventBuilder.build());

Drahomír Karchňák
committed
Gdx.app.exit();

Drahomír Karchňák
committed
private void sendExceptionEvent(Thread t, Throwable e) {
buildAndSendExceptionEvent(e.getMessage(), new ExceptionInterface(e), ExceptionType.JavaException);
}
public static void sendCustomException(ExceptionType exceptionType, String msg, StackTraceElement[] stackTraceElements) {
if(!isStarted()) return;
instance.buildAndSendExceptionEvent(msg, new StackTraceInterface(stackTraceElements), exceptionType);
}
private void sendEvent(Event e) {
if(isStarted() && e != null) instance.raven.sendEvent(e);

Drahomír Karchňák
committed
}

Drahomír Karchňák
committed
@Handler
public void handleScreenChangedEvent(TablexiaApplication.ScreenChangedEvent screenChangedEvent) {
for(String infoEventKey : SCREEN_INFO_EVENT_KEYS) {
tablexiaEventBuilderHelper.removeExtra(infoEventKey);
}
}
@Handler
public void handleScreenInfoEvent(AbstractTablexiaScreen.ScreenInfoEvent screenInfoEvent) {
for(String infoEventKey : SCREEN_INFO_EVENT_KEYS) {
if(infoEventKey.equals(screenInfoEvent.getInfoKey())) {
tablexiaEventBuilderHelper.addExtra(screenInfoEvent.getInfoKey(), screenInfoEvent.getInfoValue());
}
}
}