diff --git a/pom.xml b/pom.xml index 1f0e3a59..89145f52 100644 --- a/pom.xml +++ b/pom.xml @@ -6,8 +6,7 @@ com.conveyal analyst - 4.0.0 - + 4.0.1 diff --git a/src/main/java/com/conveyal/taui/AnalysisServer.java b/src/main/java/com/conveyal/taui/AnalysisServer.java index 10a59797..6b47da3a 100644 --- a/src/main/java/com/conveyal/taui/AnalysisServer.java +++ b/src/main/java/com/conveyal/taui/AnalysisServer.java @@ -81,16 +81,17 @@ public static void main (String... args) { // Default is JSON, will be overridden by the few controllers that do not return JSON res.type("application/json"); - // Log each API request - LOG.info("{} {}", req.requestMethod(), req.pathInfo()); - - if (!AnalysisServerConfig.offline) { + if (AnalysisServerConfig.auth0ClientId != null && AnalysisServerConfig.auth0Secret != null) { handleAuthentication(req, res); } else { + LOG.warn("No Auth0 credentials were supplied, setting accessGroup and email to placeholder defaults"); // hardwire group name if we're working offline req.attribute("accessGroup", "OFFLINE"); req.attribute("email", "analysis@conveyal.com"); } + + // Log each API request + LOG.info("{} {} by {} of {}", req.requestMethod(), req.pathInfo(), req.attribute("email"), req.attribute("accessGroup")); }); // Register all our HTTP request handlers with the Spark HTTP framework. @@ -123,23 +124,23 @@ public static void main (String... args) { exception(AnalysisServerException.class, (e, request, response) -> { AnalysisServerException ase = ((AnalysisServerException) e); - AnalysisServer.respondToException(response, ase, ase.type.name(), ase.message, ase.httpCode); + AnalysisServer.respondToException(ase, request, response, ase.type.name(), ase.message, ase.httpCode); }); exception(IOException.class, (e, request, response) -> { - AnalysisServer.respondToException(response, e, "BAD_REQUEST", e.getMessage(), 400); + AnalysisServer.respondToException(e, request, response, "BAD_REQUEST", e.getMessage(), 400); }); exception(FileUploadException.class, (e, request, response) -> { - AnalysisServer.respondToException(response, e, "BAD_REQUEST", e.getMessage(), 400); + AnalysisServer.respondToException(e, request, response, "BAD_REQUEST", e.getMessage(), 400); }); exception(NullPointerException.class, (e, request, response) -> { - AnalysisServer.respondToException(response, e, "UNKNOWN", e.getMessage(), 400); + AnalysisServer.respondToException(e, request, response, "UNKNOWN", e.getMessage(), 400); }); exception(RuntimeException.class, (e, request, response) -> { - AnalysisServer.respondToException(response, e, "RUNTIME", e.getMessage(), 400); + AnalysisServer.respondToException(e, request, response, "RUNTIME", e.getMessage(), 400); }); LOG.info("Conveyal Analysis server is ready."); @@ -150,14 +151,14 @@ public static void handleAuthentication (Request req, Response res) { // authorization required if (auth == null || auth.isEmpty()) { - AnalysisServerException.Unauthorized("You must be logged in."); + throw AnalysisServerException.Unauthorized("You must be logged in."); } // make sure it's properly formed String[] authComponents = auth.split(" "); if (authComponents.length != 2 || !"bearer".equals(authComponents[0].toLowerCase())) { - AnalysisServerException.Unknown("Authorization header is malformed: " + auth); + throw AnalysisServerException.Unknown("Authorization header is malformed: " + auth); } // validate the JWT @@ -167,22 +168,22 @@ public static void handleAuthentication (Request req, Response res) { try { jwt = verifier.verify(authComponents[1]); } catch (Exception e) { - AnalysisServerException.Forbidden("Login failed to verify with our authorization provider. " + e.getMessage()); + throw AnalysisServerException.Forbidden("Login failed to verify with our authorization provider. " + e.getMessage()); } if (!jwt.containsKey("analyst")) { - AnalysisServerException.Forbidden("Access denied. User does not have access to Analysis."); + throw AnalysisServerException.Forbidden("Access denied. User does not have access to Analysis."); } String group = null; try { group = (String) ((Map) jwt.get("analyst")).get("group"); } catch (Exception e) { - AnalysisServerException.Forbidden("Access denied. User is not associated with any group. " + e.getMessage()); + throw AnalysisServerException.Forbidden("Access denied. User is not associated with any group. " + e.getMessage()); } if (group == null) { - AnalysisServerException.Forbidden("Access denied. User is not associated with any group."); + throw AnalysisServerException.Forbidden("Access denied. User is not associated with any group."); } // attributes to be used on models @@ -190,10 +191,10 @@ public static void handleAuthentication (Request req, Response res) { req.attribute("email", jwt.get("email")); } - public static void respondToException(Response response, Exception e, String type, String message, int code) { + public static void respondToException(Exception e, Request request, Response response, String type, String message, int code) { String stack = ExceptionUtils.getStackTrace(e); - LOG.error("Server exception thrown, type: {}, message: {}", type, message); + LOG.error("{} {} -> {} {} by {} of {}", type, message, request.requestMethod(), request.pathInfo(), request.attribute("email"), request.attribute("accessGroup")); LOG.error(stack); JSONObject body = new JSONObject(); diff --git a/src/main/java/com/conveyal/taui/analysis/RegionalAnalysisManager.java b/src/main/java/com/conveyal/taui/analysis/RegionalAnalysisManager.java index e5ef97cb..9f005695 100644 --- a/src/main/java/com/conveyal/taui/analysis/RegionalAnalysisManager.java +++ b/src/main/java/com/conveyal/taui/analysis/RegionalAnalysisManager.java @@ -12,6 +12,7 @@ import com.conveyal.r5.analyst.cluster.RegionalTask; import com.conveyal.r5.analyst.scenario.Scenario; import com.conveyal.taui.AnalysisServerConfig; +import com.conveyal.taui.AnalysisServerException; import com.conveyal.taui.models.RegionalAnalysis; import com.conveyal.taui.persistence.TiledAccessGrid; import com.conveyal.taui.util.HttpUtil; @@ -116,7 +117,7 @@ public static void enqueue (RegionalAnalysis regionalAnalysis) { } } catch (IOException e) { LOG.error("error enqueueing requests", e); - throw new RuntimeException("error enqueueing requests", e); + throw AnalysisServerException.Unknown(e); } consumer.registerJob(templateTask, diff --git a/src/main/java/com/conveyal/taui/controllers/OpportunityDatasetsController.java b/src/main/java/com/conveyal/taui/controllers/OpportunityDatasetsController.java index b85dc306..728163c9 100644 --- a/src/main/java/com/conveyal/taui/controllers/OpportunityDatasetsController.java +++ b/src/main/java/com/conveyal/taui/controllers/OpportunityDatasetsController.java @@ -283,7 +283,7 @@ private static Object downloadOpportunityDataset (Request req, Response res) thr if (!s3.doesObjectExist(BUCKET, String.format("%s.%s", gridPath, format))) { // if this grid is not on S3 in the requested format, try to get the .grid format if (!s3.doesObjectExist(BUCKET, String.format("%s.grid", gridPath))) { - throw new IllegalArgumentException("This grid does not exist."); + throw AnalysisServerException.NotFound("This grid does not exist."); } else { // get the grid and convert it to the requested format S3Object s3Grid = s3.getObject(BUCKET, String.format("%s.grid", gridPath)); @@ -327,7 +327,7 @@ private static List writeOpportunityDatasetToS3(Map stops = ModificationStop.getStopsFromSegments(segments); at.frequencies = timetables.stream().map(tt -> tt.toR5(stops)).collect(Collectors.toList()); at.stops = ModificationStop.toSpec(stops); diff --git a/src/main/java/com/conveyal/taui/models/AnalysisRequest.java b/src/main/java/com/conveyal/taui/models/AnalysisRequest.java index 9246aaa3..b725e241 100644 --- a/src/main/java/com/conveyal/taui/models/AnalysisRequest.java +++ b/src/main/java/com/conveyal/taui/models/AnalysisRequest.java @@ -28,7 +28,6 @@ public class AnalysisRequest { // All analyses parameters public String accessModes; public float bikeSpeed; - public float carSpeed; public LocalDate date; public String directModes; public String egressModes; @@ -43,6 +42,7 @@ public class AnalysisRequest { // Parameters that aren't currently configurable in the UI public int bikeTrafficStress = 4; + public float carSpeed = 20; public int maxWalkTime = 20; public int maxBikeTime = 20; public int maxCarTime = 45; @@ -83,14 +83,16 @@ public AnalysisTask populateTask (AnalysisTask task, Project project) { modifications = modificationsForProject(project.accessGroup, projectId, variantIndex); } - // No idea how long this operation takes or if it is actually necessary + // The CRC is appended to the scenario ID to identify a unique revision of the scenario (still denoted here + // as variant) allowing the worker to cache and reuse networks built by applying that exact revision of the + // scenario to a base network. CRC32 crc = new CRC32(); - crc.update(modifications.stream().map(Modification::toString).collect(Collectors.joining("-")).getBytes()); - crc.update(JsonUtilities.objectToJsonBytes(this)); + crc.update(JsonUtilities.objectToJsonBytes(modifications)); + long crcValue = crc.getValue(); task.scenario = new Scenario(); // TODO figure out why we use both - task.jobId = String.format("%s-%s-%s", projectId, variantIndex, crc.getValue()); + task.jobId = String.format("%s-%s-%s", projectId, variantIndex, crcValue); task.scenario.id = task.scenarioId = task.jobId; task.scenario.modifications = modifications; @@ -146,15 +148,21 @@ public AnalysisTask populateTask (AnalysisTask task, Project project) { task.maxTripDurationMinutes = maxTripDurationMinutes; } - task.accessModes = EnumSet.copyOf(Arrays.stream(accessModes.split(",")) - .map(LegMode::valueOf).collect(Collectors.toList())); - task.directModes = EnumSet.copyOf(Arrays.stream(directModes.split(",")) - .map(LegMode::valueOf).collect(Collectors.toList())); - task.egressModes = EnumSet.copyOf(Arrays.stream(egressModes.split(",")) - .map(LegMode::valueOf).collect(Collectors.toList())); - task.transitModes = EnumSet.copyOf(Arrays.stream(transitModes.split(",")) - .map(TransitModes::valueOf).collect(Collectors.toList())); + task.accessModes = getEnumSetFromString(accessModes); + task.directModes = getEnumSetFromString(directModes); + task.egressModes = getEnumSetFromString(egressModes); + task.transitModes = transitModes != null && !"".equals(transitModes) + ? EnumSet.copyOf(Arrays.stream(transitModes.split(",")).map(TransitModes::valueOf).collect(Collectors.toList())) + : EnumSet.noneOf(TransitModes.class); return task; } + + private EnumSet getEnumSetFromString (String s) { + if (s != null && !"".equals(s)) { + return EnumSet.copyOf(Arrays.stream(s.split(",")).map(LegMode::valueOf).collect(Collectors.toList())); + } else { + return EnumSet.noneOf(LegMode.class); + } + } } diff --git a/src/main/java/com/conveyal/taui/models/Bookmark.java b/src/main/java/com/conveyal/taui/models/Bookmark.java index 0768d3cf..a8a6f7d1 100644 --- a/src/main/java/com/conveyal/taui/models/Bookmark.java +++ b/src/main/java/com/conveyal/taui/models/Bookmark.java @@ -1,12 +1,10 @@ package com.conveyal.taui.models; -import com.conveyal.r5.profile.ProfileRequest; - /** * A bookmark represents "frozen" settings for single point results. */ public class Bookmark extends Model { - public ProfileRequest profileRequest; + public AnalysisRequest profileRequest; public int isochroneCutoff; diff --git a/src/main/java/com/conveyal/taui/models/Bundle.java b/src/main/java/com/conveyal/taui/models/Bundle.java index 25277e57..2e33d371 100644 --- a/src/main/java/com/conveyal/taui/models/Bundle.java +++ b/src/main/java/com/conveyal/taui/models/Bundle.java @@ -6,6 +6,7 @@ import com.conveyal.gtfs.GTFSFeed; import com.conveyal.r5.analyst.cluster.BundleManifest; import com.conveyal.taui.AnalysisServerConfig; +import com.conveyal.taui.AnalysisServerException; import com.conveyal.taui.util.JsonUtil; import java.io.File; @@ -62,7 +63,7 @@ public Bundle clone () { try { return (Bundle) super.clone(); } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } } @@ -93,7 +94,7 @@ public FeedSummary clone () { try { return (FeedSummary) super.clone(); } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } } } diff --git a/src/main/java/com/conveyal/taui/models/ModificationStop.java b/src/main/java/com/conveyal/taui/models/ModificationStop.java index 89ae9209..f18d3272 100644 --- a/src/main/java/com/conveyal/taui/models/ModificationStop.java +++ b/src/main/java/com/conveyal/taui/models/ModificationStop.java @@ -15,28 +15,54 @@ class ModificationStop { private static double MIN_SPACING_PERCENTAGE = 0.25; + private static int DEFAULT_SEGMENT_SPEED = 15; - StopSpec stop; - double distanceFromStart; + private Coordinate coordinate; + private String id; + private double distanceFromStart; - ModificationStop(Coordinate c, String id, double distanceFromStart) { - this.stop = new StopSpec(c.x, c.y); - this.stop.id = id; + private ModificationStop(Coordinate c, String id, double distanceFromStart) { + this.coordinate = c; + this.id = id; this.distanceFromStart = distanceFromStart; } + /** + * Create the StopSpec types required by r5 + * @param stops + * @return + */ static List toSpec (List stops) { - return stops.stream().map(s -> s.stop).collect(Collectors.toList()); + return stops + .stream() + .map(s -> { + if (s.id == null){ + return new StopSpec(s.coordinate.x, s.coordinate.y); + } else { + return new StopSpec(s.id); + } + }) + .collect(Collectors.toList()); } + /** + * We don't just use `StopSpec`s here because we need to keep the `distanceFromStart` for generating hop times. + * @param segments Modification segments + * @return ModificationStop[] + */ static List getStopsFromSegments (List segments) { Stack stops = new Stack<>(); CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84; + if (segments == null || segments.size() == 0) { + return new ArrayList<>(); + } + Segment firstSegment = segments.get(0); - Coordinate firstStopCoord = firstSegment.geometry.getCoordinates()[0]; - ModificationStop firstStop = new ModificationStop(firstStopCoord, firstSegment.fromStopId, 0); - stops.add(firstStop); + + if (firstSegment.stopAtStart) { + stops.add(new ModificationStop(firstSegment.geometry.getCoordinates()[0], firstSegment.fromStopId, 0)); + } double distanceToLastStop = 0; double distanceToLineSegmentStart = 0; @@ -74,11 +100,13 @@ static List getStopsFromSegments (List segments) { distanceToLineSegmentStart += distanceThisLineSegment; } - if (segment.toStopId != null) { + if (segment.stopAtEnd) { // If the last auto-generated stop was too close, pop it - ModificationStop lastStop = stops.peek(); - if (lastStop.stop.id == null && (distanceToLineSegmentStart - distanceToLastStop) / spacing < MIN_SPACING_PERCENTAGE) { - stops.pop(); + if (stops.size() > 0) { + ModificationStop lastStop = stops.peek(); + if (lastStop.id == null && (distanceToLineSegmentStart - distanceToLastStop) / spacing < MIN_SPACING_PERCENTAGE) { + stops.pop(); + } } Coordinate endCoord = coords[coords.length - 1]; @@ -93,12 +121,16 @@ static List getStopsFromSegments (List segments) { } static int[] getDwellTimes (List stops, Integer[] dwellTimes, int defaultDwellTime) { + if (stops == null || stops.size() == 0) { + return new int[0]; + } + int[] stopDwellTimes = new int[stops.size()]; int realStopIndex = 0; for (int i = 0; i < stops.size(); i++) { - String id = stops.get(i).stop.id; - if (id == null || dwellTimes == null) { + String id = stops.get(i).id; + if (id == null || dwellTimes == null || dwellTimes.length <= realStopIndex) { stopDwellTimes[i] = defaultDwellTime; } else { Integer specificDwellTime = dwellTimes[realStopIndex]; @@ -111,16 +143,22 @@ static int[] getDwellTimes (List stops, Integer[] dwellTimes, } static int[] getHopTimes (List stops, int[] segmentSpeeds) { + if (stops == null || stops.size() < 2) { + return new int[0]; + } + int[] hopTimes = new int[stops.size() - 1]; ModificationStop lastStop = stops.get(0); int realStopIndex = 0; - for (int i = 1; i < stops.size(); i++) { - ModificationStop stop = stops.get(i); + for (int i = 0; i < hopTimes.length; i++) { + ModificationStop stop = stops.get(i + 1); double hopDistance = stop.distanceFromStart - lastStop.distanceFromStart; - hopTimes[i - 1] = (int) (hopDistance / (segmentSpeeds[realStopIndex] * 1000) * 3000); - if (stop.stop.id != null) { + int segmentSpeed = segmentSpeeds.length > realStopIndex ? segmentSpeeds[realStopIndex] : DEFAULT_SEGMENT_SPEED; + hopTimes[i] = (int) (hopDistance / (segmentSpeed * 1000) * 3000); + + if (stop.id != null) { realStopIndex++; } diff --git a/src/main/java/com/conveyal/taui/models/Project.java b/src/main/java/com/conveyal/taui/models/Project.java index c934c584..86fcc457 100644 --- a/src/main/java/com/conveyal/taui/models/Project.java +++ b/src/main/java/com/conveyal/taui/models/Project.java @@ -1,5 +1,7 @@ package com.conveyal.taui.models; +import com.conveyal.taui.AnalysisServerException; + /** * Represents a TAUI project */ @@ -15,7 +17,7 @@ public Project clone () { try { return (Project) super.clone(); } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } } } diff --git a/src/main/java/com/conveyal/taui/models/Region.java b/src/main/java/com/conveyal/taui/models/Region.java index 1f8c4fc2..05f089bf 100644 --- a/src/main/java/com/conveyal/taui/models/Region.java +++ b/src/main/java/com/conveyal/taui/models/Region.java @@ -1,5 +1,6 @@ package com.conveyal.taui.models; +import com.conveyal.taui.AnalysisServerException; import com.conveyal.taui.persistence.Persistence; import com.fasterxml.jackson.annotation.JsonView; @@ -64,7 +65,7 @@ public Region clone () { return (Region) super.clone(); } catch (CloneNotSupportedException e) { // can't happen. - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } } diff --git a/src/main/java/com/conveyal/taui/models/RegionalAnalysis.java b/src/main/java/com/conveyal/taui/models/RegionalAnalysis.java index 648dabcc..f1897eed 100644 --- a/src/main/java/com/conveyal/taui/models/RegionalAnalysis.java +++ b/src/main/java/com/conveyal/taui/models/RegionalAnalysis.java @@ -1,6 +1,7 @@ package com.conveyal.taui.models; import com.conveyal.r5.analyst.cluster.RegionalTask; +import com.conveyal.taui.AnalysisServerException; import com.conveyal.taui.analysis.RegionalAnalysisManager; import com.fasterxml.jackson.annotation.JsonView; import com.vividsolutions.jts.geom.Envelope; @@ -81,7 +82,7 @@ public RegionalAnalysis clone () { try { return (RegionalAnalysis) super.clone(); } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } } } diff --git a/src/main/java/com/conveyal/taui/persistence/MongoMap.java b/src/main/java/com/conveyal/taui/persistence/MongoMap.java index 670ae993..46c291f8 100644 --- a/src/main/java/com/conveyal/taui/persistence/MongoMap.java +++ b/src/main/java/com/conveyal/taui/persistence/MongoMap.java @@ -49,7 +49,7 @@ public boolean containsKey(Object key) { } public boolean containsValue(Object value) { - throw new UnsupportedOperationException(); + throw AnalysisServerException.Unknown("Unsupported operation"); } public V findByIdFromRequestIfPermitted(Request request) { @@ -135,7 +135,7 @@ public V updateByUserIfPermitted(V value, String updatedBy, String accessGroup) } public V put(String key, V value) { - if (key != value._id) throw new IllegalArgumentException("ID does not match"); + if (key != value._id) throw AnalysisServerException.BadRequest("ID does not match"); return put(value, null); } @@ -203,7 +203,7 @@ public V remove(Object key) { WriteResult result = wrappedCollection.removeById((String) key); LOG.info(result.toString()); if (result.getN() == 0) { - throw new RuntimeException(String.format("The data for _id %s does not exist", key)); + throw AnalysisServerException.NotFound(String.format("The data for _id %s does not exist", key)); } return null; diff --git a/src/main/java/com/conveyal/taui/persistence/OSMPersistence.java b/src/main/java/com/conveyal/taui/persistence/OSMPersistence.java index 73cbc0c5..1127ab0e 100644 --- a/src/main/java/com/conveyal/taui/persistence/OSMPersistence.java +++ b/src/main/java/com/conveyal/taui/persistence/OSMPersistence.java @@ -3,6 +3,7 @@ import com.conveyal.osmlib.OSM; import com.conveyal.osmlib.OSMCache; import com.conveyal.taui.AnalysisServerConfig; +import com.conveyal.taui.AnalysisServerException; import com.conveyal.taui.models.Bounds; import com.conveyal.taui.util.HttpUtil; import com.google.common.io.ByteStreams; @@ -38,7 +39,7 @@ public static OSM retrieveOSMFromVexForBounds(Bounds bounds, String key) throws res = HttpUtil.httpClient.execute(get); if (res.getStatusLine().getStatusCode() != 200) { - throw new Exception("Could not retrieve OSM. " + res.getStatusLine()); + throw AnalysisServerException.Unknown("Could not retrieve OSM. " + res.getStatusLine()); } InputStream is = res.getEntity().getContent(); diff --git a/src/main/java/com/conveyal/taui/persistence/TiledAccessGrid.java b/src/main/java/com/conveyal/taui/persistence/TiledAccessGrid.java index c4aa7474..01cc38dc 100644 --- a/src/main/java/com/conveyal/taui/persistence/TiledAccessGrid.java +++ b/src/main/java/com/conveyal/taui/persistence/TiledAccessGrid.java @@ -6,6 +6,7 @@ import com.conveyal.r5.analyst.cluster.AccessGridWriter; import com.conveyal.r5.util.S3Util; import com.conveyal.taui.AnalysisServerConfig; +import com.conveyal.taui.AnalysisServerException; import com.conveyal.taui.util.JsonUtil; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -150,7 +151,7 @@ protected double computeValueForOrigin(int x, int y, int[] valuesThisOrigin, int TILE_SIZE, valuesThisOrigin.length); } catch (IOException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } }); @@ -166,7 +167,7 @@ protected double computeValueForOrigin(int x, int y, int[] valuesThisOrigin, int try { writer.writePixel(x % TILE_SIZE, y % TILE_SIZE, valuesThisOrigin); } catch (IOException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } return 0; @@ -190,7 +191,7 @@ protected double computeValueForOrigin(int x, int y, int[] valuesThisOrigin, int S3Util.s3.putObject(bucketName, key, input, metaData); } catch (IOException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } }); @@ -207,7 +208,7 @@ protected double computeValueForOrigin(int x, int y, int[] valuesThisOrigin, int LOG.info("Done saving tiled access grid to S3"); } catch (IOException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } } @@ -218,7 +219,7 @@ private void read () { this.header = JsonUtil.objectMapper.readValue(is, Header.class); is.close(); } catch (IOException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } } @@ -240,13 +241,13 @@ public int[] getGridCoordinates (int x, int y) { for (int i = 0; i < 8; i++) header[i] = tile.readByte(); String headerStr = new String(header); - if (!"ACCESSGR".equals(headerStr)) throw new IllegalStateException("Tile is not in Access Grid format!"); - if (tile.readInt() != 0) throw new IllegalStateException("Invalid access grid tile version!"); + if (!"ACCESSGR".equals(headerStr)) throw AnalysisServerException.BadRequest("Tile is not in Access Grid format!"); + if (tile.readInt() != 0) throw AnalysisServerException.BadRequest("Invalid access grid tile version!"); tile.seek(24); // seek to width int width = readIntLittleEndian(tile); int height = readIntLittleEndian(tile); - if (width != TILE_SIZE || height != TILE_SIZE) throw new IllegalStateException("Invalid access grid tile size!"); + if (width != TILE_SIZE || height != TILE_SIZE) throw AnalysisServerException.BadRequest("Invalid access grid tile size!"); long nValuesPerPixel = readIntLittleEndian(tile); // 4 bytes per value @@ -262,7 +263,7 @@ public int[] getGridCoordinates (int x, int y) { return values; } catch (IOException | ExecutionException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } } @@ -279,7 +280,7 @@ public static TiledAccessGrid get (String bucketName, String key) { try { return cache.get(compositeKey); } catch (ExecutionException e) { - throw new RuntimeException(e); + throw AnalysisServerException.Unknown(e); } }