Skip to content

Commit 6f296e6

Browse files
committed
Make sure offline routing doesn't crash.
1 parent 3375661 commit 6f296e6

2 files changed

Lines changed: 54 additions & 25 deletions

File tree

IsraelHiking.Web/android/app/src/main/java/com/mapeak/valhalla/ValhallaPlugin.kt

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class ValhallaPlugin : Plugin() {
4141
try {
4242
val result = tiles.extract(tarFileName, sliceId)
4343
call.resolve(JSObject().put("extractedFiles", result.extractedFiles).put("tilesDir", result.tilesDir))
44-
} catch (ex: Exception) {
45-
call.reject("Tile extraction failed: ${ex.message}", ex)
44+
} catch (ex: Throwable) {
45+
call.rejectWith("Tile extraction failed", ex)
4646
}
4747
}
4848

@@ -61,8 +61,8 @@ class ValhallaPlugin : Plugin() {
6161
try {
6262
profiles.store(content)
6363
call.resolve()
64-
} catch (ex: Exception) {
65-
call.reject("Failed to store the routing profiles: ${ex.message}", ex)
64+
} catch (ex: Throwable) {
65+
call.rejectWith("Failed to store the routing profiles", ex)
6666
}
6767
}
6868

@@ -89,8 +89,8 @@ class ValhallaPlugin : Plugin() {
8989
try {
9090
tiles.delete(sliceId)
9191
call.resolve()
92-
} catch (ex: Exception) {
93-
call.reject("Failed to delete the tiles of $sliceId: ${ex.message}", ex)
92+
} catch (ex: Throwable) {
93+
call.rejectWith("Failed to delete the tiles of $sliceId", ex)
9494
}
9595
}
9696

@@ -103,8 +103,8 @@ class ValhallaPlugin : Plugin() {
103103
try {
104104
tiles.clear()
105105
call.resolve()
106-
} catch (ex: Exception) {
107-
call.reject("Failed to clear the tiles: ${ex.message}", ex)
106+
} catch (ex: Throwable) {
107+
call.rejectWith("Failed to clear the tiles", ex)
108108
}
109109
}
110110

@@ -141,8 +141,17 @@ class ValhallaPlugin : Plugin() {
141141
tiles.tilesDir()
142142
)
143143
call.resolve(JSObject().put("raw", raw))
144-
} catch (ex: Exception) {
145-
call.reject("Valhalla route failed: ${ex.message}", ex)
144+
} catch (ex: Throwable) {
145+
call.rejectWith("Valhalla route failed", ex)
146146
}
147147
}
148+
149+
/**
150+
* Rejects a call with anything that was thrown - a failure to link or to reflect is an Error
151+
* rather than an Exception, and it should still reach the web layer rather than take the app
152+
* down with it.
153+
*/
154+
private fun PluginCall.rejectWith(message: String, error: Throwable) {
155+
reject("$message: ${error.message}", error as? Exception ?: RuntimeException(error))
156+
}
148157
}

IsraelHiking.Web/android/app/src/main/java/com/mapeak/valhalla/ValhallaRouter.kt

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ import com.valhalla.api.models.CostingOptions
1010
import com.valhalla.api.models.DistanceUnit
1111
import com.valhalla.api.models.PedestrianCostingOptions
1212
import com.valhalla.api.models.RouteRequest
13-
import com.valhalla.api.models.RouteResponse
1413
import com.valhalla.api.models.RoutingWaypoint
1514
import com.valhalla.config.ValhallaConfigBuilder
16-
import com.valhalla.valhalla.Valhalla
17-
import com.valhalla.valhalla.ValhallaResponse
15+
import com.valhalla.valhalla.config.ValhallaConfigManager
1816
import java.io.File
1917

2018
/**
@@ -38,25 +36,47 @@ class ValhallaRouter(private val context: Context) {
3836

3937
private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
4038
private val profiles by lazy { ValhallaProfiles(context) }
41-
42-
/** Built once with the tiles directory, the tiles themselves are read on every request */
43-
private var valhalla: Valhalla? = null
39+
private val configManager by lazy { ValhallaConfigManager(context) }
4440

4541
/**
4642
* Returns the valhalla response json - the web layer decodes the shape and the elevation.
4743
*/
4844
fun route(request: ValhallaRouteRequest, tilesDir: File): String {
49-
val response = engine(tilesDir).route(toRouteRequest(request))
50-
return when (response) {
51-
is ValhallaResponse.Json -> moshi.adapter(RouteResponse::class.java).toJson(response.jsonResponse)
52-
else -> throw IllegalStateException("Valhalla answered in a format that was not asked for")
53-
}
45+
val requestJson = moshi.adapter(RouteRequest::class.java).toJson(toRouteRequest(request))
46+
return routeRaw(requestJson, ensureConfig(tilesDir))
5447
}
5548

56-
private fun engine(tilesDir: File): Valhalla {
57-
return valhalla
58-
?: Valhalla(context, ValhallaConfigBuilder().withTileDir(tilesDir.absolutePath).build())
59-
.also { valhalla = it }
49+
/**
50+
* Sends an already serialized request to the native engine and returns its raw response.
51+
*
52+
* `Valhalla.route()` would be the way to do this, but the published valhalla-mobile is built
53+
* against valhalla-models 0.1.1, where `RouteRequest` still had a nested `DirectionsOptions`
54+
* that 0.2.0 flattened away, so calling it against any current models throws
55+
* NoSuchMethodError - see https://github.com/Rallista/valhalla-mobile/issues/60. The request
56+
* above is built and serialized here from the very same models, so nothing is skipped or
57+
* guessed, and this only reaches valhalla's own entry point, which is `internal`.
58+
*
59+
* Once valhalla-mobile is released against current models this whole method goes away, and
60+
* route() above becomes:
61+
* val response = Valhalla(context, config).route(toRouteRequest(request))
62+
*/
63+
private fun routeRaw(requestJson: String, configPath: String): String {
64+
val valhallaKotlinClass = Class.forName("com.valhalla.valhalla.ValhallaKotlin")
65+
val valhallaKotlin = valhallaKotlinClass.getDeclaredConstructor().newInstance()
66+
val routeMethod = valhallaKotlinClass.getMethod("route", String::class.java, String::class.java)
67+
return routeMethod.invoke(valhallaKotlin, requestJson, configPath) as String
68+
}
69+
70+
/**
71+
* Writes valhalla.json if it isn't there yet. The tiles directory never changes, so the config
72+
* stays valid when more slices are extracted into it.
73+
*/
74+
private fun ensureConfig(tilesDir: File): String {
75+
val configPath = configManager.getAbsolutePath()
76+
if (!File(configPath).exists()) {
77+
configManager.writeConfig(ValhallaConfigBuilder().withTileDir(tilesDir.absolutePath).build())
78+
}
79+
return configPath
6080
}
6181

6282
private fun toRouteRequest(request: ValhallaRouteRequest): RouteRequest {

0 commit comments

Comments
 (0)