added track file import

This commit is contained in:
Fredrik Teschke
2014-05-02 03:41:19 +02:00
parent 6467222dd9
commit b9e28d811b
14 changed files with 72391 additions and 14 deletions
@@ -17,7 +17,6 @@ import com.sap.sailing.domain.racelog.tracking.DeviceIdentifierStringSerializati
import com.sap.sailing.domain.racelog.tracking.SmartphoneUUIDIdentifier;
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
import com.sap.sailing.domain.racelogtracking.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.GPSFixJsonDeserializer;
@@ -33,6 +32,7 @@ import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdent
import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.GPSFixJsonHandlerImpl;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.impl.SmartphoneUUIDJsonHandler;
import com.sap.sailing.server.gateway.trackfiles.TrackFileImportDeviceIdentifier;
public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
@@ -61,6 +61,7 @@ public class Activator implements BundleActivator {
registrations.add(context.registerService(DeviceIdentifierJsonHandler.class, new PingDeviceIdentifierJsonHandler(), getDict(PingDeviceIdentifierImpl.TYPE)));
registrations.add(context.registerService(DeviceIdentifierMongoHandler.class, new TrackFileImportDeviceIdentifierMongoHandler(), getDict(TrackFileImportDeviceIdentifier.TYPE)));
registrations.add(context.registerService(DeviceIdentifierJsonHandler.class, new TrackFileImportDeviceIdentifierJsonHandler(), getDict(TrackFileImportDeviceIdentifier.TYPE)));
registrations.add(context.registerService(DeviceIdentifierStringSerializationHandler.class, new TrackFileImportDeviceIdentifierStringSerializationhandler(), getDict(TrackFileImportDeviceIdentifier.TYPE)));
registerGPSFixJsonService(context, new GPSFixJsonDeserializer(), new GPSFixJsonSerializer(), GPSFixJsonDeserializer.TYPE);
@@ -0,0 +1,42 @@
package com.sap.sailing.domain.racelogtracking.impl;
import java.util.UUID;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
import com.sap.sailing.server.gateway.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.server.gateway.trackfiles.TrackFileImportDeviceIdentifierImpl;
public class TrackFileImportDeviceIdentifierJsonHandler implements DeviceIdentifierJsonHandler {
private static enum Fields {UUID, FILE_NAME, TRACK_NAME, DATE_AS_MILLIS};
@Override
public DeviceIdentifier deserialize(Object serialized, String type, String stringRepresentation)
throws TransformationException {
JSONObject json = (JSONObject) serialized;
UUID uuid = UUID.fromString((String) json.get(Fields.UUID.name()));
String fileName = (String) json.get(Fields.FILE_NAME.name());
String trackName = (String) json.get(Fields.TRACK_NAME.name());
Long dateAsMillis = (Long) json.get(Fields.DATE_AS_MILLIS.name());
TimePoint timePoint = dateAsMillis == null ? null : new MillisecondsTimePoint(dateAsMillis);
return new TrackFileImportDeviceIdentifierImpl(uuid, fileName, trackName, timePoint);
}
@Override
public Pair<JSONObject, String> serialize(DeviceIdentifier deviceIdentifier) throws TransformationException {
TrackFileImportDeviceIdentifier id = TrackFileImportDeviceIdentifierImpl.cast(deviceIdentifier);
JSONObject result = new JSONObject();
result.put(Fields.UUID.name(), id.getId().toString());
result.put(Fields.FILE_NAME.name(), id.getFileName());
result.put(Fields.TRACK_NAME.name(), id.getTrackName());
result.put(Fields.DATE_AS_MILLIS.name(), id.getUploadedAt() == null ? null : id.getUploadedAt().asMillis());
return new Pair<JSONObject, String>(result, TrackFileImportDeviceIdentifier.TYPE);
}
}
@@ -10,8 +10,8 @@ import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.TrackFileImportDeviceIdentifierImpl;
import com.sap.sailing.server.gateway.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.server.gateway.trackfiles.TrackFileImportDeviceIdentifierImpl;
public class TrackFileImportDeviceIdentifierMongoHandler implements DeviceIdentifierMongoHandler {
private static enum Fields {UUID, FILE_NAME, TRACK_NAME, DATE_AS_MILLIS};
@@ -32,7 +32,7 @@ public class TrackFileImportDeviceIdentifierMongoHandler implements DeviceIdenti
public Pair<DBObject, String> serialize(DeviceIdentifier deviceIdentifier) throws TransformationException {
TrackFileImportDeviceIdentifier id = TrackFileImportDeviceIdentifierImpl.cast(deviceIdentifier);
DBObject result = new BasicDBObject();
result.put(Fields.UUID.name(), id.getId());
result.put(Fields.UUID.name(), id.getId().toString());
result.put(Fields.FILE_NAME.name(), id.getFileName());
result.put(Fields.TRACK_NAME.name(), id.getTrackName());
result.put(Fields.DATE_AS_MILLIS.name(), id.getUploadedAt() == null ? null : id.getUploadedAt().asMillis());
@@ -6,8 +6,8 @@ import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifierStringSerializationHandler;
import com.sap.sailing.domain.racelogtracking.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.TrackFileImportDeviceIdentifierImpl;
import com.sap.sailing.server.gateway.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.server.gateway.trackfiles.TrackFileImportDeviceIdentifierImpl;
public class TrackFileImportDeviceIdentifierStringSerializationhandler implements
DeviceIdentifierStringSerializationHandler {
@@ -44,4 +44,5 @@ Bundle-ClassPath: .
Web-ContextPath: /sailingserver
Export-Package: com.sap.sailing.server.gateway,
com.sap.sailing.server.gateway.jaxrs,
com.sap.sailing.server.gateway.jaxrs.spi
com.sap.sailing.server.gateway.jaxrs.spi,
com.sap.sailing.server.gateway.trackfiles
@@ -205,11 +205,19 @@
</servlet-mapping>
<servlet>
<servlet-name>TrackFilesExportPostServlet</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.impl.TrackFilesExportPostServlet</servlet-class>
<servlet-class>com.sap.sailing.server.gateway.trackfiles.impl.TrackFilesExportPostServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TrackFilesExportPostServlet</servlet-name>
<url-pattern>/trackfiles</url-pattern>
<url-pattern>/trackfiles/export</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>TrackFilesImportServlet</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.trackfiles.impl.TrackFilesImportServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TrackFilesImportServlet</servlet-name>
<url-pattern>/trackfiles/import</url-pattern>
</servlet-mapping>
<servlet>
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.racelogtracking;
package com.sap.sailing.server.gateway.trackfiles;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@@ -46,8 +46,8 @@ public class TrackFileImportDeviceIdentifierImpl implements TrackFileImportDevic
@Override
public boolean equals(Object obj) {
if (obj instanceof PingDeviceIdentifier) {
return id.equals(((PingDeviceIdentifier) obj).getId());
if (obj instanceof TrackFileImportDeviceIdentifier) {
return id.equals(((TrackFileImportDeviceIdentifier) obj).getId());
}
return false;
}
@@ -1,4 +1,4 @@
package com.sap.sailing.server.gateway.impl;
package com.sap.sailing.server.gateway.trackfiles.impl;
import java.io.IOException;
import java.util.ArrayList;
@@ -0,0 +1,113 @@
package com.sap.sailing.server.gateway.trackfiles.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.json.simple.JSONArray;
import com.sap.sailing.domain.common.racelog.tracking.TypeBasedServiceFinder;
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.server.gateway.AbstractJsonHttpServlet;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.DeviceIdentifierJsonSerializer;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
import com.sap.sailing.server.gateway.trackfiles.TrackFileImportDeviceIdentifierImpl;
import com.sap.sailing.server.trackfiles.Import;
import com.sap.sailing.server.trackfiles.Import.FixCallback;
/**
* Servlet that processes uploaded track files by adding their fixes to the GPSFixStore.
* Returns a JSON array of generated device identifiers.
* @author Fredrik Teschke
*
*/
public class TrackFilesImportServlet extends AbstractJsonHttpServlet {
private static final long serialVersionUID = 1120226743039934620L;
private void storeFix(GPSFix fix, DeviceIdentifier deviceIdentifier) {
getService().getGPSFixStore().storeFix(deviceIdentifier, fix);
}
@SuppressWarnings("unchecked")
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (!ServletFileUpload.isMultipartContent(req)) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
Set<FileItem> files = new HashSet<>();
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items;
try {
items = (List<FileItem>) upload.parseRequest(req);
} catch (FileUploadException e) {
throw new IOException("Could not parse request");
}
for (FileItem item : items) {
if (!item.isFormField())
files.add(item);
}
final List<DeviceIdentifier> deviceIdList = new ArrayList<>();
for (FileItem file : files) {
final String fileName = file.getName();
final Map<String, DeviceIdentifier> deviceIds = new HashMap<>();
Import.INSTANCE.importFixes(file.getInputStream(), new FixCallback() {
@Override
public void addFix(GPSFix fix, String trackName) {
DeviceIdentifier deviceId = deviceIds.get(trackName);
if (deviceId == null) {
deviceId = new TrackFileImportDeviceIdentifierImpl(fileName, trackName);
deviceIds.put(trackName, deviceId);
deviceIdList.add(deviceId);
}
storeFix(fix, deviceId);
}
@Override
public void addFix(GPSFix fix) {
DeviceIdentifier deviceId = deviceIds.get(null);
if (deviceId == null) {
deviceId = new TrackFileImportDeviceIdentifierImpl(fileName, null);
deviceIds.put(null, deviceId);
deviceIdList.add(deviceId);
}
storeFix(fix, deviceId);
}
}, true);
}
//setJsonResponseHeader(resp);
//DO NOT set a JSON response header. This causes the browser to wrap the response in a
//<pre> tag when uploading from GWT, as this is an AJAX-request inside an iFrame.
resp.setContentType("text/html");
TypeBasedServiceFinder<DeviceIdentifierJsonHandler> serviceFinder =
getServiceFinderFactory().createServiceFinder(DeviceIdentifierJsonHandler.class);
JsonSerializer<DeviceIdentifier> serializer = new DeviceIdentifierJsonSerializer(serviceFinder);
JSONArray array = new JSONArray();
for (DeviceIdentifier deviceId : deviceIdList) {
array.add(serializer.serialize(deviceId));
}
array.writeJSONString(resp.getWriter());
}
}
@@ -0,0 +1,42 @@
package com.sap.sailing.server.trackfiles.test;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.server.trackfiles.Import;
import com.sap.sailing.server.trackfiles.Import.FixCallback;
public class TrackFileImportTest {
private boolean callbackCalled = false;
@Before
public void setup() {
callbackCalled = false;
}
@Test
public void testImport() throws IOException {
InputStream in = getClass().getResourceAsStream("/Cardiff Race17 - COMPETITORS.gpx");
Import.INSTANCE.importFixes(in, new FixCallback() {
@Override
public void addFix(GPSFix fix, String trackName) {
if (fix instanceof GPSFixMoving) {
callbackCalled = true;
}
}
@Override
public void addFix(GPSFix fix) {
// expecting track names
}
}, false);
assertTrue(callbackCalled);
}
}
@@ -0,0 +1,24 @@
package com.sap.sailing.server.trackfiles;
import java.io.IOException;
import java.io.InputStream;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.server.trackfiles.impl.ImportImpl;
public interface Import {
Import INSTANCE = new ImportImpl();
interface FixCallback {
void addFix(GPSFix fix);
void addFix(GPSFix fix, String trackName);
}
/**
* Retrieves the fixes from the {@code inputStream}, and calls the
* {@code callback} with every new fix.
* @param inferSpeedAndBearing Should speed and bearing be inferred by looking
* at the previous fix, if that data is not directly present within the file?
*/
void importFixes(InputStream inputStream, FixCallback callback, boolean inferSpeedAndBearing) throws IOException;
}
@@ -0,0 +1,105 @@
package com.sap.sailing.server.trackfiles.impl;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import slash.common.type.CompactCalendar;
import slash.navigation.base.BaseNavigationPosition;
import slash.navigation.base.BaseRoute;
import slash.navigation.base.NavigationFormatParser;
import slash.navigation.base.ParserResult;
import slash.navigation.base.Wgs84Position;
import slash.navigation.gpx.Gpx11Format;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.server.trackfiles.Import;
/**
* Currently can only import GPX v1.1 files. See bug 1933 for a detailed discussion.
* @author Fredrik Teschke
*
*/
public class ImportImpl implements Import {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void importFixes(InputStream inputStream, FixCallback callback, boolean inferSpeedAndBearing) throws IOException {
//TODO for now we can't use this, as this invokes GPSBabel -> license problem (bug 1933)
// @SuppressWarnings("rawtypes")
// List<BaseRoute> routes = new NavigationFormatParser().read(inputStream).getAllRoutes();
//TODO dirty hack, because no public read method for inputstream and custom list of formats
NavigationFormatParser parser = new NavigationFormatParser();
List<BaseRoute> routes;
try {
Method m = NavigationFormatParser.class.getDeclaredMethod("read", InputStream.class, Integer.TYPE,
CompactCalendar.class, List.class);
m.setAccessible(true);
ParserResult result = (ParserResult) m.invoke(parser, inputStream, 1024 * 1024, null,
Arrays.asList(new Gpx11Format()));
if (result == null) {
throw new IOException("Could not find routes in file - only understand GPX v1.1");
}
routes = result.getAllRoutes();
} catch (Exception e) {
throw new IOException(e);
}
for (BaseRoute route : routes) {
String routeName = route.getName();
List<? extends BaseNavigationPosition> positions = (List<? extends BaseNavigationPosition>) route.getPositions();
GPSFix previousFix = null;
for (BaseNavigationPosition p : positions) {
try {
Date t = p.getTime().getTime();
Double heading = null;
if (p instanceof Wgs84Position) {
heading = ((Wgs84Position) p).getHeading();
}
Double speed = p.getSpeed();
Position pos = new DegreePosition(p.getLatitude(), p.getLongitude());
TimePoint timePoint = new MillisecondsTimePoint(t);
SpeedWithBearing speedWithBearing = null;
if (speed != null && heading != null) {
speedWithBearing = new KnotSpeedWithBearingImpl(speed, new DegreeBearingImpl(heading));
} else if (inferSpeedAndBearing && previousFix != null) {
GPSFix tmpFix = new GPSFixImpl(pos, timePoint);
speedWithBearing = previousFix.getSpeedAndBearingRequiredToReach(tmpFix);
}
GPSFix fix = null;
if (speedWithBearing != null) {
fix = new GPSFixMovingImpl(pos, timePoint, speedWithBearing);
} else {
fix = new GPSFixImpl(pos, timePoint);
}
if (routeName != null) {
callback.addFix(fix, routeName);
} else {
callback.addFix(fix);
}
previousFix = fix;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}