Repository: graphhopper/graphhopper
Pull request #1378 (2018-05-25 10:41:30)

Split off api
This does three things about sub-modules of which 1.) and 2.) are necessary to cleanly finish the across-repository Dropwizardification, and 3.) happened on the way:

1.) Split off ``web-bundle`` from ``web``. This is minor and probably nobody will notice. Problem is that ``web`` builds into a shaded JAR (of which we don't use the shading), and such JARs are ugly to import from other projects, but we want to import the web services from other projects (to combine them to a single server), so the web services must go out of the module in which they are tied together.

2.) Split off ``api`` from ``core``. This is, I think, a major win, also for our mobile platforms, because it means that the ``client-hc`` now doesn't pull in the entire ``core`` as a dependency, which it was doing before. I simply moved all the things needed by the client _and_ the server _and_ the core, transitively, until I could remove ``core`` from ``client-hc``.

3.) Then, I also started moving some of the serialization and deserialization code to ``api``, see #1137.   But doing so requires a Jackson dependency in ``api``, which means we now also have it (transitively) in ``core``, in contrast to what the latest discussion in #1291 says. Read on:

- It is _possible_ to _not_ put serialization / deserialization code into an ``api`` module. But then, such code would have to go into _yet another_ module. Not framing this as "what dependencies do I need" but as "what does the module specify", it would mean that ``api`` contains the Java data classes and ``representation`` (or something) contains the wire format (JSON), of which there could be several. Again, this is _possible_, but the question is whether that would be practical, or maybe a bit purist. Actually, since GraphHopper is both a library and a REST service, it makes sense to say that our Java objects and our JSON representations are 1:1 and should be close together, see below.

- Allowing _on top of that_ the JSON _library_ to be replaced would be totally enterprisey and over-engineered. We use Dropwizard, and Dropwizard uses Jackson, and the whole idea is to remove technical/boring/distracting stuff like serialization from code that is supposed to be about routing, and in the age of frameworks, that is made _harder_ by wrapping it in custom interfaces, not easier.

- We are still not using JSON (de-)serialization in the ``core``, so we _can_ split off the additional ``representation`` module any time if we must. Actually, this was totally easy to do, without the custom JSON facade: ``GraphHopper`` core methods can just be passed deserialized objects like ``JsonFeatureCollection``, not JSON maps or filenames. Voilà. This is the start of another (different) discussion: Is GraphHopper ``core`` a _library_, or is it supposed to be exactly like ``web`` but without the web?
  - If the former, then it can stay like this: the user of the library can just de-serialize the JSON themselves (with a single call to Jackson, or whatever), or build the object in code, and pass the object, that's how pure libraries are supposed to be, and we can remove the Jackson dependency from ``api`` if we want, and from then on, ``GraphHopper`` only knows about Java objects, not about JSON or strings or files (except for ``GraphHopperStorage``).
  - If the latter, we can just close the deal and say, fine, ``core`` includes Jackson now, and it can read JSON files by itself, and that's it.



Patch:
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 8604ec2c3..5a04ef81f 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -20,7 +20,6 @@ android {
 
     lintOptions {
         /* CGIARProvider refers to java.awt
-         * Helper7 refers to java.lang.management
          * HeightTile refers to javax.imageio and java.awt
          * OSMElement refers to javax.xml.stream
          */
diff --git a/reader-json/pom.xml b/api/pom.xml
similarity index 57%
rename from reader-json/pom.xml
rename to api/pom.xml
index 492576cba..3f6a3b4da 100644
--- a/reader-json/pom.xml
+++ b/api/pom.xml
@@ -1,31 +1,42 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
+    <modelVersion>4.0.0</modelVersion>
     <groupId>com.graphhopper</groupId>
-    <artifactId>graphhopper-reader-json</artifactId>
-    <version>0.11-SNAPSHOT</version>
+    <artifactId>graphhopper-api</artifactId>
     <packaging>jar</packaging>
-    <name>GraphHopper Reader JSON</name>
+    <version>0.11-SNAPSHOT</version>
+    <name>GraphHopper API</name>
+    <description>Use the GraphHopper routing engine as a web-service</description>
 
     <parent>
         <groupId>com.graphhopper</groupId>
-        <artifactId>graphhopper-parent</artifactId>    	
+        <artifactId>graphhopper-parent</artifactId>
         <version>0.11-SNAPSHOT</version>
     </parent>
 
     <dependencies>
         <dependency>
-            <groupId>com.graphhopper</groupId>
-            <artifactId>graphhopper-core</artifactId>
-            <version>${project.parent.version}</version>
+            <groupId>javax.xml.ws</groupId>
+            <artifactId>jaxws-api</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <version>2.0.1</version>
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
             <version>${jackson.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.vividsolutions</groupId>
+            <artifactId>jts-core</artifactId>
+            <version>1.14.0</version>
+        </dependency>
         <dependency>
             <groupId>com.bedatadriven</groupId>
             <artifactId>jackson-datatype-jts</artifactId>
@@ -37,33 +48,28 @@
                 </exclusion>
             </exclusions>
         </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
-            <version>${slf4j.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>log4j</groupId>
-            <artifactId>log4j</artifactId>
-            <version>${log4j.version}</version>
-            <scope>test</scope>
-        </dependency>
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.12</version>
             <scope>test</scope>
         </dependency>
+    </dependencies>
 
-        <dependency>
-            <groupId>com.graphhopper</groupId>
-            <artifactId>graphhopper-core</artifactId>
-            <version>${project.parent.version}</version>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>   
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.5.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 
-    </dependencies>
-    
 </project>
+
+
diff --git a/core/src/main/java/com/graphhopper/GHRequest.java b/api/src/main/java/com/graphhopper/GHRequest.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/GHRequest.java
rename to api/src/main/java/com/graphhopper/GHRequest.java
diff --git a/core/src/main/java/com/graphhopper/GHResponse.java b/api/src/main/java/com/graphhopper/GHResponse.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/GHResponse.java
rename to api/src/main/java/com/graphhopper/GHResponse.java
diff --git a/core/src/main/java/com/graphhopper/GraphHopperAPI.java b/api/src/main/java/com/graphhopper/GraphHopperAPI.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/GraphHopperAPI.java
rename to api/src/main/java/com/graphhopper/GraphHopperAPI.java
diff --git a/core/src/main/java/com/graphhopper/PathWrapper.java b/api/src/main/java/com/graphhopper/PathWrapper.java
similarity index 91%
rename from core/src/main/java/com/graphhopper/PathWrapper.java
rename to api/src/main/java/com/graphhopper/PathWrapper.java
index 59306ff90..d74dbb3e7 100644
--- a/core/src/main/java/com/graphhopper/PathWrapper.java
+++ b/api/src/main/java/com/graphhopper/PathWrapper.java
@@ -18,7 +18,6 @@
 package com.graphhopper;
 
 import com.graphhopper.util.InstructionList;
-import com.graphhopper.util.PathMerger;
 import com.graphhopper.util.PointList;
 import com.graphhopper.util.details.PathDetail;
 import com.graphhopper.util.shapes.BBox;
@@ -246,13 +245,32 @@ public class PathWrapper {
         for (Map.Entry<String, List<PathDetail>> detailEntry : details.entrySet()) {
             if (this.pathDetails.containsKey(detailEntry.getKey())) {
                 List<PathDetail> pd = this.pathDetails.get(detailEntry.getKey());
-                PathMerger.merge(pd, detailEntry.getValue());
+                merge(pd, detailEntry.getValue());
             } else {
                 this.pathDetails.put(detailEntry.getKey(), detailEntry.getValue());
             }
         }
     }
 
+    /**
+     * Merges <code>otherDetails</code> into the <code>pathDetails</code>.
+     * <p>
+     * This method makes sure that Entry list around via points are merged correctly.
+     * See #1091 and the misplaced PathDetail after waypoints.
+     */
+    public static void merge(List<PathDetail> pathDetails, List<PathDetail> otherDetails) {
+        // Make sure that the PathDetail list is merged correctly at via points
+        if (!pathDetails.isEmpty() && !otherDetails.isEmpty()) {
+            PathDetail lastDetail = pathDetails.get(pathDetails.size() - 1);
+            if (lastDetail.getValue().equals(otherDetails.get(0).getValue())) {
+                lastDetail.setLast(otherDetails.get(0).getLast());
+                otherDetails.remove(0);
+            }
+        }
+
+        pathDetails.addAll(otherDetails);
+    }
+
     public Map<String, List<PathDetail>> getPathDetails() {
         return this.pathDetails;
     }
diff --git a/core/src/main/java/com/graphhopper/Trip.java b/api/src/main/java/com/graphhopper/Trip.java
similarity index 84%
rename from core/src/main/java/com/graphhopper/Trip.java
rename to api/src/main/java/com/graphhopper/Trip.java
index 7c6866d6a..43805069b 100644
--- a/core/src/main/java/com/graphhopper/Trip.java
+++ b/api/src/main/java/com/graphhopper/Trip.java
@@ -1,6 +1,5 @@
 package com.graphhopper;
 
-import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.InstructionList;
 import com.vividsolutions.jts.geom.Geometry;
 import com.vividsolutions.jts.geom.Point;
@@ -12,14 +11,12 @@ public class Trip {
     public static abstract class Leg {
         public final String type;
         public final String departureLocation;
-        public final List<EdgeIteratorState> edges;
         public final Geometry geometry;
         public final double distance;
 
-        public Leg(String type, String departureLocation, List<EdgeIteratorState> edges, Geometry geometry, double distance) {
+        public Leg(String type, String departureLocation, Geometry geometry, double distance) {
             this.type = type;
             this.departureLocation = departureLocation;
-            this.edges = edges;
             this.geometry = geometry;
             this.distance = distance;
         }
@@ -75,8 +72,8 @@ public class Trip {
         private final Date departureTime;
         private final Date arrivalTime;
 
-        public WalkLeg(String departureLocation, Date departureTime, List<EdgeIteratorState> edges, Geometry geometry, double distance, InstructionList instructions, Date arrivalTime) {
-            super("walk", departureLocation, edges, geometry, distance);
+        public WalkLeg(String departureLocation, Date departureTime, Geometry geometry, double distance, InstructionList instructions, Date arrivalTime) {
+            super("walk", departureLocation, geometry, distance);
             this.instructions = instructions;
             this.departureTime = departureTime;
             this.arrivalTime = arrivalTime;
@@ -102,13 +99,13 @@ public class Trip {
         public final String trip_id;
         public final String route_id;
 
-        public PtLeg(String feedId, boolean isInSameVehicleAsPrevious, String tripId, String routeId, List<EdgeIteratorState> edges, List<Stop> stops, double distance, long travelTime, Geometry geometry) {
-            super("pt", stops.get(0).stop_name, edges, geometry, distance);
+        public PtLeg(String feedId, boolean isInSameVehicleAsPrevious, String tripId, String routeId, String headsign, List<Stop> stops, double distance, long travelTime, Geometry geometry) {
+            super("pt", stops.get(0).stop_name, geometry, distance);
             this.feed_id = feedId;
             this.isInSameVehicleAsPrevious = isInSameVehicleAsPrevious;
             this.trip_id = tripId;
             this.route_id = routeId;
-            this.trip_headsign = edges.get(0).getName();
+            this.trip_headsign = headsign;
             this.travelTime = travelTime;
             this.stops = stops;
         }
diff --git a/web/src/main/java/com/graphhopper/http/WebHelper.java b/api/src/main/java/com/graphhopper/http/WebHelper.java
similarity index 97%
rename from web/src/main/java/com/graphhopper/http/WebHelper.java
rename to api/src/main/java/com/graphhopper/http/WebHelper.java
index b107746b2..abd31882d 100644
--- a/web/src/main/java/com/graphhopper/http/WebHelper.java
+++ b/api/src/main/java/com/graphhopper/http/WebHelper.java
@@ -40,8 +40,6 @@ import java.net.URLEncoder;
 import java.text.NumberFormat;
 import java.util.*;
 
-import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
-
 /**
  * Code which handles polyline encoding and other web stuff.
  * <p>
@@ -149,13 +147,13 @@ public class WebHelper {
         sb.append((char) (num));
     }
 
-    public static Response.ResponseBuilder gpxSuccessResponseBuilder(GHResponse ghRsp, String timeString, String trackName, boolean enableElevation, boolean withRoute, boolean withTrack, boolean withWayPoints) {
+    public static Response.ResponseBuilder gpxSuccessResponseBuilder(GHResponse ghRsp, String timeString, String trackName, boolean enableElevation, boolean withRoute, boolean withTrack, boolean withWayPoints, String version) {
         if (ghRsp.getAll().size() > 1) {
             throw new WebApplicationException("Alternatives are currently not yet supported for GPX");
         }
 
         long time = timeString != null ? Long.parseLong(timeString) : System.currentTimeMillis();
-        return Response.ok(ghRsp.getBest().getInstructions().createGPX(trackName, time, enableElevation, withRoute, withTrack, withWayPoints), "application/gpx+xml").
+        return Response.ok(ghRsp.getBest().getInstructions().createGPX(trackName, time, enableElevation, withRoute, withTrack, withWayPoints, version), "application/gpx+xml").
                 header("Content-Disposition", "attachment;filename=" + "GraphHopper.gpx");
     }
 
@@ -191,7 +189,7 @@ public class WebHelper {
                 error.setAttribute("message", t.getMessage());
                 error.setAttribute("details", t.getClass().getName());
             }
-            return Response.status(SC_BAD_REQUEST).entity(doc).build();
+            return Response.status(400).entity(doc).build();
         } catch (ParserConfigurationException e) {
             throw new RuntimeException(e);
         }
@@ -263,7 +261,7 @@ public class WebHelper {
                 ((GHException) t).getDetails().forEach(error::putPOJO);
             }
         }
-        return Response.status(SC_BAD_REQUEST).entity(json).build();
+        return Response.status(400).entity(json).build();
     }
 
     private static String getMessage(Throwable t) {
diff --git a/core/src/main/java/com/graphhopper/routing/util/HintsMap.java b/api/src/main/java/com/graphhopper/routing/util/HintsMap.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/routing/util/HintsMap.java
rename to api/src/main/java/com/graphhopper/routing/util/HintsMap.java
diff --git a/core/src/main/java/com/graphhopper/util/AngleCalc.java b/api/src/main/java/com/graphhopper/util/AngleCalc.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/AngleCalc.java
rename to api/src/main/java/com/graphhopper/util/AngleCalc.java
diff --git a/core/src/main/java/com/graphhopper/util/DistanceCalc.java b/api/src/main/java/com/graphhopper/util/DistanceCalc.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/DistanceCalc.java
rename to api/src/main/java/com/graphhopper/util/DistanceCalc.java
diff --git a/core/src/main/java/com/graphhopper/util/DistanceCalc3D.java b/api/src/main/java/com/graphhopper/util/DistanceCalc3D.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/DistanceCalc3D.java
rename to api/src/main/java/com/graphhopper/util/DistanceCalc3D.java
diff --git a/core/src/main/java/com/graphhopper/util/DistanceCalcEarth.java b/api/src/main/java/com/graphhopper/util/DistanceCalcEarth.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/DistanceCalcEarth.java
rename to api/src/main/java/com/graphhopper/util/DistanceCalcEarth.java
diff --git a/core/src/main/java/com/graphhopper/util/DistancePlaneProjection.java b/api/src/main/java/com/graphhopper/util/DistancePlaneProjection.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/DistancePlaneProjection.java
rename to api/src/main/java/com/graphhopper/util/DistancePlaneProjection.java
diff --git a/core/src/main/java/com/graphhopper/util/FinishInstruction.java b/api/src/main/java/com/graphhopper/util/FinishInstruction.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/FinishInstruction.java
rename to api/src/main/java/com/graphhopper/util/FinishInstruction.java
diff --git a/core/src/main/java/com/graphhopper/util/GPXEntry.java b/api/src/main/java/com/graphhopper/util/GPXEntry.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/GPXEntry.java
rename to api/src/main/java/com/graphhopper/util/GPXEntry.java
diff --git a/core/src/main/java/com/graphhopper/util/Helper.java b/api/src/main/java/com/graphhopper/util/Helper.java
similarity index 69%
rename from core/src/main/java/com/graphhopper/util/Helper.java
rename to api/src/main/java/com/graphhopper/util/Helper.java
index 9caf49225..de08e7310 100644
--- a/core/src/main/java/com/graphhopper/util/Helper.java
+++ b/api/src/main/java/com/graphhopper/util/Helper.java
@@ -17,25 +17,12 @@
  */
 package com.graphhopper.util;
 
-import com.carrotsearch.hppc.IntArrayList;
-import com.carrotsearch.hppc.IntIndexedContainer;
 import com.graphhopper.util.shapes.BBox;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.io.*;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import java.nio.MappedByteBuffer;
 import java.nio.charset.Charset;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
 import java.text.DateFormat;
 import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
@@ -43,11 +30,8 @@ import java.util.*;
 import java.util.Map.Entry;
 
 /**
- * Several utility classes which are compatible with Java6 on Android.
- * <p>
  *
  * @author Peter Karich
- * @see Helper7 for none-Android compatible methods.
  */
 public class Helper {
     public static final DistanceCalc DIST_EARTH = new DistanceCalcEarth();
@@ -57,7 +41,6 @@ public class Helper {
     public static final Charset UTF_CS = Charset.forName("UTF-8");
     public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
     public static final long MB = 1L << 20;
-    private static final Logger LOGGER = LoggerFactory.getLogger(Helper.class);
     // +- 180 and +-90 => let use use 400
     private static final float DEGREE_FACTOR = Integer.MAX_VALUE / 400f;
     // milli meter is a bit extreme but we have integers
@@ -66,15 +49,6 @@ public class Helper {
     private Helper() {
     }
 
-    public static ArrayList<Integer> intListToArrayList(IntIndexedContainer from) {
-        int len = from.size();
-        ArrayList<Integer> list = new ArrayList<>(len);
-        for (int i = 0; i < len; i++) {
-            list.add(from.get(i));
-        }
-        return list;
-    }
-
     public static Locale getLocale(String param) {
         int pointIndex = param.indexOf('.');
         if (pointIndex > 0)
@@ -112,34 +86,6 @@ public class Helper {
         return counter++;
     }
 
-    public static void loadProperties(Map<String, String> map, Reader tmpReader) throws IOException {
-        BufferedReader reader = new BufferedReader(tmpReader);
-        String line;
-        try {
-            while ((line = reader.readLine()) != null) {
-                if (line.startsWith("//") || line.startsWith("#")) {
-                    continue;
-                }
-
-                if (Helper.isEmpty(line)) {
-                    continue;
-                }
-
-                int index = line.indexOf("=");
-                if (index < 0) {
-                    LOGGER.warn("Skipping configuration at line:" + line);
-                    continue;
-                }
-
-                String field = line.substring(0, index);
-                String value = line.substring(index + 1);
-                map.put(field.trim(), value.trim());
-            }
-        } finally {
-            reader.close();
-        }
-    }
-
     public static void saveProperties(Map<String, String> map, Writer tmpWriter) throws IOException {
         BufferedWriter writer = new BufferedWriter(tmpWriter);
         try {
@@ -296,10 +242,6 @@ public class Helper {
         return list;
     }
 
-    public static IntArrayList createTList(int... list) {
-        return IntArrayList.from(list);
-    }
-
     public static PointList createPointList(double... list) {
         if (list.length % 2 != 0)
             throw new IllegalArgumentException("list should consist of lat,lon pairs!");
@@ -373,79 +315,6 @@ public class Helper {
         return integEle / ELE_FACTOR;
     }
 
-    public static void cleanMappedByteBuffer(final ByteBuffer buffer) {
-        // TODO avoid reflection on every call
-        try {
-            AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
-                @Override
-                public Object run() throws Exception {
-                    if (Constants.JRE_IS_MINIMUM_JAVA9) {
-                        // >=JDK9 class sun.misc.Unsafe { void invokeCleaner(ByteBuffer buf) }
-                        final Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
-                        // fetch the unsafe instance and bind it to the virtual MethodHandle
-                        final Field f = unsafeClass.getDeclaredField("theUnsafe");
-                        f.setAccessible(true);
-                        final Object theUnsafe = f.get(null);
-                        final Method method = unsafeClass.getDeclaredMethod("invokeCleaner", ByteBuffer.class);
-                        try {
-                            method.invoke(theUnsafe, buffer);
-                            return null;
-                        } catch (Throwable t) {
-                            throw new RuntimeException(t);
-                        }
-                    }
-
-                    if (buffer.getClass().getSimpleName().equals("MappedByteBufferAdapter")) {
-                        if (!Constants.ANDROID)
-                            throw new RuntimeException("MappedByteBufferAdapter only supported for Android at the moment");
-
-                        // For Android 4.1 call ((MappedByteBufferAdapter)buffer).free() see #914
-                        Class<?> directByteBufferClass = Class.forName("java.nio.MappedByteBufferAdapter");
-                        callBufferFree(buffer, directByteBufferClass);
-                    } else {
-                        // <=JDK8 class DirectByteBuffer { sun.misc.Cleaner cleaner(Buffer buf) }
-                        //        then call sun.misc.Cleaner.clean
-                        final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
-                        try {
-                            final Method dbbCleanerMethod = directByteBufferClass.getMethod("cleaner");
-                            dbbCleanerMethod.setAccessible(true);
-                            // call: cleaner = ((DirectByteBuffer)buffer).cleaner()
-                            final Object cleaner = dbbCleanerMethod.invoke(buffer);
-                            if (cleaner != null) {
-                                final Class<?> cleanerMethodReturnType = dbbCleanerMethod.getReturnType();
-                                final Method cleanMethod = cleanerMethodReturnType.getDeclaredMethod("clean");
-                                cleanMethod.setAccessible(true);
-                                // call: ((sun.misc.Cleaner)cleaner).clean()
-                                cleanMethod.invoke(cleaner);
-                            }
-                        } catch (NoSuchMethodException ex2) {
-                            if (Constants.ANDROID)
-                                // For Android 5.1.1 call ((DirectByteBuffer)buffer).free() see #933
-                                callBufferFree(buffer, directByteBufferClass);
-                            else
-                                // ignore if method cleaner or clean is not available
-                                LOGGER.warn("NoSuchMethodException | " + Constants.JAVA_VERSION, ex2);
-                        }
-                    }
-
-                    return null;
-                }
-            });
-        } catch (PrivilegedActionException e) {
-            throw new RuntimeException("Unable to unmap the mapped buffer", e);
-        }
-    }
-
-    private static void callBufferFree(ByteBuffer buffer, Class<?> directByteBufferClass)
-            throws InvocationTargetException, IllegalAccessException {
-        try {
-            final Method dbbFreeMethod = directByteBufferClass.getMethod("free");
-            dbbFreeMethod.setAccessible(true);
-            dbbFreeMethod.invoke(buffer);
-        } catch (NoSuchMethodException ex2) {
-            LOGGER.warn("NoSuchMethodException | " + Constants.JAVA_VERSION, ex2);
-        }
-    }
 
     /**
      * Trying to force the release of the mapped ByteBuffer. See
diff --git a/core/src/main/java/com/graphhopper/util/Instruction.java b/api/src/main/java/com/graphhopper/util/Instruction.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/Instruction.java
rename to api/src/main/java/com/graphhopper/util/Instruction.java
diff --git a/core/src/main/java/com/graphhopper/util/InstructionAnnotation.java b/api/src/main/java/com/graphhopper/util/InstructionAnnotation.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/InstructionAnnotation.java
rename to api/src/main/java/com/graphhopper/util/InstructionAnnotation.java
diff --git a/core/src/main/java/com/graphhopper/util/InstructionList.java b/api/src/main/java/com/graphhopper/util/InstructionList.java
similarity index 97%
rename from core/src/main/java/com/graphhopper/util/InstructionList.java
rename to api/src/main/java/com/graphhopper/util/InstructionList.java
index 70eab2ee1..71fa3108b 100644
--- a/core/src/main/java/com/graphhopper/util/InstructionList.java
+++ b/api/src/main/java/com/graphhopper/util/InstructionList.java
@@ -148,13 +148,13 @@ public class InstructionList extends AbstractList<Instruction> {
      *
      * @return string to be stored as gpx file
      */
-    public String createGPX() {
-        return createGPX("GraphHopper", new Date().getTime());
+    public String createGPX(String version) {
+        return createGPX("GraphHopper", new Date().getTime(), version);
     }
 
-    public String createGPX(String trackName, long startTimeMillis) {
+    public String createGPX(String trackName, long startTimeMillis, String version) {
         boolean includeElevation = size() > 0 && get(0).getPoints().is3D();
-        return createGPX(trackName, startTimeMillis, includeElevation, true, true, true);
+        return createGPX(trackName, startTimeMillis, includeElevation, true, true, true, version);
     }
 
     private void createWayPointBlock(StringBuilder output, Instruction instruction, DecimalFormat decimalFormat) {
@@ -171,7 +171,7 @@ public class InstructionList extends AbstractList<Instruction> {
         output.append("</wpt>");
     }
 
-    public String createGPX(String trackName, long startTimeMillis, boolean includeElevation, boolean withRoute, boolean withTrack, boolean withWayPoints) {
+    public String createGPX(String trackName, long startTimeMillis, boolean includeElevation, boolean withRoute, boolean withTrack, boolean withWayPoints, String version) {
         DateFormat formatter = Helper.createFormatter();
 
         DecimalFormat decimalFormat = new DecimalFormat("#", DecimalFormatSymbols.getInstance(Locale.ROOT));
@@ -181,7 +181,7 @@ public class InstructionList extends AbstractList<Instruction> {
 
         String header = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>"
                 + "<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
-                + " creator=\"Graphhopper version " + Constants.VERSION + "\" version=\"1.1\""
+                + " creator=\"Graphhopper version " + version + "\" version=\"1.1\""
                 // This xmlns:gh acts only as ID, no valid URL necessary.
                 // Use a separate namespace for custom extensions to make basecamp happy.
                 + " xmlns:gh=\"https://graphhopper.com/public/schema/gpx/1.1\">"
diff --git a/core/src/main/java/com/graphhopper/util/NumHelper.java b/api/src/main/java/com/graphhopper/util/NumHelper.java
similarity index 85%
rename from core/src/main/java/com/graphhopper/util/NumHelper.java
rename to api/src/main/java/com/graphhopper/util/NumHelper.java
index 8ee33be99..7f16dd203 100644
--- a/core/src/main/java/com/graphhopper/util/NumHelper.java
+++ b/api/src/main/java/com/graphhopper/util/NumHelper.java
@@ -39,11 +39,4 @@ public class NumHelper {
         return Double.compare(d1, d2);
     }
 
-    public static boolean equalsEps(float d1, float d2) {
-        return equalsEps(d1, d2, DEFAULT_PRECISION);
-    }
-
-    public static boolean equalsEps(float d1, float d2, float epsilon) {
-        return Math.abs(d1 - d2) < epsilon;
-    }
 }
diff --git a/reader-json/src/main/java/com/graphhopper/json/GHJsonFactory.java b/api/src/main/java/com/graphhopper/util/ObjectMapperFactory.java
similarity index 80%
rename from reader-json/src/main/java/com/graphhopper/json/GHJsonFactory.java
rename to api/src/main/java/com/graphhopper/util/ObjectMapperFactory.java
index b27ffa900..b90b91485 100644
--- a/reader-json/src/main/java/com/graphhopper/json/GHJsonFactory.java
+++ b/api/src/main/java/com/graphhopper/util/ObjectMapperFactory.java
@@ -15,20 +15,15 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper.json;
+package com.graphhopper.util;
 
 import com.bedatadriven.jackson.datatype.jts.JtsModule;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
-/**
- * This class wraps the creation of the specific GHJson implementation.
- *
- * @author Peter Karich
- */
-public class GHJsonFactory {
-    public GHJson create() {
+public class ObjectMapperFactory {
+    public static ObjectMapper create() {
         ObjectMapper objectMapper = new ObjectMapper();
         objectMapper.registerModule(new JtsModule());
-        return new GHJsonJackson(objectMapper);
+        return objectMapper;
     }
 }
diff --git a/core/src/main/java/com/graphhopper/util/PMap.java b/api/src/main/java/com/graphhopper/util/PMap.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/PMap.java
rename to api/src/main/java/com/graphhopper/util/PMap.java
diff --git a/core/src/main/java/com/graphhopper/util/Parameters.java b/api/src/main/java/com/graphhopper/util/Parameters.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/Parameters.java
rename to api/src/main/java/com/graphhopper/util/Parameters.java
diff --git a/core/src/main/java/com/graphhopper/util/PointAccess.java b/api/src/main/java/com/graphhopper/util/PointAccess.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/PointAccess.java
rename to api/src/main/java/com/graphhopper/util/PointAccess.java
diff --git a/core/src/main/java/com/graphhopper/util/PointList.java b/api/src/main/java/com/graphhopper/util/PointList.java
similarity index 94%
rename from core/src/main/java/com/graphhopper/util/PointList.java
rename to api/src/main/java/com/graphhopper/util/PointList.java
index d30ce6f8a..8788410b5 100644
--- a/core/src/main/java/com/graphhopper/util/PointList.java
+++ b/api/src/main/java/com/graphhopper/util/PointList.java
@@ -152,7 +152,8 @@ public class PointList implements Iterable<GHPoint3D>, PointAccess {
             throw new UnsupportedOperationException("cannot access EMPTY PointList");
         }
     };
-    private final static DistanceCalc3D distCalc3D = Helper.DIST_3D;
+
+    private final static DistanceCalc3D distCalc3D = new DistanceCalc3D();
     final static String ERR_MSG = "Tried to access PointList with too big index!";
     protected int size = 0;
     protected boolean is3D;
@@ -410,16 +411,24 @@ public class PointList implements Iterable<GHPoint3D>, PointAccess {
         for (int i = 0; i < getSize(); i++) {
             coordinates[i] = includeElevation ?
                     new Coordinate(
-                            Helper.round6(getLongitude(i)),
-                            Helper.round6(getLatitude(i)),
-                            Helper.round2(getElevation(i))) :
+                            round6(getLongitude(i)),
+                            round6(getLatitude(i)),
+                            round2(getElevation(i))) :
                     new Coordinate(
-                            Helper.round6(getLongitude(i)),
-                            Helper.round6(getLatitude(i)));
+                            round6(getLongitude(i)),
+                            round6(getLatitude(i)));
         }
         return gf.createLineString(coordinates);
     }
 
+    public static final double round6(double value) {
+        return Math.round(value * 1e6) / 1e6;
+    }
+
+    public static final double round2(double value) {
+        return Math.round(value * 100) / 100d;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj == null)
@@ -433,18 +442,28 @@ public class PointList implements Iterable<GHPoint3D>, PointAccess {
             return false;
 
         for (int i = 0; i < size(); i++) {
-            if (!NumHelper.equalsEps(getLatitude(i), other.getLatitude(i)))
+            if (!equalsEps(getLatitude(i), other.getLatitude(i)))
                 return false;
 
-            if (!NumHelper.equalsEps(getLongitude(i), other.getLongitude(i)))
+            if (!equalsEps(getLongitude(i), other.getLongitude(i)))
                 return false;
 
-            if (this.is3D() && !NumHelper.equalsEps(getElevation(i), other.getElevation(i)))
+            if (this.is3D() && !equalsEps(getElevation(i), other.getElevation(i)))
                 return false;
         }
         return true;
     }
 
+    private final static double DEFAULT_PRECISION = 1e-6;
+
+    public static boolean equalsEps(double d1, double d2) {
+        return equalsEps(d1, d2, DEFAULT_PRECISION);
+    }
+
+    public static boolean equalsEps(double d1, double d2, double epsilon) {
+        return Math.abs(d1 - d2) < epsilon;
+    }
+
     /**
      * Clones this PointList. If this PointList was immutable, the cloned will be mutable. If this PointList was a
      * ShallowImmutablePointList, the cloned PointList will be a regular PointList.
diff --git a/core/src/main/java/com/graphhopper/util/RoundaboutInstruction.java b/api/src/main/java/com/graphhopper/util/RoundaboutInstruction.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/RoundaboutInstruction.java
rename to api/src/main/java/com/graphhopper/util/RoundaboutInstruction.java
diff --git a/core/src/main/java/com/graphhopper/util/ShallowImmutablePointList.java b/api/src/main/java/com/graphhopper/util/ShallowImmutablePointList.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/ShallowImmutablePointList.java
rename to api/src/main/java/com/graphhopper/util/ShallowImmutablePointList.java
diff --git a/core/src/main/java/com/graphhopper/util/Translation.java b/api/src/main/java/com/graphhopper/util/Translation.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/Translation.java
rename to api/src/main/java/com/graphhopper/util/Translation.java
diff --git a/core/src/main/java/com/graphhopper/util/ViaInstruction.java b/api/src/main/java/com/graphhopper/util/ViaInstruction.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/ViaInstruction.java
rename to api/src/main/java/com/graphhopper/util/ViaInstruction.java
diff --git a/core/src/main/java/com/graphhopper/util/details/PathDetail.java b/api/src/main/java/com/graphhopper/util/details/PathDetail.java
similarity index 84%
rename from core/src/main/java/com/graphhopper/util/details/PathDetail.java
rename to api/src/main/java/com/graphhopper/util/details/PathDetail.java
index 9f091da6c..b3ed3ec30 100644
--- a/core/src/main/java/com/graphhopper/util/details/PathDetail.java
+++ b/api/src/main/java/com/graphhopper/util/details/PathDetail.java
@@ -1,10 +1,15 @@
 package com.graphhopper.util.details;
 
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
 /**
  * A detail information of a Path
  *
  * @author Robin Boldt
  */
+@JsonSerialize(using = PathDetailSerializer.class)
+@JsonDeserialize(using = PathDetailDeserializer.class)
 public class PathDetail {
     private final Object value;
     private int first;
diff --git a/api/src/main/java/com/graphhopper/util/details/PathDetailDeserializer.java b/api/src/main/java/com/graphhopper/util/details/PathDetailDeserializer.java
new file mode 100644
index 000000000..4557a6f93
--- /dev/null
+++ b/api/src/main/java/com/graphhopper/util/details/PathDetailDeserializer.java
@@ -0,0 +1,41 @@
+package com.graphhopper.util.details;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+
+public class PathDetailDeserializer extends JsonDeserializer<PathDetail> {
+
+    @Override
+    public PathDetail deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+        JsonNode pathDetail = jp.readValueAsTree();
+        if (pathDetail.size() != 3)
+            throw new JsonParseException(jp, "PathDetail array must have exactly 3 entries but was " + pathDetail.size());
+
+        JsonNode from = pathDetail.get(0);
+        JsonNode to = pathDetail.get(1);
+        JsonNode val = pathDetail.get(2);
+
+        PathDetail pd;
+        if (val.isBoolean())
+            pd = new PathDetail(val.asBoolean());
+        else if (val.isLong())
+            pd = new PathDetail(val.asLong());
+        else if (val.isInt())
+            pd = new PathDetail(val.asInt());
+        else if (val.isDouble())
+            pd = new PathDetail(val.asDouble());
+        else if (val.isTextual())
+            pd = new PathDetail(val.asText());
+        else
+            throw new JsonParseException(jp, "Unsupported type of PathDetail value " + pathDetail.getNodeType().name());
+
+        pd.setFirst(from.asInt());
+        pd.setLast(to.asInt());
+        return pd;
+    }
+}
diff --git a/api/src/main/java/com/graphhopper/util/details/PathDetailSerializer.java b/api/src/main/java/com/graphhopper/util/details/PathDetailSerializer.java
new file mode 100644
index 000000000..17e257688
--- /dev/null
+++ b/api/src/main/java/com/graphhopper/util/details/PathDetailSerializer.java
@@ -0,0 +1,34 @@
+package com.graphhopper.util.details;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+
+public class PathDetailSerializer extends JsonSerializer<PathDetail> {
+
+    @Override
+    public void serialize(PathDetail value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        gen.writeStartArray();
+
+        gen.writeNumber(value.getFirst());
+        gen.writeNumber(value.getLast());
+
+        if (value.getValue() instanceof Double)
+            gen.writeNumber((Double) value.getValue());
+        else if (value.getValue() instanceof Long)
+            gen.writeNumber((Long) value.getValue());
+        else if (value.getValue() instanceof Integer)
+            gen.writeNumber((Integer) value.getValue());
+        else if (value.getValue() instanceof Boolean)
+            gen.writeBoolean((Boolean) value.getValue());
+        else if (value.getValue() instanceof String)
+            gen.writeString((String) value.getValue());
+        else
+            throw new JsonGenerationException("Unsupported type for PathDetail.value" + value.getValue().getClass(), gen);
+
+        gen.writeEndArray();
+    }
+}
diff --git a/core/src/test/java/com/graphhopper/util/InstructionTest.java b/api/src/test/java/com/graphhopper/util/InstructionTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/InstructionTest.java
rename to api/src/test/java/com/graphhopper/util/InstructionTest.java
diff --git a/core/src/test/java/com/graphhopper/util/PMapTest.java b/api/src/test/java/com/graphhopper/util/PMapTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/PMapTest.java
rename to api/src/test/java/com/graphhopper/util/PMapTest.java
diff --git a/core/src/test/java/com/graphhopper/util/PointListTest.java b/api/src/test/java/com/graphhopper/util/PointListTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/PointListTest.java
rename to api/src/test/java/com/graphhopper/util/PointListTest.java
diff --git a/core/src/test/java/com/graphhopper/util/shapes/BBoxTest.java b/api/src/test/java/com/graphhopper/util/shapes/BBoxTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/shapes/BBoxTest.java
rename to api/src/test/java/com/graphhopper/util/shapes/BBoxTest.java
diff --git a/core/src/test/java/com/graphhopper/util/shapes/CircleTest.java b/api/src/test/java/com/graphhopper/util/shapes/CircleTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/shapes/CircleTest.java
rename to api/src/test/java/com/graphhopper/util/shapes/CircleTest.java
diff --git a/core/src/test/java/com/graphhopper/util/shapes/GHPoint3DTest.java b/api/src/test/java/com/graphhopper/util/shapes/GHPoint3DTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/shapes/GHPoint3DTest.java
rename to api/src/test/java/com/graphhopper/util/shapes/GHPoint3DTest.java
diff --git a/core/src/test/java/com/graphhopper/util/shapes/GHPointTest.java b/api/src/test/java/com/graphhopper/util/shapes/GHPointTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/shapes/GHPointTest.java
rename to api/src/test/java/com/graphhopper/util/shapes/GHPointTest.java
diff --git a/core/src/test/java/com/graphhopper/util/shapes/PolygonTest.java b/api/src/test/java/com/graphhopper/util/shapes/PolygonTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/shapes/PolygonTest.java
rename to api/src/test/java/com/graphhopper/util/shapes/PolygonTest.java
diff --git a/client-hc/pom.xml b/client-hc/pom.xml
index 2817017b2..18334fd17 100644
--- a/client-hc/pom.xml
+++ b/client-hc/pom.xml
@@ -36,27 +36,8 @@
     <dependencies>
         <dependency>
             <groupId>com.graphhopper</groupId>
-            <artifactId>graphhopper-core</artifactId>
+            <artifactId>graphhopper-api</artifactId>
             <version>${project.parent.version}</version>
-            <exclusions>
-                <!-- We cannot yet exclude this as e.g. Helper class uses trove classes
-                     and Helper is used e.g. in PointList
-                <exclusion>
-                    <groupId>net.sf.trove4j</groupId>
-                    <artifactId>trove4j</artifactId>
-                </exclusion>
-                -->
-                <exclusion>
-                    <groupId>org.apache.xmlgraphics</groupId>
-                    <artifactId>xmlgraphics-commons</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-            <version>${jackson.version}</version>
         </dependency>
         <dependency>
             <groupId>javax.xml.ws</groupId>
diff --git a/client-hc/src/main/java/com/graphhopper/api/GHMResponse.java b/client-hc/src/main/java/com/graphhopper/api/GHMResponse.java
deleted file mode 100644
index b60c21a2c..000000000
--- a/client-hc/src/main/java/com/graphhopper/api/GHMResponse.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.graphhopper.api;
-
-import com.graphhopper.GHResponse;
-
-/**
- * @author Peter Karich
- */
-public class GHMResponse extends GHResponse {
-
-    private final int fromIndex;
-    private final int toIndex;
-    private final boolean identicalStartAndEnd;
-
-    public GHMResponse(int fromIndex, int toIndex, boolean identicalStartAndEnd) {
-        this.fromIndex = fromIndex;
-        this.toIndex = toIndex;
-        this.identicalStartAndEnd = identicalStartAndEnd;
-    }
-
-    public boolean isIdenticalStartAndEnd() {
-        return identicalStartAndEnd;
-    }
-
-    public int getFromIndex() {
-        return fromIndex;
-    }
-
-    public int getToIndex() {
-        return toIndex;
-    }
-}
diff --git a/client-hc/src/main/java/com/graphhopper/api/GoogleMatrixSyncRequester.java b/client-hc/src/main/java/com/graphhopper/api/GoogleMatrixSyncRequester.java
index 59a9684da..9291593b8 100644
--- a/client-hc/src/main/java/com/graphhopper/api/GoogleMatrixSyncRequester.java
+++ b/client-hc/src/main/java/com/graphhopper/api/GoogleMatrixSyncRequester.java
@@ -13,11 +13,6 @@ import java.util.List;
  */
 public class GoogleMatrixSyncRequester extends GHMatrixAbstractRequester {
 
-    public GoogleMatrixSyncRequester() {
-        super();
-        initIgnore();
-    }
-
     public GoogleMatrixSyncRequester(String serviceUrl) {
         super(serviceUrl);
         initIgnore();
diff --git a/client-hc/src/main/java/com/graphhopper/api/GraphHopperGeocoding.java b/client-hc/src/main/java/com/graphhopper/api/GraphHopperGeocoding.java
index c35115648..e0d27a3e7 100644
--- a/client-hc/src/main/java/com/graphhopper/api/GraphHopperGeocoding.java
+++ b/client-hc/src/main/java/com/graphhopper/api/GraphHopperGeocoding.java
@@ -20,6 +20,7 @@ package com.graphhopper.api;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.graphhopper.api.model.GHGeocodingRequest;
 import com.graphhopper.api.model.GHGeocodingResponse;
+import com.graphhopper.http.WebHelper;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.ResponseBody;
diff --git a/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java b/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java
index f1bab6ba5..a0e7aeabc 100644
--- a/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java
+++ b/client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java
@@ -24,6 +24,7 @@ import com.graphhopper.GHRequest;
 import com.graphhopper.GHResponse;
 import com.graphhopper.GraphHopperAPI;
 import com.graphhopper.PathWrapper;
+import com.graphhopper.http.WebHelper;
 import com.graphhopper.util.*;
 import com.graphhopper.util.details.PathDetail;
 import com.graphhopper.util.exceptions.*;
@@ -31,13 +32,12 @@ import com.graphhopper.util.shapes.GHPoint;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.ResponseBody;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 
-import static com.graphhopper.util.Helper.*;
+import static com.graphhopper.util.Helper.round6;
+import static com.graphhopper.util.Helper.toLowerCase;
 
 /**
  * Main wrapper of the GraphHopper Directions API for a simple and efficient
@@ -54,7 +54,6 @@ public class GraphHopperWeb implements GraphHopperAPI {
     private boolean instructions = true;
     private boolean calcPoints = true;
     private boolean elevation = false;
-    private boolean turnDescription = true;
     private String optimize = "false";
     private final Set<String> ignoreSet;
 
@@ -103,10 +102,7 @@ public class GraphHopperWeb implements GraphHopperAPI {
         return downloader;
     }
 
-    PathWrapper createPathWrapper(JsonNode path,
-                                  boolean tmpCalcPoints, boolean tmpInstructions,
-                                  boolean tmpElevation, boolean turnDescription,
-                                  boolean tmpCalcDetails) {
+    PathWrapper createPathWrapper(JsonNode path, boolean tmpElevation, boolean turnDescription) {
         PathWrapper pathWrapper = new PathWrapper();
         pathWrapper.addErrors(readErrors(path));
         if (pathWrapper.hasErrors())
@@ -140,12 +136,12 @@ public class GraphHopperWeb implements GraphHopperAPI {
             }
         }
 
-        if (tmpCalcPoints) {
+        if (path.has("points")) {
             String pointStr = path.get("points").asText();
             PointList pointList = WebHelper.decodePolyline(pointStr, 100, tmpElevation);
             pathWrapper.setPoints(pointList);
 
-            if (tmpInstructions) {
+            if (path.has("instructions")) {
                 JsonNode instrArr = path.get("instructions");
 
                 InstructionList il = new InstructionList(null);
@@ -218,40 +214,15 @@ public class GraphHopperWeb implements GraphHopperAPI {
                 pathWrapper.setInstructions(il);
             }
 
-            if (tmpCalcDetails) {
+            if (path.has("details")) {
                 JsonNode details = path.get("details");
                 Map<String, List<PathDetail>> pathDetails = new HashMap<>(details.size());
                 Iterator<Map.Entry<String, JsonNode>> detailIterator = details.fields();
                 while (detailIterator.hasNext()) {
                     Map.Entry<String, JsonNode> detailEntry = detailIterator.next();
                     List<PathDetail> pathDetailList = new ArrayList<>();
-
-                    // TODO duplicate deserialization code for PathDetail, @see GraphHopperServletModule.PathDetailDeserializer
-                    // see issue #1137
                     for (JsonNode pathDetail : detailEntry.getValue()) {
-                        if (pathDetail.size() != 3)
-                            throw new IllegalStateException("PathDetail must have exactly 3 entries but was " + pathDetail.size());
-
-                        JsonNode from = pathDetail.get(0);
-                        JsonNode to = pathDetail.get(1);
-                        JsonNode val = pathDetail.get(2);
-
-                        PathDetail pd;
-                        if (val.isBoolean())
-                            pd = new PathDetail(val.asBoolean());
-                        else if (val.isLong())
-                            pd = new PathDetail(val.asLong());
-                        else if (val.isInt())
-                            pd = new PathDetail(val.asInt());
-                        else if (val.isDouble())
-                            pd = new PathDetail(val.asDouble());
-                        else if (val.isTextual())
-                            pd = new PathDetail(val.asText());
-                        else
-                            throw new IllegalStateException("Unsupported type of PathDetail value for key " + detailEntry.getKey() + " and value " + val.toString() + "of class " + val.getClass());
-
-                        pd.setFirst(from.asInt());
-                        pd.setLast(to.asInt());
+                        PathDetail pd = objectMapper.convertValue(pathDetail, PathDetail.class);
                         pathDetailList.add(pd);
                     }
                     pathDetails.put(detailEntry.getKey(), pathDetailList);
@@ -392,13 +363,11 @@ public class GraphHopperWeb implements GraphHopperAPI {
 
             JsonNode paths = json.get("paths");
 
-            boolean tmpInstructions = request.getHints().getBool("instructions", instructions);
-            boolean tmpCalcPoints = request.getHints().getBool("calc_points", calcPoints);
             boolean tmpElevation = request.getHints().getBool("elevation", elevation);
-            boolean tmpTurnDescription = request.getHints().getBool("turn_description", turnDescription);
+            boolean tmpTurnDescription = request.getHints().getBool("turn_description", true);
 
             for (JsonNode path : paths) {
-                PathWrapper altRsp = createPathWrapper(path, tmpCalcPoints, tmpInstructions, tmpElevation, tmpTurnDescription, !request.getPathDetails().isEmpty());
+                PathWrapper altRsp = createPathWrapper(path, tmpElevation, tmpTurnDescription);
                 res.add(altRsp);
             }
 
diff --git a/client-hc/src/main/java/com/graphhopper/api/WebHelper.java b/client-hc/src/main/java/com/graphhopper/api/WebHelper.java
deleted file mode 100644
index a7df96be3..000000000
--- a/client-hc/src/main/java/com/graphhopper/api/WebHelper.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package com.graphhopper.api;
-
-import com.graphhopper.util.PointList;
-
-import java.net.URLEncoder;
-
-/**
- * Code which handles polyline encoding and other web stuff.
- * <p>
- *
- * @author Peter Karich
- */
-public class WebHelper {
-
-    public static String encodeURL(String str) {
-        try {
-            return URLEncoder.encode(str, "UTF-8");
-        } catch (Exception _ignore) {
-            return str;
-        }
-    }
-
-    public static PointList decodePolyline(String encoded, int initCap, boolean is3D) {
-        PointList poly = new PointList(initCap, is3D);
-        int index = 0;
-        int len = encoded.length();
-        int lat = 0, lng = 0, ele = 0;
-        while (index < len) {
-            // latitude
-            int b, shift = 0, result = 0;
-            do {
-                b = encoded.charAt(index++) - 63;
-                result |= (b & 0x1f) << shift;
-                shift += 5;
-            } while (b >= 0x20);
-            int deltaLatitude = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
-            lat += deltaLatitude;
-
-            // longitute
-            shift = 0;
-            result = 0;
-            do {
-                b = encoded.charAt(index++) - 63;
-                result |= (b & 0x1f) << shift;
-                shift += 5;
-            } while (b >= 0x20);
-            int deltaLongitude = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
-            lng += deltaLongitude;
-
-            if (is3D) {
-                // elevation
-                shift = 0;
-                result = 0;
-                do {
-                    b = encoded.charAt(index++) - 63;
-                    result |= (b & 0x1f) << shift;
-                    shift += 5;
-                } while (b >= 0x20);
-                int deltaElevation = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
-                ele += deltaElevation;
-                poly.add((double) lat / 1e5, (double) lng / 1e5, (double) ele / 100);
-            } else {
-                poly.add((double) lat / 1e5, (double) lng / 1e5);
-            }
-        }
-        return poly;
-    }
-}
diff --git a/core/pom.xml b/core/pom.xml
index 991336863..23a8520a3 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -36,6 +36,11 @@
         </license>
     </licenses>
     <dependencies>
+        <dependency>
+            <groupId>com.graphhopper</groupId>
+            <artifactId>graphhopper-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
         <dependency>
             <groupId>com.carrotsearch</groupId>
             <artifactId>hppc</artifactId>
@@ -60,25 +65,6 @@
             </exclusions>
         </dependency>
 
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-annotations</artifactId>
-            <version>${jackson.version}</version>
-        </dependency>
-        
-        <dependency>
-            <groupId>com.vividsolutions</groupId>
-            <artifactId>jts-core</artifactId>
-            <version>1.14.0</version>
-        </dependency>
-
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-            <version>${jackson.version}</version>
-            <scope>test</scope>
-        </dependency>
-
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
diff --git a/core/src/main/java/com/graphhopper/json/GHJson.java b/core/src/main/java/com/graphhopper/json/GHJson.java
deleted file mode 100644
index f336aa762..000000000
--- a/core/src/main/java/com/graphhopper/json/GHJson.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package com.graphhopper.json;
-
-import java.io.Reader;
-
-/**
- * A simple JSON (de)serialization facade. E.g. to be easily replaced with platform specific
- * implementations.
- *
- * @author Peter Karich
- */
-public interface GHJson {
-    /**
-     * This method reads JSON data from the provided source and creates an instance of the provided
-     * class.
-     */
-    <T> T fromJson(Reader source, Class<T> aClass);
-}
diff --git a/core/src/main/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupHelper.java b/core/src/main/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupHelper.java
new file mode 100644
index 000000000..f6f07184f
--- /dev/null
+++ b/core/src/main/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupHelper.java
@@ -0,0 +1,38 @@
+package com.graphhopper.routing.util.spatialrules;
+
+import com.graphhopper.GraphHopper;
+import com.graphhopper.json.geo.JsonFeatureCollection;
+import com.graphhopper.routing.util.DataFlagEncoder;
+import com.graphhopper.routing.util.FlagEncoder;
+import com.graphhopper.routing.util.FlagEncoderFactory;
+import com.graphhopper.util.PMap;
+import com.graphhopper.util.shapes.BBox;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class to build the spatial rule index
+ *
+ * @author Robin Boldt
+ */
+public class SpatialRuleLookupHelper {
+
+    private static final Logger logger = LoggerFactory.getLogger(SpatialRuleLookupHelper.class);
+
+    public static void buildAndInjectSpatialRuleIntoGH(GraphHopper graphHopper, BBox maxBounds, JsonFeatureCollection jsonFeatureCollection) {
+        final SpatialRuleLookup index = SpatialRuleLookupBuilder.buildIndex(jsonFeatureCollection, "ISO_A3", new CountriesSpatialRuleFactory(), .1, maxBounds);
+        logger.info("Set spatial rule lookup with " + index.size() + " rules");
+        final FlagEncoderFactory oldFEF = graphHopper.getFlagEncoderFactory();
+        graphHopper.setFlagEncoderFactory(new FlagEncoderFactory() {
+            @Override
+            public FlagEncoder createFlagEncoder(String name, PMap configuration) {
+                if (name.equals(GENERIC)) {
+                    return new DataFlagEncoder(configuration).setSpatialRuleLookup(index);
+                }
+
+                return oldFEF.createFlagEncoder(name, configuration);
+            }
+        });
+    }
+
+}
diff --git a/core/src/main/java/com/graphhopper/storage/IntIterator.java b/core/src/main/java/com/graphhopper/storage/IntIterator.java
deleted file mode 100644
index 90e745533..000000000
--- a/core/src/main/java/com/graphhopper/storage/IntIterator.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package com.graphhopper.storage;
-
-/**
- * @author Peter Karich
- */
-public interface IntIterator {
-    boolean next();
-
-    int getValue();
-
-    void remove();
-
-    class Helper {
-        public static int count(IntIterator iter) {
-            int counter = 0;
-            while (iter.next()) {
-                ++counter;
-            }
-            return counter;
-        }
-    }
-}
diff --git a/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java b/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
index e3c1c47da..ec52bcf00 100644
--- a/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
+++ b/core/src/main/java/com/graphhopper/storage/MMapDataAccess.java
@@ -19,16 +19,25 @@ package com.graphhopper.storage;
 
 import com.graphhopper.util.Constants;
 import com.graphhopper.util.Helper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.StringTokenizer;
 
 /**
  * A DataAccess implementation using a memory-mapped file, i.e. a facility of the
@@ -49,6 +58,9 @@ import java.util.List;
  * @author Michael Zilske
  */
 public final class MMapDataAccess extends AbstractDataAccess {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(MMapDataAccess.class);
+
     private final boolean allowWrites;
     private RandomAccessFile raFile;
     private List<ByteBuffer> segments = new ArrayList<>();
@@ -58,6 +70,92 @@ public final class MMapDataAccess extends AbstractDataAccess {
         this.allowWrites = allowWrites;
     }
 
+    public static boolean jreIsMinimumJava9() {
+        final StringTokenizer st = new StringTokenizer(System.getProperty("java.specification.version"), ".");
+        int JVM_MAJOR_VERSION = Integer.parseInt(st.nextToken());
+        int JVM_MINOR_VERSION;
+        if (st.hasMoreTokens()) {
+            JVM_MINOR_VERSION = Integer.parseInt(st.nextToken());
+        } else {
+            JVM_MINOR_VERSION = 0;
+        }
+        return JVM_MAJOR_VERSION > 1 || (JVM_MAJOR_VERSION == 1 && JVM_MINOR_VERSION >= 9);
+    }
+
+    public static void cleanMappedByteBuffer(final ByteBuffer buffer) {
+        // TODO avoid reflection on every call
+        try {
+            AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+                @Override
+                public Object run() throws Exception {
+                    if (jreIsMinimumJava9()) {
+                        // >=JDK9 class sun.misc.Unsafe { void invokeCleaner(ByteBuffer buf) }
+                        final Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+                        // fetch the unsafe instance and bind it to the virtual MethodHandle
+                        final Field f = unsafeClass.getDeclaredField("theUnsafe");
+                        f.setAccessible(true);
+                        final Object theUnsafe = f.get(null);
+                        final Method method = unsafeClass.getDeclaredMethod("invokeCleaner", ByteBuffer.class);
+                        try {
+                            method.invoke(theUnsafe, buffer);
+                            return null;
+                        } catch (Throwable t) {
+                            throw new RuntimeException(t);
+                        }
+                    }
+
+                    if (buffer.getClass().getSimpleName().equals("MappedByteBufferAdapter")) {
+                        if (!Constants.ANDROID)
+                            throw new RuntimeException("MappedByteBufferAdapter only supported for Android at the moment");
+
+                        // For Android 4.1 call ((MappedByteBufferAdapter)buffer).free() see #914
+                        Class<?> directByteBufferClass = Class.forName("java.nio.MappedByteBufferAdapter");
+                        callBufferFree(buffer, directByteBufferClass);
+                    } else {
+                        // <=JDK8 class DirectByteBuffer { sun.misc.Cleaner cleaner(Buffer buf) }
+                        //        then call sun.misc.Cleaner.clean
+                        final Class<?> directByteBufferClass = Class.forName("java.nio.DirectByteBuffer");
+                        try {
+                            final Method dbbCleanerMethod = directByteBufferClass.getMethod("cleaner");
+                            dbbCleanerMethod.setAccessible(true);
+                            // call: cleaner = ((DirectByteBuffer)buffer).cleaner()
+                            final Object cleaner = dbbCleanerMethod.invoke(buffer);
+                            if (cleaner != null) {
+                                final Class<?> cleanerMethodReturnType = dbbCleanerMethod.getReturnType();
+                                final Method cleanMethod = cleanerMethodReturnType.getDeclaredMethod("clean");
+                                cleanMethod.setAccessible(true);
+                                // call: ((sun.misc.Cleaner)cleaner).clean()
+                                cleanMethod.invoke(cleaner);
+                            }
+                        } catch (NoSuchMethodException ex2) {
+                            if (Constants.ANDROID)
+                                // For Android 5.1.1 call ((DirectByteBuffer)buffer).free() see #933
+                                callBufferFree(buffer, directByteBufferClass);
+                            else
+                                // ignore if method cleaner or clean is not available
+                                LOGGER.warn("NoSuchMethodException | " + System.getProperty("java.version"), ex2);
+                        }
+                    }
+
+                    return null;
+                }
+            });
+        } catch (PrivilegedActionException e) {
+            throw new RuntimeException("Unable to unmap the mapped buffer", e);
+        }
+    }
+
+    private static void callBufferFree(ByteBuffer buffer, Class<?> directByteBufferClass)
+            throws InvocationTargetException, IllegalAccessException {
+        try {
+            final Method dbbFreeMethod = directByteBufferClass.getMethod("free");
+            dbbFreeMethod.setAccessible(true);
+            dbbFreeMethod.invoke(buffer);
+        } catch (NoSuchMethodException ex2) {
+            LOGGER.warn("NoSuchMethodException | " + System.getProperty("java.version"), ex2);
+        }
+    }
+
     private void initRandomAccessFile() {
         if (raFile != null) {
             return;
@@ -362,7 +460,7 @@ public final class MMapDataAccess extends AbstractDataAccess {
     private void clean(int from, int to) {
         for (int i = from; i < to; i++) {
             ByteBuffer bb = segments.get(i);
-            Helper.cleanMappedByteBuffer(bb);
+            cleanMappedByteBuffer(bb);
             segments.set(i, null);
         }
     }
diff --git a/core/src/main/java/com/graphhopper/storage/StorableProperties.java b/core/src/main/java/com/graphhopper/storage/StorableProperties.java
index 9167d29a6..fc825aa08 100644
--- a/core/src/main/java/com/graphhopper/storage/StorableProperties.java
+++ b/core/src/main/java/com/graphhopper/storage/StorableProperties.java
@@ -19,10 +19,10 @@ package com.graphhopper.storage;
 
 import com.graphhopper.util.Constants;
 import com.graphhopper.util.Helper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
+import java.io.*;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
@@ -34,6 +34,9 @@ import static com.graphhopper.util.Helper.*;
  * @author Peter Karich
  */
 public class StorableProperties implements Storable<StorableProperties> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(StorableProperties.class);
+
     private final Map<String, String> map = new LinkedHashMap<>();
     private final DataAccess da;
 
@@ -189,4 +192,32 @@ public class StorableProperties implements Storable<StorableProperties> {
     public synchronized String toString() {
         return da.toString();
     }
+
+    static void loadProperties(Map<String, String> map, Reader tmpReader) throws IOException {
+        BufferedReader reader = new BufferedReader(tmpReader);
+        String line;
+        try {
+            while ((line = reader.readLine()) != null) {
+                if (line.startsWith("//") || line.startsWith("#")) {
+                    continue;
+                }
+
+                if (Helper.isEmpty(line)) {
+                    continue;
+                }
+
+                int index = line.indexOf("=");
+                if (index < 0) {
+                    LOGGER.warn("Skipping configuration at line:" + line);
+                    continue;
+                }
+
+                String field = line.substring(0, index);
+                String value = line.substring(index + 1);
+                map.put(field.trim(), value.trim());
+            }
+        } finally {
+            reader.close();
+        }
+    }
 }
diff --git a/core/src/main/java/com/graphhopper/util/Helper7.java b/core/src/main/java/com/graphhopper/util/Helper7.java
deleted file mode 100644
index 1e94f55fb..000000000
--- a/core/src/main/java/com/graphhopper/util/Helper7.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package com.graphhopper.util;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
-/**
- * Put the usage of proprietary "sun" classes and after jdk6 classes into this class. To use Helper
- * class under Android as well.
- * <p>
- *
- * @author Peter Karich
- */
-public class Helper7 {
-
-    /**
-     * <code>true</code>, if this platform supports unmapping mmapped files.
-     */
-    public static final boolean UNMAP_SUPPORTED;
-
-    static {
-        boolean v;
-        try {
-            Class.forName("sun.misc.Cleaner");
-            Class.forName("java.nio.DirectByteBuffer")
-                    .getMethod("cleaner");
-            v = true;
-        } catch (Exception e) {
-            v = false;
-        }
-        UNMAP_SUPPORTED = v;
-    }
-
-    public static String getBeanMemInfo() {
-        java.lang.management.OperatingSystemMXBean mxbean = java.lang.management.ManagementFactory.getOperatingSystemMXBean();
-        com.sun.management.OperatingSystemMXBean sunmxbean = (com.sun.management.OperatingSystemMXBean) mxbean;
-        long freeMemory = sunmxbean.getFreePhysicalMemorySize();
-        long availableMemory = sunmxbean.getTotalPhysicalMemorySize();
-        return "free:" + freeMemory / Helper.MB + ", available:" + availableMemory / Helper.MB
-                + ", rfree:" + Runtime.getRuntime().freeMemory() / Helper.MB;
-    }
-
-    public static void close(XMLStreamReader r) {
-        try {
-            if (r != null) {
-                r.close();
-            }
-        } catch (XMLStreamException ex) {
-            throw new RuntimeException("Couldn't close xml reader", ex);
-        }
-    }
-}
diff --git a/core/src/main/java/com/graphhopper/util/PathMerger.java b/core/src/main/java/com/graphhopper/util/PathMerger.java
index 7ae523a2d..6cc31fb52 100644
--- a/core/src/main/java/com/graphhopper/util/PathMerger.java
+++ b/core/src/main/java/com/graphhopper/util/PathMerger.java
@@ -157,25 +157,6 @@ public class PathMerger {
         }
     }
 
-    /**
-     * Merges <code>otherDetails</code> into the <code>pathDetails</code>.
-     * <p>
-     * This method makes sure that Entry list around via points are merged correctly.
-     * See #1091 and the misplaced PathDetail after waypoints.
-     */
-    public static void merge(List<PathDetail> pathDetails, List<PathDetail> otherDetails) {
-        // Make sure that the PathDetail list is merged correctly at via points
-        if (!pathDetails.isEmpty() && !otherDetails.isEmpty()) {
-            PathDetail lastDetail = pathDetails.get(pathDetails.size() - 1);
-            if (lastDetail.getValue().equals(otherDetails.get(0).getValue())) {
-                lastDetail.setLast(otherDetails.get(0).getLast());
-                otherDetails.remove(0);
-            }
-        }
-
-        pathDetails.addAll(otherDetails);
-    }
-
     /**
      * This method iterates over all instructions and uses the available context to improve the instructions.
      * If the requests contains a heading, this method can transform the first continue to a u-turn if the heading
diff --git a/reader-json/src/test/resources/com/graphhopper/json/geo/geojson1.json b/core/src/test/resources/com/graphhopper/json/geo/geojson1.json
similarity index 100%
rename from reader-json/src/test/resources/com/graphhopper/json/geo/geojson1.json
rename to core/src/test/resources/com/graphhopper/json/geo/geojson1.json
diff --git a/reader-json/src/test/resources/com/graphhopper/storage/change/overlaydata1.json b/core/src/test/resources/com/graphhopper/storage/change/overlaydata1.json
similarity index 100%
rename from reader-json/src/test/resources/com/graphhopper/storage/change/overlaydata1.json
rename to core/src/test/resources/com/graphhopper/storage/change/overlaydata1.json
diff --git a/pom.xml b/pom.xml
index d6da149d4..8ff3c4c55 100644
--- a/pom.xml
+++ b/pom.xml
@@ -76,8 +76,9 @@
         <module>reader-osm</module>
         <module>isochrone</module>
         <module>reader-gtfs</module>
-        <module>reader-json</module>
         <module>tools</module>
+        <module>web-bundle</module>
+        <module>api</module>
         <module>web</module>
         <module>client-hc</module>
     </modules>
@@ -189,10 +190,6 @@
                         <bundledSignature>jdk-non-portable</bundledSignature>
                     </bundledSignatures>
                     <excludes>
-                        <!-- Excluded because of "Helper7#getBeanMemInfo" -->
-                        <exclude>
-                            com/graphhopper/util/Helper7.class
-                        </exclude>
                         <exclude>
                             com/graphhopper/storage/UnsafeDataAccess.class
                         </exclude>
diff --git a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/TripFromLabel.java b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/TripFromLabel.java
index 9def351bc..2ad068967 100644
--- a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/TripFromLabel.java
+++ b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/TripFromLabel.java
@@ -68,12 +68,12 @@ class TripFromLabel {
         if (legs.size() > 1 && legs.get(0) instanceof Trip.WalkLeg) {
             final Trip.WalkLeg accessLeg = (Trip.WalkLeg) legs.get(0);
             legs.set(0, new Trip.WalkLeg(accessLeg.departureLocation, new Date(legs.get(1).getDepartureTime().getTime() - (accessLeg.getArrivalTime().getTime() - accessLeg.getDepartureTime().getTime())),
-                    accessLeg.edges, accessLeg.geometry, accessLeg.distance, accessLeg.instructions, legs.get(1).getDepartureTime()));
+                    accessLeg.geometry, accessLeg.distance, accessLeg.instructions, legs.get(1).getDepartureTime()));
         }
         if (legs.size() > 1 && legs.get(legs.size() - 1) instanceof Trip.WalkLeg) {
             final Trip.WalkLeg egressLeg = (Trip.WalkLeg) legs.get(legs.size() - 1);
             legs.set(legs.size() - 1, new Trip.WalkLeg(egressLeg.departureLocation, legs.get(legs.size() - 2).getArrivalTime(),
-                    egressLeg.edges, egressLeg.geometry, egressLeg.distance, egressLeg.instructions,
+                    egressLeg.geometry, egressLeg.distance, egressLeg.instructions,
                     new Date(legs.get(legs.size() - 2).getArrivalTime().getTime() + (egressLeg.getArrivalTime().getTime() - egressLeg.getDepartureTime().getTime()))));
         }
 
@@ -360,7 +360,7 @@ class TripFromLabel {
                             feedIdWithTimezone.feedId, partition.get(0).edge.nTransfers == 0,
                             tripDescriptor.getTripId(),
                             tripDescriptor.getRouteId(),
-                            edges(partition).map(edgeLabel -> edgeLabel.edgeIteratorState).collect(Collectors.toList()),
+                            edges(partition).map(edgeLabel -> edgeLabel.edgeIteratorState).collect(Collectors.toList()).get(0).getName(),
                             stops,
                             partition.stream().mapToDouble(t -> t.edge.distance).sum(),
                             path.get(i - 1).label.currentTime - boardTime,
@@ -385,7 +385,6 @@ class TripFromLabel {
             return Collections.singletonList(new Trip.WalkLeg(
                     "Walk",
                     Date.from(departureTime),
-                    edges(path).map(edgeLabel -> edgeLabel.edgeIteratorState).collect(Collectors.toList()),
                     lineStringFromEdges(path),
                     edges(path).mapToDouble(edgeLabel -> edgeLabel.distance).sum(),
                     instructions,
diff --git a/reader-json/src/main/java/com/graphhopper/json/GHJsonJackson.java b/reader-json/src/main/java/com/graphhopper/json/GHJsonJackson.java
deleted file mode 100644
index cf835aca0..000000000
--- a/reader-json/src/main/java/com/graphhopper/json/GHJsonJackson.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package com.graphhopper.json;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import java.io.IOException;
-import java.io.Reader;
-
-class GHJsonJackson implements GHJson {
-    private final ObjectMapper objectMapper;
-
-    GHJsonJackson(ObjectMapper om) {
-        this.objectMapper = om;
-    }
-
-    @Override
-    public <T> T fromJson(Reader source, Class<T> aClass) {
-        try {
-            return objectMapper.readValue(source, aClass);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/reader-json/src/main/java/com/graphhopper/json/JsonFeatureConverter.java b/reader-json/src/main/java/com/graphhopper/json/JsonFeatureConverter.java
deleted file mode 100644
index e4e01c6d0..000000000
--- a/reader-json/src/main/java/com/graphhopper/json/JsonFeatureConverter.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package com.graphhopper.json;
-
-import com.graphhopper.json.geo.JsonFeatureCollection;
-import com.graphhopper.routing.util.EncodingManager;
-import com.graphhopper.storage.change.ChangeGraphHelper;
-
-import java.io.*;
-
-import static com.graphhopper.util.Helper.UTF_CS;
-
-/**
- * Creates JsonFeature out of files and applies them to the graph.
- * Currently not used in GraphHopper itself.
- *
- * @author Peter Karich
- */
-public class JsonFeatureConverter {
-    private final ChangeGraphHelper changeGraphHelper;
-    private final GHJson ghson;
-    private final EncodingManager encodingManager;
-
-    public JsonFeatureConverter(GHJson ghson, ChangeGraphHelper changeGraphHelper, EncodingManager encodingManager) {
-        this.ghson = ghson;
-        this.changeGraphHelper = changeGraphHelper;
-        this.encodingManager = encodingManager;
-    }
-
-    /**
-     * Easily read GeoJSON files from disc and apply the specified changes to the graph.
-     */
-    public long applyChanges(String fileOrFolderStr) {
-        File fileOrFolder = new File(fileOrFolderStr);
-        try {
-            if (fileOrFolder.isFile()) {
-                return applyChanges(new InputStreamReader(new FileInputStream(fileOrFolder), UTF_CS));
-            }
-
-            long sum = 0;
-            File[] fList = new File(fileOrFolderStr).listFiles(new FilenameFilter() {
-                @Override
-                public boolean accept(File dir, String name) {
-                    return name.endsWith(".json");
-                }
-            });
-            for (File f : fList) {
-                sum += applyChanges(new InputStreamReader(new FileInputStream(f), UTF_CS));
-            }
-            return sum;
-
-        } catch (FileNotFoundException ex) {
-            throw new RuntimeException(ex);
-        }
-    }
-
-    /**
-     * This method applies changes to the graph, specified by the reader.
-     *
-     * @return number of successfully applied edge changes
-     */
-    public long applyChanges(Reader reader) {
-        // read full file, later support one json feature or collection per line to avoid high mem consumption
-        JsonFeatureCollection data = ghson.fromJson(reader, JsonFeatureCollection.class);
-        return changeGraphHelper.applyChanges(encodingManager, data.getFeatures());
-    }
-}
diff --git a/reader-json/src/main/java/com/graphhopper/spatialrules/SpatialRuleLookupHelper.java b/reader-json/src/main/java/com/graphhopper/spatialrules/SpatialRuleLookupHelper.java
deleted file mode 100644
index 37c472f38..000000000
--- a/reader-json/src/main/java/com/graphhopper/spatialrules/SpatialRuleLookupHelper.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.graphhopper.spatialrules;
-
-import com.graphhopper.GraphHopper;
-import com.graphhopper.json.GHJsonFactory;
-import com.graphhopper.json.geo.JsonFeatureCollection;
-import com.graphhopper.routing.lm.LandmarkStorage;
-import com.graphhopper.routing.util.DataFlagEncoder;
-import com.graphhopper.routing.util.FlagEncoder;
-import com.graphhopper.routing.util.FlagEncoderFactory;
-import com.graphhopper.routing.util.spatialrules.CountriesSpatialRuleFactory;
-import com.graphhopper.routing.util.spatialrules.SpatialRuleLookup;
-import com.graphhopper.routing.util.spatialrules.SpatialRuleLookupBuilder;
-import com.graphhopper.util.CmdArgs;
-import com.graphhopper.util.PMap;
-import com.graphhopper.util.shapes.BBox;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-
-import static com.graphhopper.util.Helper.UTF_CS;
-
-/**
- * Helper class to build the spatial rule index
- *
- * @author Robin Boldt
- */
-public class SpatialRuleLookupHelper {
-
-    private static final Logger logger = LoggerFactory.getLogger(SpatialRuleLookupHelper.class);
-
-    public static void buildAndInjectSpatialRuleIntoGH(GraphHopper graphHopper, CmdArgs args) {
-        String spatialRuleLocation = args.get("spatial_rules.location", "");
-        if (!spatialRuleLocation.isEmpty()) {
-            try {
-                final BBox maxBounds = BBox.parseBBoxString(args.get("spatial_rules.max_bbox", "-180, 180, -90, 90"));
-                final InputStreamReader reader = new InputStreamReader(new FileInputStream(spatialRuleLocation), UTF_CS);
-                final SpatialRuleLookup index = SpatialRuleLookupBuilder.buildIndex(new GHJsonFactory().create().fromJson(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory(), .1, maxBounds);
-                logger.info("Set spatial rule lookup with " + index.size() + " rules");
-                final FlagEncoderFactory oldFEF = graphHopper.getFlagEncoderFactory();
-                graphHopper.setFlagEncoderFactory(new FlagEncoderFactory() {
-                    @Override
-                    public FlagEncoder createFlagEncoder(String name, PMap configuration) {
-                        if (name.equals(GENERIC)) {
-                            return new DataFlagEncoder(configuration).setSpatialRuleLookup(index);
-                        }
-
-                        return oldFEF.createFlagEncoder(name, configuration);
-                    }
-                });
-            } catch (IOException ex) {
-                throw new RuntimeException(ex);
-            }
-        }
-    }
-
-    public static JsonFeatureCollection createLandmarkSplittingFeatureCollection(String location) {
-        try {
-            Reader reader = location.isEmpty() ? new InputStreamReader(LandmarkStorage.class.getResource("map.geo.json").openStream(), UTF_CS) : new InputStreamReader(new FileInputStream(location), UTF_CS);
-            return new GHJsonFactory().create().fromJson(reader, JsonFeatureCollection.class);
-        } catch (IOException e) {
-            logger.error("Problem while reading border map GeoJSON. Skipping this.", e);
-        }
-        return null;
-    }
-
-}
diff --git a/web-bundle/pom.xml b/web-bundle/pom.xml
new file mode 100644
index 000000000..9d3f5ea0f
--- /dev/null
+++ b/web-bundle/pom.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.graphhopper</groupId>
+    <artifactId>graphhopper-web-bundle</artifactId>
+    <packaging>jar</packaging>
+    <version>0.11-SNAPSHOT</version>
+    <name>GraphHopper Dropwizard Bundle</name>
+    <description>Use the GraphHopper routing engine as a web-service</description>
+
+    <parent>
+        <groupId>com.graphhopper</groupId>
+        <artifactId>graphhopper-parent</artifactId>
+        <version>0.11-SNAPSHOT</version>
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.graphhopper</groupId>
+            <artifactId>graphhopper-reader-osm</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.graphhopper</groupId>
+            <artifactId>graphhopper-reader-json</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.graphhopper</groupId>
+            <artifactId>graphhopper-reader-gtfs</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.graphhopper</groupId>
+            <artifactId>graphhopper-isochrone</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
+        <!-- required for JDK9 -->
+        <dependency>
+            <groupId>javax.xml.ws</groupId>
+            <artifactId>jaxws-api</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <!-- required for dropwizard -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>21.0</version>
+        </dependency>
+        <dependency>
+            <groupId>io.dropwizard</groupId>
+            <artifactId>dropwizard-core</artifactId>
+            <version>1.2.2</version>
+        </dependency>
+        <dependency>
+            <groupId>io.dropwizard-bundles</groupId>
+            <artifactId>dropwizard-configurable-assets-bundle</artifactId>
+            <version>1.0.5</version>
+        </dependency>
+        <dependency>
+            <groupId>io.dropwizard</groupId>
+            <artifactId>dropwizard-testing</artifactId>
+            <version>1.2.2</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- for integration tests of service -->
+        <dependency>
+            <groupId>com.graphhopper</groupId>
+            <artifactId>directions-api-client-hc</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.graphhopper</groupId>
+            <artifactId>directions-api-client</artifactId>
+            <version>0.10.0</version>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.5.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
+
+
diff --git a/web/src/main/java/com/graphhopper/http/GraphHopperBundle.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
similarity index 72%
rename from web/src/main/java/com/graphhopper/http/GraphHopperBundle.java
rename to web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
index 1771a19ad..5c636bfcb 100644
--- a/web/src/main/java/com/graphhopper/http/GraphHopperBundle.java
+++ b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
@@ -18,29 +18,22 @@
 
 package com.graphhopper.http;
 
-import com.fasterxml.jackson.core.JsonGenerationException;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.module.SimpleModule;
 import com.graphhopper.GraphHopper;
 import com.graphhopper.GraphHopperAPI;
 import com.graphhopper.http.health.GraphHopperHealthCheck;
 import com.graphhopper.http.health.GraphHopperStorageHealthCheck;
-import com.graphhopper.http.resources.*;
 import com.graphhopper.isochrone.algorithm.RasterHullBuilder;
 import com.graphhopper.reader.gtfs.GraphHopperGtfs;
 import com.graphhopper.reader.gtfs.GtfsStorage;
 import com.graphhopper.reader.gtfs.PtFlagEncoder;
 import com.graphhopper.reader.gtfs.RealtimeFeed;
+import com.graphhopper.resources.*;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.storage.GHDirectory;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.TranslationMap;
-import com.graphhopper.util.details.PathDetail;
 import io.dropwizard.ConfiguredBundle;
 import io.dropwizard.lifecycle.Managed;
 import io.dropwizard.setup.Bootstrap;
@@ -49,11 +42,8 @@ import org.glassfish.hk2.api.Factory;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 
 import javax.inject.Inject;
-import javax.servlet.DispatcherType;
-import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.EnumSet;
 
 public class GraphHopperBundle implements ConfiguredBundle<GraphHopperBundleConfiguration> {
 
@@ -166,10 +156,6 @@ public class GraphHopperBundle implements ConfiguredBundle<GraphHopperBundleConf
         } else {
             runRegularGraphHopper(configuration.getGraphHopperConfiguration(), environment);
         }
-
-        environment.servlets().addFilter("cors", CORSFilter.class).addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "*");
-        environment.servlets().addFilter("ipfilter", new IPFilter(configuration.getGraphHopperConfiguration().get("jetty.whiteips", ""), configuration.getGraphHopperConfiguration().get("jetty.blackips", ""))).addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "*");
-
     }
 
     private void runPtGraphHopper(CmdArgs configuration, Environment environment) {
@@ -245,67 +231,7 @@ public class GraphHopperBundle implements ConfiguredBundle<GraphHopperBundleConf
         environment.jersey().register(I18NResource.class);
         environment.jersey().register(InfoResource.class);
 
-        SimpleModule pathDetailModule = new SimpleModule();
-        pathDetailModule.addSerializer(PathDetail.class, new PathDetailSerializer());
-        pathDetailModule.addDeserializer(PathDetail.class, new PathDetailDeserializer());
-        environment.getObjectMapper().registerModule(pathDetailModule);
         environment.healthChecks().register("graphhopper", new GraphHopperHealthCheck(graphHopperManaged.getGraphHopper()));
     }
 
-    public static class PathDetailSerializer extends JsonSerializer<PathDetail> {
-
-        @Override
-        public void serialize(PathDetail value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
-            gen.writeStartArray();
-
-            gen.writeNumber(value.getFirst());
-            gen.writeNumber(value.getLast());
-
-            if (value.getValue() instanceof Double)
-                gen.writeNumber((Double) value.getValue());
-            else if (value.getValue() instanceof Long)
-                gen.writeNumber((Long) value.getValue());
-            else if (value.getValue() instanceof Integer)
-                gen.writeNumber((Integer) value.getValue());
-            else if (value.getValue() instanceof Boolean)
-                gen.writeBoolean((Boolean) value.getValue());
-            else if (value.getValue() instanceof String)
-                gen.writeString((String) value.getValue());
-            else
-                throw new JsonGenerationException("Unsupported type for PathDetail.value" + value.getValue().getClass(), gen);
-
-            gen.writeEndArray();
-        }
-    }
-
-    public static class PathDetailDeserializer extends JsonDeserializer<PathDetail> {
-
-        @Override
-        public PathDetail deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
-            JsonNode pathDetail = jp.readValueAsTree();
-            if (pathDetail.size() != 3)
-                throw new JsonParseException(jp, "PathDetail array must have exactly 3 entries but was " + pathDetail.size());
-
-            JsonNode from = pathDetail.get(0);
-            JsonNode to = pathDetail.get(1);
-            JsonNode val = pathDetail.get(2);
-
-            PathDetail pd;
-            if (val.isBoolean())
-                pd = new PathDetail(val.asBoolean());
-            else if (val.isLong())
-                pd = new PathDetail(val.asLong());
-            else if (val.isDouble())
-                pd = new PathDetail(val.asDouble());
-            else if (val.isTextual())
-                pd = new PathDetail(val.asText());
-            else
-                throw new JsonParseException(jp, "Unsupported type of PathDetail value " + pathDetail.getNodeType().name());
-
-            pd.setFirst(from.asInt());
-            pd.setLast(to.asInt());
-            return pd;
-        }
-    }
-
 }
diff --git a/web/src/main/java/com/graphhopper/http/GraphHopperBundleConfiguration.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundleConfiguration.java
similarity index 100%
rename from web/src/main/java/com/graphhopper/http/GraphHopperBundleConfiguration.java
rename to web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundleConfiguration.java
diff --git a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java
new file mode 100644
index 000000000..4da2e92a5
--- /dev/null
+++ b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to GraphHopper GmbH under one or more contributor
+ *  license agreements. See the NOTICE file distributed with this work for
+ *  additional information regarding copyright ownership.
+ *
+ *  GraphHopper GmbH licenses this file to you under the Apache License,
+ *  Version 2.0 (the "License"); you may not use this file except in
+ *  compliance with the License. You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.graphhopper.http;
+
+import com.graphhopper.GraphHopper;
+import com.graphhopper.util.ObjectMapperFactory;
+import com.graphhopper.json.geo.JsonFeatureCollection;
+import com.graphhopper.reader.osm.GraphHopperOSM;
+import com.graphhopper.routing.lm.LandmarkStorage;
+import com.graphhopper.routing.util.spatialrules.SpatialRuleLookupHelper;
+import com.graphhopper.util.CmdArgs;
+import com.graphhopper.util.Parameters;
+import com.graphhopper.util.shapes.BBox;
+import io.dropwizard.lifecycle.Managed;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import static com.graphhopper.util.Helper.UTF_CS;
+
+@Singleton
+public class GraphHopperManaged implements Managed {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    private final GraphHopper graphHopper;
+
+    @Inject
+    public GraphHopperManaged(CmdArgs configuration) {
+        String splitAreaLocation = configuration.get(Parameters.Landmark.PREPARE + "split_area_location", "");
+        JsonFeatureCollection landmarkSplittingFeatureCollection;
+        try (Reader reader = splitAreaLocation.isEmpty() ? new InputStreamReader(LandmarkStorage.class.getResource("map.geo.json").openStream(), UTF_CS) : new InputStreamReader(new FileInputStream(splitAreaLocation), UTF_CS)) {
+            landmarkSplittingFeatureCollection = ObjectMapperFactory.create().readValue(reader, JsonFeatureCollection.class);
+        } catch (IOException e1) {
+            logger.error("Problem while reading border map GeoJSON. Skipping this.", e1);
+            landmarkSplittingFeatureCollection = null;
+        }
+        graphHopper = new GraphHopperOSM(landmarkSplittingFeatureCollection).forServer();
+        String spatialRuleLocation = configuration.get("spatial_rules.location", "");
+        if (!spatialRuleLocation.isEmpty()) {
+            final BBox maxBounds = BBox.parseBBoxString(configuration.get("spatial_rules.max_bbox", "-180, 180, -90, 90"));
+            try (final InputStreamReader reader = new InputStreamReader(new FileInputStream(spatialRuleLocation), UTF_CS)) {
+                JsonFeatureCollection jsonFeatureCollection = ObjectMapperFactory.create().readValue(reader, JsonFeatureCollection.class);
+                SpatialRuleLookupHelper.buildAndInjectSpatialRuleIntoGH(graphHopper, maxBounds, jsonFeatureCollection);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        graphHopper.init(configuration);
+    }
+
+    @Override
+    public void start() {
+        graphHopper.importOrLoad();
+        logger.info("loaded graph at:" + graphHopper.getGraphHopperLocation()
+                + ", data_reader_file:" + graphHopper.getDataReaderFile()
+                + ", flag_encoders:" + graphHopper.getEncodingManager()
+                + ", " + graphHopper.getGraphHopperStorage().toDetailsString());
+    }
+
+    GraphHopper getGraphHopper() {
+        return graphHopper;
+    }
+
+    @Override
+    public void stop() {
+        graphHopper.close();
+    }
+
+
+}
diff --git a/web/src/main/java/com/graphhopper/http/health/GraphHopperHealthCheck.java b/web-bundle/src/main/java/com/graphhopper/http/health/GraphHopperHealthCheck.java
similarity index 100%
rename from web/src/main/java/com/graphhopper/http/health/GraphHopperHealthCheck.java
rename to web-bundle/src/main/java/com/graphhopper/http/health/GraphHopperHealthCheck.java
diff --git a/web/src/main/java/com/graphhopper/http/health/GraphHopperStorageHealthCheck.java b/web-bundle/src/main/java/com/graphhopper/http/health/GraphHopperStorageHealthCheck.java
similarity index 100%
rename from web/src/main/java/com/graphhopper/http/health/GraphHopperStorageHealthCheck.java
rename to web-bundle/src/main/java/com/graphhopper/http/health/GraphHopperStorageHealthCheck.java
diff --git a/web/src/main/java/com/graphhopper/http/resources/ChangeGraphResource.java b/web-bundle/src/main/java/com/graphhopper/resources/ChangeGraphResource.java
similarity index 97%
rename from web/src/main/java/com/graphhopper/http/resources/ChangeGraphResource.java
rename to web-bundle/src/main/java/com/graphhopper/resources/ChangeGraphResource.java
index a1504f410..4480c06f7 100644
--- a/web/src/main/java/com/graphhopper/http/resources/ChangeGraphResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/ChangeGraphResource.java
@@ -15,7 +15,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper.http.resources;
+package com.graphhopper.resources;
 
 import com.codahale.metrics.annotation.Timed;
 import com.graphhopper.GraphHopper;
diff --git a/web/src/main/java/com/graphhopper/http/resources/I18NResource.java b/web-bundle/src/main/java/com/graphhopper/resources/I18NResource.java
similarity index 98%
rename from web/src/main/java/com/graphhopper/http/resources/I18NResource.java
rename to web-bundle/src/main/java/com/graphhopper/resources/I18NResource.java
index 95401c3d9..b8e352052 100644
--- a/web/src/main/java/com/graphhopper/http/resources/I18NResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/I18NResource.java
@@ -15,7 +15,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper.http.resources;
+package com.graphhopper.resources;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.graphhopper.util.Translation;
diff --git a/web/src/main/java/com/graphhopper/http/resources/InfoResource.java b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
similarity index 98%
rename from web/src/main/java/com/graphhopper/http/resources/InfoResource.java
rename to web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
index 822941667..9f7dbbc72 100644
--- a/web/src/main/java/com/graphhopper/http/resources/InfoResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
@@ -15,7 +15,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper.http.resources;
+package com.graphhopper.resources;
 
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.util.Constants;
diff --git a/web/src/main/java/com/graphhopper/http/resources/IsochroneResource.java b/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java
similarity index 99%
rename from web/src/main/java/com/graphhopper/http/resources/IsochroneResource.java
rename to web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java
index b8a086a26..12e0ecefd 100644
--- a/web/src/main/java/com/graphhopper/http/resources/IsochroneResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/IsochroneResource.java
@@ -1,4 +1,4 @@
-package com.graphhopper.http.resources;
+package com.graphhopper.resources;
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
diff --git a/web/src/main/java/com/graphhopper/http/resources/NearestResource.java b/web-bundle/src/main/java/com/graphhopper/resources/NearestResource.java
similarity index 98%
rename from web/src/main/java/com/graphhopper/http/resources/NearestResource.java
rename to web-bundle/src/main/java/com/graphhopper/resources/NearestResource.java
index 5d434f739..9de751294 100644
--- a/web/src/main/java/com/graphhopper/http/resources/NearestResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/NearestResource.java
@@ -15,7 +15,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper.http.resources;
+package com.graphhopper.resources;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/web/src/main/java/com/graphhopper/http/resources/RouteResource.java b/web-bundle/src/main/java/com/graphhopper/resources/RouteResource.java
similarity index 98%
rename from web/src/main/java/com/graphhopper/http/resources/RouteResource.java
rename to web-bundle/src/main/java/com/graphhopper/resources/RouteResource.java
index 83e247ccb..d3cdc4e3e 100644
--- a/web/src/main/java/com/graphhopper/http/resources/RouteResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/RouteResource.java
@@ -15,13 +15,14 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper.http.resources;
+package com.graphhopper.resources;
 
 import com.graphhopper.GHRequest;
 import com.graphhopper.GHResponse;
 import com.graphhopper.GraphHopperAPI;
 import com.graphhopper.http.WebHelper;
 import com.graphhopper.routing.util.EncodingManager;
+import com.graphhopper.util.Constants;
 import com.graphhopper.util.Parameters;
 import com.graphhopper.util.StopWatch;
 import com.graphhopper.util.shapes.GHPoint;
@@ -150,7 +151,7 @@ public class RouteResource {
                     + ", points0: " + ghResponse.getBest().getPoints().getSize()
                     + ", debugInfo: " + ghResponse.getDebugInfo());
             return writeGPX ?
-                    WebHelper.gpxSuccessResponseBuilder(ghResponse, timeString, trackName, enableElevation, withRoute, withTrack, withWayPoints).
+                    WebHelper.gpxSuccessResponseBuilder(ghResponse, timeString, trackName, enableElevation, withRoute, withTrack, withWayPoints, Constants.VERSION).
                             header("X-GH-Took", "" + Math.round(took * 1000)).
                             build()
                     :
diff --git a/web/pom.xml b/web/pom.xml
index b423597a7..cf5175d34 100644
--- a/web/pom.xml
+++ b/web/pom.xml
@@ -22,43 +22,10 @@
     <dependencies>
         <dependency>
             <groupId>com.graphhopper</groupId>
-            <artifactId>graphhopper-reader-osm</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>com.graphhopper</groupId>
-            <artifactId>graphhopper-reader-json</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>com.graphhopper</groupId>
-            <artifactId>graphhopper-reader-gtfs</artifactId>
+            <artifactId>graphhopper-web-bundle</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
 
-        <dependency>
-            <groupId>com.graphhopper</groupId>
-            <artifactId>graphhopper-isochrone</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
-
-        <!-- required for JDK9 -->
-        <dependency>
-            <groupId>javax.xml.ws</groupId>
-            <artifactId>jaxws-api</artifactId>
-            <version>2.3.0</version>
-        </dependency>
-        <!-- required for dropwizard -->
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-            <version>21.0</version>
-        </dependency>
-        <dependency>
-            <groupId>io.dropwizard</groupId>
-            <artifactId>dropwizard-core</artifactId>
-            <version>1.2.2</version>
-        </dependency>
         <dependency>
             <groupId>io.dropwizard-bundles</groupId>
             <artifactId>dropwizard-configurable-assets-bundle</artifactId>
diff --git a/web/src/main/java/com/graphhopper/http/GraphHopperApplication.java b/web/src/main/java/com/graphhopper/http/GraphHopperApplication.java
index a2dcdb275..accb9c104 100644
--- a/web/src/main/java/com/graphhopper/http/GraphHopperApplication.java
+++ b/web/src/main/java/com/graphhopper/http/GraphHopperApplication.java
@@ -34,6 +34,8 @@ import io.dropwizard.bundles.assets.ConfiguredAssetsBundle;
 import io.dropwizard.setup.Bootstrap;
 import io.dropwizard.setup.Environment;
 
+import javax.servlet.DispatcherType;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -74,5 +76,7 @@ public final class GraphHopperApplication extends Application<GraphHopperServerC
         }));
 
         environment.jersey().register(new RootResource());
+        environment.servlets().addFilter("cors", CORSFilter.class).addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "*");
+        environment.servlets().addFilter("ipfilter", new IPFilter(configuration.getGraphHopperConfiguration().get("jetty.whiteips", ""), configuration.getGraphHopperConfiguration().get("jetty.blackips", ""))).addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "*");
     }
 }
diff --git a/web/src/main/java/com/graphhopper/http/GraphHopperManaged.java b/web/src/main/java/com/graphhopper/http/GraphHopperManaged.java
deleted file mode 100644
index 0a95c525b..000000000
--- a/web/src/main/java/com/graphhopper/http/GraphHopperManaged.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-package com.graphhopper.http;
-
-import com.graphhopper.GraphHopper;
-import com.graphhopper.reader.osm.GraphHopperOSM;
-import com.graphhopper.spatialrules.SpatialRuleLookupHelper;
-import com.graphhopper.util.CmdArgs;
-import com.graphhopper.util.Parameters;
-import io.dropwizard.lifecycle.Managed;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-@Singleton
-public class GraphHopperManaged implements Managed {
-
-    private final Logger logger = LoggerFactory.getLogger(getClass());
-    private final GraphHopper graphHopper;
-
-    @Inject
-    public GraphHopperManaged(CmdArgs configuration) {
-        graphHopper = new GraphHopperOSM(
-                SpatialRuleLookupHelper.createLandmarkSplittingFeatureCollection(configuration.get(Parameters.Landmark.PREPARE + "split_area_location", ""))
-        ).forServer();
-        SpatialRuleLookupHelper.buildAndInjectSpatialRuleIntoGH(graphHopper, configuration);
-        graphHopper.init(configuration);
-    }
-
-    @Override
-    public void start() {
-        graphHopper.importOrLoad();
-        logger.info("loaded graph at:" + graphHopper.getGraphHopperLocation()
-                + ", data_reader_file:" + graphHopper.getDataReaderFile()
-                + ", flag_encoders:" + graphHopper.getEncodingManager()
-                + ", " + graphHopper.getGraphHopperStorage().toDetailsString());
-    }
-
-    GraphHopper getGraphHopper() {
-        return graphHopper;
-    }
-
-    @Override
-    public void stop() {
-        graphHopper.close();
-    }
-
-
-}


Test patch:
diff --git a/core/src/main/java/com/graphhopper/util/exceptions/ConnectionNotFoundException.java b/api/src/main/java/com/graphhopper/util/exceptions/ConnectionNotFoundException.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/exceptions/ConnectionNotFoundException.java
rename to api/src/main/java/com/graphhopper/util/exceptions/ConnectionNotFoundException.java
diff --git a/core/src/main/java/com/graphhopper/util/exceptions/DetailedIllegalArgumentException.java b/api/src/main/java/com/graphhopper/util/exceptions/DetailedIllegalArgumentException.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/exceptions/DetailedIllegalArgumentException.java
rename to api/src/main/java/com/graphhopper/util/exceptions/DetailedIllegalArgumentException.java
diff --git a/core/src/main/java/com/graphhopper/util/exceptions/DetailedRuntimeException.java b/api/src/main/java/com/graphhopper/util/exceptions/DetailedRuntimeException.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/exceptions/DetailedRuntimeException.java
rename to api/src/main/java/com/graphhopper/util/exceptions/DetailedRuntimeException.java
diff --git a/core/src/main/java/com/graphhopper/util/exceptions/GHException.java b/api/src/main/java/com/graphhopper/util/exceptions/GHException.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/exceptions/GHException.java
rename to api/src/main/java/com/graphhopper/util/exceptions/GHException.java
diff --git a/core/src/main/java/com/graphhopper/util/exceptions/PointDistanceExceededException.java b/api/src/main/java/com/graphhopper/util/exceptions/PointDistanceExceededException.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/exceptions/PointDistanceExceededException.java
rename to api/src/main/java/com/graphhopper/util/exceptions/PointDistanceExceededException.java
diff --git a/core/src/main/java/com/graphhopper/util/exceptions/PointNotFoundException.java b/api/src/main/java/com/graphhopper/util/exceptions/PointNotFoundException.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/exceptions/PointNotFoundException.java
rename to api/src/main/java/com/graphhopper/util/exceptions/PointNotFoundException.java
diff --git a/core/src/main/java/com/graphhopper/util/exceptions/PointOutOfBoundsException.java b/api/src/main/java/com/graphhopper/util/exceptions/PointOutOfBoundsException.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/exceptions/PointOutOfBoundsException.java
rename to api/src/main/java/com/graphhopper/util/exceptions/PointOutOfBoundsException.java
diff --git a/core/src/main/java/com/graphhopper/util/shapes/BBox.java b/api/src/main/java/com/graphhopper/util/shapes/BBox.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/shapes/BBox.java
rename to api/src/main/java/com/graphhopper/util/shapes/BBox.java
diff --git a/core/src/main/java/com/graphhopper/util/shapes/Circle.java b/api/src/main/java/com/graphhopper/util/shapes/Circle.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/shapes/Circle.java
rename to api/src/main/java/com/graphhopper/util/shapes/Circle.java
diff --git a/core/src/main/java/com/graphhopper/util/shapes/GHPlace.java b/api/src/main/java/com/graphhopper/util/shapes/GHPlace.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/shapes/GHPlace.java
rename to api/src/main/java/com/graphhopper/util/shapes/GHPlace.java
diff --git a/core/src/main/java/com/graphhopper/util/shapes/GHPoint.java b/api/src/main/java/com/graphhopper/util/shapes/GHPoint.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/shapes/GHPoint.java
rename to api/src/main/java/com/graphhopper/util/shapes/GHPoint.java
diff --git a/core/src/main/java/com/graphhopper/util/shapes/GHPoint3D.java b/api/src/main/java/com/graphhopper/util/shapes/GHPoint3D.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/shapes/GHPoint3D.java
rename to api/src/main/java/com/graphhopper/util/shapes/GHPoint3D.java
diff --git a/core/src/main/java/com/graphhopper/util/shapes/Polygon.java b/api/src/main/java/com/graphhopper/util/shapes/Polygon.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/shapes/Polygon.java
rename to api/src/main/java/com/graphhopper/util/shapes/Polygon.java
diff --git a/core/src/main/java/com/graphhopper/util/shapes/Shape.java b/api/src/main/java/com/graphhopper/util/shapes/Shape.java
similarity index 100%
rename from core/src/main/java/com/graphhopper/util/shapes/Shape.java
rename to api/src/main/java/com/graphhopper/util/shapes/Shape.java
diff --git a/web/src/test/java/com/graphhopper/http/WebHelperTest.java b/api/src/test/java/com/graphhopper/http/WebHelperTest.java
similarity index 71%
rename from web/src/test/java/com/graphhopper/http/WebHelperTest.java
rename to api/src/test/java/com/graphhopper/http/WebHelperTest.java
index e1758b08e..2fb80f928 100644
--- a/web/src/test/java/com/graphhopper/http/WebHelperTest.java
+++ b/api/src/test/java/com/graphhopper/http/WebHelperTest.java
@@ -19,6 +19,7 @@ package com.graphhopper.http;
 
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.PointList;
+import org.junit.Assert;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -30,18 +31,18 @@ public class WebHelperTest {
     @Test
     public void testDecode() throws Exception {
         PointList list = WebHelper.decodePolyline("_p~iF~ps|U", 1, false);
-        assertEquals(Helper.createPointList(38.5, -120.2), list);
+        Assert.assertEquals(Helper.createPointList(38.5, -120.2), list);
 
         list = WebHelper.decodePolyline("_p~iF~ps|U_ulLnnqC_mqNvxq`@", 3, false);
-        assertEquals(Helper.createPointList(38.5, -120.2, 40.7, -120.95, 43.252, -126.453), list);
+        Assert.assertEquals(Helper.createPointList(38.5, -120.2, 40.7, -120.95, 43.252, -126.453), list);
     }
 
     @Test
     public void testEncode() throws Exception {
-        assertEquals("_p~iF~ps|U", WebHelper.encodePolyline(
+        Assert.assertEquals("_p~iF~ps|U", WebHelper.encodePolyline(
                 Helper.createPointList(38.5, -120.2)));
 
-        assertEquals("_p~iF~ps|U_ulLnnqC_mqNvxq`@", WebHelper.encodePolyline(
+        Assert.assertEquals("_p~iF~ps|U_ulLnnqC_mqNvxq`@", WebHelper.encodePolyline(
                 Helper.createPointList(38.5, -120.2, 40.7, -120.95, 43.252, -126.453)));
     }
 
@@ -50,27 +51,27 @@ public class WebHelperTest {
         PointList list = Helper.createPointList(38.5, -120.2, 43.252, -126.453,
                 40.7, -120.95, 50.3139, 10.61279, 50.04303, 9.49768);
         String str = WebHelper.encodePolyline(list);
-        assertEquals(list, WebHelper.decodePolyline(str, list.getSize(), false));
+        Assert.assertEquals(list, WebHelper.decodePolyline(str, list.getSize(), false));
 
         list = Helper.createPointList(38.5, -120.2, 43.252, -126.453,
                 40.7, -120.95, 40.70001, -120.95001);
         str = WebHelper.encodePolyline(list);
-        assertEquals(list, WebHelper.decodePolyline(str, list.getSize(), false));
+        Assert.assertEquals(list, WebHelper.decodePolyline(str, list.getSize(), false));
     }
 
     @Test
     public void testDecode3D() throws Exception {
         PointList list = WebHelper.decodePolyline("_p~iF~ps|Uo}@", 1, true);
-        assertEquals(Helper.createPointList3D(38.5, -120.2, 10), list);
+        Assert.assertEquals(Helper.createPointList3D(38.5, -120.2, 10), list);
 
         list = WebHelper.decodePolyline("_p~iF~ps|Uo}@_ulLnnqC_anF_mqNvxq`@?", 3, true);
-        assertEquals(Helper.createPointList3D(38.5, -120.2, 10, 40.7, -120.95, 1234, 43.252, -126.453, 1234), list);
+        Assert.assertEquals(Helper.createPointList3D(38.5, -120.2, 10, 40.7, -120.95, 1234, 43.252, -126.453, 1234), list);
     }
 
     @Test
     public void testEncode3D() throws Exception {
-        assertEquals("_p~iF~ps|Uo}@", WebHelper.encodePolyline(Helper.createPointList3D(38.5, -120.2, 10)));
-        assertEquals("_p~iF~ps|Uo}@_ulLnnqC_anF_mqNvxq`@?", WebHelper.encodePolyline(
+        Assert.assertEquals("_p~iF~ps|Uo}@", WebHelper.encodePolyline(Helper.createPointList3D(38.5, -120.2, 10)));
+        Assert.assertEquals("_p~iF~ps|Uo}@_ulLnnqC_anF_mqNvxq`@?", WebHelper.encodePolyline(
                 Helper.createPointList3D(38.5, -120.2, 10, 40.7, -120.95, 1234, 43.252, -126.453, 1234)));
     }
 }
diff --git a/core/src/test/java/com/graphhopper/util/AngleCalcTest.java b/api/src/test/java/com/graphhopper/util/AngleCalcTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/AngleCalcTest.java
rename to api/src/test/java/com/graphhopper/util/AngleCalcTest.java
diff --git a/core/src/test/java/com/graphhopper/util/DistanceCalcEarthTest.java b/api/src/test/java/com/graphhopper/util/DistanceCalcEarthTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/DistanceCalcEarthTest.java
rename to api/src/test/java/com/graphhopper/util/DistanceCalcEarthTest.java
diff --git a/core/src/test/java/com/graphhopper/util/FinishInstructionTest.java b/api/src/test/java/com/graphhopper/util/FinishInstructionTest.java
similarity index 100%
rename from core/src/test/java/com/graphhopper/util/FinishInstructionTest.java
rename to api/src/test/java/com/graphhopper/util/FinishInstructionTest.java
diff --git a/core/src/test/java/com/graphhopper/GHRequestTest.java b/api/src/test/java/com/graphhopper/util/GHRequestTest.java
similarity index 98%
rename from core/src/test/java/com/graphhopper/GHRequestTest.java
rename to api/src/test/java/com/graphhopper/util/GHRequestTest.java
index 60a5fd904..882ac8409 100644
--- a/core/src/test/java/com/graphhopper/GHRequestTest.java
+++ b/api/src/test/java/com/graphhopper/util/GHRequestTest.java
@@ -15,8 +15,9 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper;
+package com.graphhopper.util;
 
+import com.graphhopper.GHRequest;
 import com.graphhopper.util.shapes.GHPoint;
 import org.junit.Test;
 
diff --git a/core/src/test/java/com/graphhopper/GHResponseTest.java b/api/src/test/java/com/graphhopper/util/GHResponseTest.java
similarity index 86%
rename from core/src/test/java/com/graphhopper/GHResponseTest.java
rename to api/src/test/java/com/graphhopper/util/GHResponseTest.java
index c0fe4ef3a..b9b19108d 100644
--- a/core/src/test/java/com/graphhopper/GHResponseTest.java
+++ b/api/src/test/java/com/graphhopper/util/GHResponseTest.java
@@ -15,8 +15,11 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper;
+package com.graphhopper.util;
 
+import com.graphhopper.GHResponse;
+import com.graphhopper.PathWrapper;
+import org.junit.Assert;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -25,7 +28,7 @@ import static org.junit.Assert.assertFalse;
 public class GHResponseTest {
     @Test
     public void testToString() throws Exception {
-        assertEquals("no paths", new GHResponse().toString());
+        Assert.assertEquals("no paths", new GHResponse().toString());
     }
 
     @Test
diff --git a/core/src/test/java/com/graphhopper/util/HelperTest.java b/api/src/test/java/com/graphhopper/util/HelperTest.java
similarity index 76%
rename from core/src/test/java/com/graphhopper/util/HelperTest.java
rename to api/src/test/java/com/graphhopper/util/HelperTest.java
index fbf08d37a..833427f73 100644
--- a/core/src/test/java/com/graphhopper/util/HelperTest.java
+++ b/api/src/test/java/com/graphhopper/util/HelperTest.java
@@ -17,16 +17,9 @@
  */
 package com.graphhopper.util;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.HashMap;
 import java.util.Locale;
-import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -35,15 +28,6 @@ import static org.junit.Assert.assertTrue;
  * @author Peter Karich
  */
 public class HelperTest {
-    @Before
-    public void setUp() {
-        Helper.removeDir(new File("test"));
-    }
-
-    @After
-    public void tearDown() {
-        Helper.removeDir(new File("test"));
-    }
 
     @Test
     public void testCountBitValue() throws Exception {
@@ -56,17 +40,6 @@ public class HelperTest {
         assertEquals(5, Helper.countBitValue(20));
     }
 
-    @Test
-    public void testUnzip() throws Exception {
-        String to = "./target/tmp/test";
-        Helper.removeDir(new File(to));
-        new Unzipper().unzip("./src/test/resources/com/graphhopper/util/test.zip", to, false);
-        assertTrue(new File("./target/tmp/test/file2 bäh").exists());
-        assertTrue(new File("./target/tmp/test/folder1").isDirectory());
-        assertTrue(new File("./target/tmp/test/folder1/folder 3").isDirectory());
-        Helper.removeDir(new File(to));
-    }
-
     @Test
     public void testGetLocale() throws Exception {
         assertEquals(Locale.GERMAN, Helper.getLocale("de"));
@@ -91,14 +64,6 @@ public class HelperTest {
         assertEquals(3, Helper.keepIn(-2, 3, 4), 1e-2);
     }
 
-    @Test
-    public void testLoadProperties() throws IOException {
-        Map<String, String> map = new HashMap<>();
-        Helper.loadProperties(map, new StringReader("blup=test\n blup2 = xy"));
-        assertEquals("test", map.get("blup"));
-        assertEquals("xy", map.get("blup2"));
-    }
-
     @Test
     public void testUnsignedConversions() {
         long l = Helper.toUnsignedLong(-1);
diff --git a/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebIT.java b/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebIT.java
index 9ab276be7..012c0e8a2 100644
--- a/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebIT.java
+++ b/client-hc/src/test/java/com/graphhopper/api/GraphHopperWebIT.java
@@ -247,7 +247,7 @@ public class GraphHopperWebIT {
         req.getHints().put("instructions", true);
         req.getHints().put("calc_points", true);
         GHResponse ghResponse = gh.route(req);
-        String gpx = ghResponse.getBest().getInstructions().createGPX();
+        String gpx = ghResponse.getBest().getInstructions().createGPX("wurst");
         assertTrue(gpx.contains("<gpx"));
         assertTrue(gpx.contains("<rtept lat="));
         assertTrue(gpx.contains("<trk><name>"));
@@ -311,7 +311,7 @@ public class GraphHopperWebIT {
         // Actual path for the request: point=48.354413%2C8.676335&point=48.35442%2C8.676345
         // Modified the sign though
         JsonNode json = new ObjectMapper().readTree("{\"instructions\":[{\"distance\":1.073,\"sign\":741,\"interval\":[0,1],\"text\":\"Continue onto A 81\",\"time\":32,\"street_name\":\"A 81\"},{\"distance\":0,\"sign\":4,\"interval\":[1,1],\"text\":\"Finish!\",\"time\":0,\"street_name\":\"\"}],\"descend\":0,\"ascend\":0,\"distance\":1.073,\"bbox\":[8.676286,48.354446,8.676297,48.354453],\"weight\":0.032179,\"time\":32,\"points_encoded\":true,\"points\":\"gfcfHwq}s@}c~AAA?\",\"snapped_waypoints\":\"gfcfHwq}s@}c~AAA?\"}");
-        PathWrapper wrapper = new GraphHopperWeb().createPathWrapper(json, true, true, true, true, false);
+        PathWrapper wrapper = new GraphHopperWeb().createPathWrapper(json, true, true);
 
         assertEquals(741, wrapper.getInstructions().get(0).getSign());
         assertEquals("Continue onto A 81", wrapper.getInstructions().get(0).getName());
diff --git a/reader-json/src/test/java/com/graphhopper/json/geo/JsonFeatureCollectionTest.java b/core/src/test/java/com/graphhopper/json/geo/JsonFeatureCollectionTest.java
similarity index 85%
rename from reader-json/src/test/java/com/graphhopper/json/geo/JsonFeatureCollectionTest.java
rename to core/src/test/java/com/graphhopper/json/geo/JsonFeatureCollectionTest.java
index 36ffbf8bb..3bd197e2a 100644
--- a/reader-json/src/test/java/com/graphhopper/json/geo/JsonFeatureCollectionTest.java
+++ b/core/src/test/java/com/graphhopper/json/geo/JsonFeatureCollectionTest.java
@@ -17,13 +17,14 @@
  */
 package com.graphhopper.json.geo;
 
-import com.graphhopper.json.GHJson;
-import com.graphhopper.json.GHJsonFactory;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.graphhopper.util.ObjectMapperFactory;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.PointList;
 import com.vividsolutions.jts.geom.LineString;
 import org.junit.Test;
 
+import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.util.Map;
@@ -34,11 +35,12 @@ import static org.junit.Assert.assertEquals;
  * @author Peter Karich
  */
 public class JsonFeatureCollectionTest {
-    private final GHJson ghson = new GHJsonFactory().create();
+    private final ObjectMapper objectMapper = ObjectMapperFactory.create();
 
     @Test
-    public void testDeserialization() {
-        JsonFeatureCollection data = getJson("geojson1.json");
+    public void testDeserialization() throws IOException {
+        Reader reader = new InputStreamReader(getClass().getResourceAsStream("geojson1.json"), Helper.UTF_CS);
+        JsonFeatureCollection data = objectMapper.readValue(reader, JsonFeatureCollection.class);
         assertEquals(3, data.getFeatures().size());
 
         JsonFeature f1 = data.getFeatures().get(0);
@@ -61,8 +63,4 @@ public class JsonFeatureCollectionTest {
         assertEquals("a", ((Map) f3.getProperty("prop1")).get("test"));
     }
 
-    JsonFeatureCollection getJson(String name) {
-        Reader reader = new InputStreamReader(getClass().getResourceAsStream(name), Helper.UTF_CS);
-        return ghson.fromJson(reader, JsonFeatureCollection.class);
-    }
 }
diff --git a/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java b/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
index 608ff250d..3c3af34e5 100644
--- a/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
+++ b/core/src/test/java/com/graphhopper/routing/AbstractRoutingAlgorithmTester.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.carrotsearch.hppc.IntIndexedContainer;
 import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.FastestWeighting;
@@ -182,7 +183,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         GraphHopperStorage ghStorage = createTestStorage();
         RoutingAlgorithm algo = createAlgo(ghStorage);
         Path p = algo.calcPath(0, 7);
-        assertEquals(p.toString(), Helper.createTList(0, 4, 5, 7), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(new int[]{0, 4, 5, 7}), p.calcNodes());
         assertEquals(p.toString(), 62.1, p.getDistance(), .1);
     }
 
@@ -194,7 +195,7 @@ public abstract class AbstractRoutingAlgorithmTester {
 
         RoutingAlgorithm algo = createAlgo(graph);
         Path p = algo.calcPath(0, 0);
-        assertEquals(p.toString(), Helper.createTList(0), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(new int[]{0}), p.calcNodes());
         assertEquals(p.toString(), 0, p.getDistance(), 1.e-6);
     }
 
@@ -211,7 +212,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         graph.edge(4, 1, 9, true);
         Path p = createAlgo(graph).calcPath(0, 4);
         assertEquals(p.toString(), 20, p.getDistance(), 1e-4);
-        assertEquals(Helper.createTList(0, 2, 1, 4), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 2, 1, 4}), p.calcNodes());
     }
 
     @Test
@@ -224,7 +225,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         graph.edge(4, 1, 9, true);
         Path p = createAlgo(graph).calcPath(3, 5);
         assertEquals(p.toString(), 28, p.getDistance(), 1e-4);
-        assertEquals(Helper.createTList(3, 2, 1, 4, 5), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{3, 2, 1, 4, 5}), p.calcNodes());
     }
 
     // see calc-fastest-graph.svg
@@ -234,7 +235,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         initDirectedAndDiffSpeed(graphShortest, carEncoder);
         Path p1 = createAlgo(graphShortest, defaultOpts).
                 calcPath(0, 3);
-        assertEquals(Helper.createTList(0, 1, 5, 2, 3), p1.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 1, 5, 2, 3}), p1.calcNodes());
         assertEquals(p1.toString(), 402.3, p1.getDistance(), .1);
         assertEquals(p1.toString(), 144823, p1.getTime());
 
@@ -244,7 +245,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         initDirectedAndDiffSpeed(graphFastest, carEncoder);
         Path p2 = createAlgo(graphFastest, opts).
                 calcPath(0, 3);
-        assertEquals(Helper.createTList(0, 4, 6, 7, 5, 3), p2.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 4, 6, 7, 5, 3}), p2.calcNodes());
         assertEquals(p2.toString(), 1261.7, p2.getDistance(), 0.1);
         assertEquals(p2.toString(), 111442, p2.getTime());
     }
@@ -299,7 +300,7 @@ public abstract class AbstractRoutingAlgorithmTester {
                 calcPath(0, 7);
         assertEquals(p1.toString(), 17000, p1.getDistance(), 1e-6);
         assertEquals(p1.toString(), 12240 * 1000, p1.getTime());
-        assertEquals(Helper.createTList(0, 4, 5, 7), p1.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 4, 5, 7}), p1.calcNodes());
     }
 
     protected void initFootVsCar(Graph graph) {
@@ -394,14 +395,14 @@ public abstract class AbstractRoutingAlgorithmTester {
     public void testWikipediaShortestPath() {
         GraphHopperStorage ghStorage = createWikipediaTestGraph();
         Path p = createAlgo(ghStorage).calcPath(0, 4);
-        assertEquals(p.toString(), Helper.createTList(0, 2, 5, 4), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(new int[]{0, 2, 5, 4}), p.calcNodes());
         assertEquals(p.toString(), 20, p.getDistance(), 1e-4);
     }
 
     @Test
     public void testCalcIf1EdgeAway() {
         Path p = createAlgo(createTestStorage()).calcPath(1, 2);
-        assertEquals(Helper.createTList(1, 2), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{1, 2}), p.calcNodes());
         assertEquals(p.toString(), 35.1, p.getDistance(), .1);
     }
 
@@ -428,12 +429,12 @@ public abstract class AbstractRoutingAlgorithmTester {
         // PrepareTowerNodesShortcutsTest.printEdges((CHGraph) graph);
         Path p = createAlgo(graph).calcPath(0, 4);
         // PrepareTowerNodesShortcutsTest.printEdges((CHGraph) graph);
-        assertEquals(p.toString(), Helper.createTList(0, 7, 6, 8, 3, 4), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(new int[]{0, 7, 6, 8, 3, 4}), p.calcNodes());
         assertEquals(p.toString(), 335.8, p.getDistance(), .1);
 
         p = createAlgo(graph).calcPath(1, 2);
         // the other way around is even larger as 0-1 is already 11008.452
-        assertEquals(p.toString(), Helper.createTList(1, 2), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(new int[]{1, 2}), p.calcNodes());
         assertEquals(p.toString(), 10007.7, p.getDistance(), .1);
     }
 
@@ -497,7 +498,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         Path p = createAlgo(graph).calcPath(0, 4);
         assertEquals(p.toString(), 40, p.getDistance(), 1e-4);
         assertEquals(p.toString(), 5, p.calcNodes().size());
-        assertEquals(Helper.createTList(0, 7, 6, 5, 4), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 7, 6, 5, 4}), p.calcNodes());
     }
 
     @Test
@@ -508,8 +509,8 @@ public abstract class AbstractRoutingAlgorithmTester {
         assertEquals(12, p.calcNodes().size());
 
         IntIndexedContainer list = p.calcNodes();
-        if (!Helper.createTList(36, 46, 56, 66, 76, 86, 85, 84, 94, 93, 92, 91).equals(list)
-                && !Helper.createTList(36, 46, 56, 66, 76, 86, 85, 84, 83, 82, 92, 91).equals(list)) {
+        if (!IntArrayList.from(new int[]{36, 46, 56, 66, 76, 86, 85, 84, 94, 93, 92, 91}).equals(list)
+                && !IntArrayList.from(new int[]{36, 46, 56, 66, 76, 86, 85, 84, 83, 82, 92, 91}).equals(list)) {
             assertTrue("wrong locations: " + list.toString(), false);
         }
         assertEquals(66f, p.getDistance(), 1e-3);
@@ -520,14 +521,14 @@ public abstract class AbstractRoutingAlgorithmTester {
 
     public void testBug1(GraphHopperStorage g) {
         Path p = createAlgo(g).calcPath(34, 36);
-        assertEquals(Helper.createTList(34, 35, 36), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{34, 35, 36}), p.calcNodes());
         assertEquals(3, p.calcNodes().size());
         assertEquals(17, p.getDistance(), 1e-5);
     }
 
     public void testCorrectWeight(GraphHopperStorage g) {
         Path p = createAlgo(g).calcPath(45, 72);
-        assertEquals(Helper.createTList(45, 44, 54, 64, 74, 73, 72), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{45, 44, 54, 64, 74, 73, 72}), p.calcNodes());
         assertEquals(38f, p.getDistance(), 1e-3);
     }
 
@@ -552,7 +553,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         graph.edge(4, 2, 1, false);
 
         Path p = createAlgo(graph).calcPath(0, 2);
-        assertEquals(Helper.createTList(0, 1, 2), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 1, 2}), p.calcNodes());
         assertEquals(p.toString(), 5.99, p.getDistance(), 1e-4);
         assertEquals(p.toString(), 3, p.calcNodes().size());
     }
@@ -567,7 +568,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         graph.edge(3, 1, 4, true);
 
         Path p = createAlgo(graph).calcPath(0, 3);
-        assertEquals(Helper.createTList(0, 1, 2, 3), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 1, 2, 3}), p.calcNodes());
     }
 
     // a-b-0-c-1
@@ -599,12 +600,12 @@ public abstract class AbstractRoutingAlgorithmTester {
         AlgorithmOptions opts = new AlgorithmOptions(DIJKSTRA_BI, weighting);
         RoutingAlgorithmFactory prepare = createFactory(graph, opts);
         Path p = prepare.createAlgo(getGraph(graph, opts.getWeighting()), opts).calcPath(4, 0);
-        assertEquals(Helper.createTList(4, 1, 0), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{4, 1, 0}), p.calcNodes());
         assertEquals(Helper.createPointList(0, 2, 1, 1.5, 1.5, 1, 1, 0.6), p.calcPoints());
         assertEquals(274128, p.calcPoints().calcDistance(new DistanceCalcEarth()), 1);
 
         p = prepare.createAlgo(getGraph(graph, opts.getWeighting()), opts).calcPath(2, 1);
-        assertEquals(Helper.createTList(2, 0, 1), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{2, 0, 1}), p.calcNodes());
         assertEquals(Helper.createPointList(0, 0, 1, 0.6, 1.5, 1, 1, 1.5), p.calcPoints());
         assertEquals(279482, p.calcPoints().calcDistance(new DistanceCalcEarth()), 1);
     }
@@ -622,20 +623,20 @@ public abstract class AbstractRoutingAlgorithmTester {
         // identical tower nodes
         Path p = calcPathViaQuery(ghStorage, 0.001, 0.000, 0.001, 0.000);
         assertTrue(p.isFound());
-        assertEquals(Helper.createTList(0), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0}), p.calcNodes());
         // assertEquals(1, p.calcPoints().size());
         assertEquals(p.toString(), 0, p.getDistance(), 1e-4);
 
         // identical query points on edge
         p = calcPath(ghStorage, 0, 1, 0, 1);
         assertTrue(p.isFound());
-        assertEquals(Helper.createTList(8), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{8}), p.calcNodes());
         // assertEquals(1, p.calcPoints().size());
         assertEquals(p.toString(), 0, p.getDistance(), 1e-4);
 
         // very close
         p = calcPathViaQuery(ghStorage, 0.00092, 0, 0.00091, 0);
-        assertEquals(Helper.createTList(8, 9), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{8, 9}), p.calcNodes());
         assertEquals(p.toString(), 1.11, p.getDistance(), .1);
     }
 
@@ -646,12 +647,12 @@ public abstract class AbstractRoutingAlgorithmTester {
 
         // 0-7 to 4-3
         Path p = calcPathViaQuery(graph, 0.0009, 0, 0.001, 0.001105);
-        assertEquals(p.toString(), Helper.createTList(10, 7, 6, 8, 3, 9), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(new int[]{10, 7, 6, 8, 3, 9}), p.calcNodes());
         assertEquals(p.toString(), 324.11, p.getDistance(), 0.01);
 
         // 0-1 to 2-3
         p = calcPathViaQuery(graph, 0.001, 0.0001, 0.010, 0.0011);
-        assertEquals(p.toString(), Helper.createTList(0, 7, 6, 8, 3, 9), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(new int[]{0, 7, 6, 8, 3, 9}), p.calcNodes());
         assertEquals(p.toString(), 1335.35, p.getDistance(), 0.01);
     }
 
@@ -659,7 +660,7 @@ public abstract class AbstractRoutingAlgorithmTester {
     public void testViaEdges_WithCoordinates() {
         GraphHopperStorage ghStorage = createTestStorage();
         Path p = calcPath(ghStorage, 0, 1, 2, 3);
-        assertEquals(Helper.createTList(8, 1, 2, 9), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{8, 1, 2, 9}), p.calcNodes());
         assertEquals(p.toString(), 56.7, p.getDistance(), .1);
     }
 
@@ -683,17 +684,17 @@ public abstract class AbstractRoutingAlgorithmTester {
 
         // 0-1 to 3-4
         Path p = calcPathViaQuery(graph, 0.00010, 0.00001, 0, 0.00009);
-        assertEquals(Helper.createTList(5, 1, 2, 3, 6), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 1, 2, 3, 6}), p.calcNodes());
         assertEquals(p.toString(), 26.81, p.getDistance(), .1);
 
         // overlapping edges: 2-3 and 3-2
         p = calcPathViaQuery(graph, 0.000049, 0.00014, 0.00001, 0.0001);
-        assertEquals(Helper.createTList(5, 6), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6}), p.calcNodes());
         assertEquals(p.toString(), 6.2, p.getDistance(), .1);
 
         // 'from' and 'to' edge share one node '2': 1-2 to 3-2
         p = calcPathViaQuery(graph, 0.00009, 0.00011, 0.00001, 0.00011);
-        assertEquals(p.toString(), Helper.createTList(6, 2, 5), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(new int[]{6, 2, 5}), p.calcNodes());
         assertEquals(p.toString(), 12.57, p.getDistance(), .1);
     }
 
@@ -703,7 +704,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         GraphHopperStorage graph = createGHStorage(encodingManager, Arrays.asList(weighting), false);
         initDirectedAndDiffSpeed(graph, carEncoder);
         Path p = calcPathViaQuery(weighting, graph, 0.002, 0.0005, 0.0017, 0.0031);
-        assertEquals(Helper.createTList(8, 1, 5, 3, 9), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{8, 1, 5, 3, 9}), p.calcNodes());
         assertEquals(602.98, p.getDistance(), 1e-1);
     }
 
@@ -853,7 +854,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         initEleGraph(graph);
         Path p = createAlgo(graph, opts).calcPath(0, 10);
         // GHUtility.printEdgeInfo(graph, carEncoder);
-        assertEquals(Helper.createTList(0, 4, 6, 10), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 4, 6, 10}), p.calcNodes());
 
         AlgorithmOptions fakeOpts = AlgorithmOptions.start().weighting(fakeWeighting).build();
         graph = createGHStorage(encodingManager, Arrays.asList(fakeOpts.getWeighting()), true);
@@ -864,7 +865,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         RoutingAlgorithmFactory factory = createFactory(graph, fakeOpts);
         QueryGraph qGraph = new QueryGraph(getGraph(graph, fakeWeighting)).lookup(from, to);
         p = factory.createAlgo(qGraph, fakeOpts).calcPath(from.getClosestNode(), to.getClosestNode());
-        assertEquals(Helper.createTList(12, 0, 1, 2, 11, 7, 10, 13), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{12, 0, 1, 2, 11, 7, 10, 13}), p.calcNodes());
         assertEquals(37009621, p.getTime());
         assertEquals(616827, p.getDistance(), 1);
         assertEquals(493462, p.getWeight(), 1);
@@ -893,7 +894,7 @@ public abstract class AbstractRoutingAlgorithmTester {
         RoutingAlgorithm algoCar = createFactory(ghStorage, carOptions).
                 createAlgo(getGraph(ghStorage, carWeighting), carOptions);
         Path p1 = algoCar.calcPath(0, 7);
-        assertEquals(Helper.createTList(0, 1, 5, 6, 7), p1.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 1, 5, 6, 7}), p1.calcNodes());
         assertEquals(p1.toString(), 26000, p1.getDistance(), 1e-6);
     }
 
diff --git a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
index 492251272..c80f0eefa 100644
--- a/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
+++ b/core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.routing.AlternativeRoute.AlternativeBidirSearch;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
@@ -28,7 +29,6 @@ import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphExtension;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.RAMDirectory;
-import com.graphhopper.util.Helper;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -123,12 +123,12 @@ public class AlternativeRouteTest {
         assertEquals(bestPath.calcNodes(), bestAlt.calcNodes());
         assertEquals(bestPath.getWeight(), bestAlt.getWeight(), 1e-3);
 
-        assertEquals(Helper.createTList(5, 6, 3, 4), bestAlt.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6, 3, 4}), bestAlt.calcNodes());
 
         // Note: here plateau is longer, even longer than optimum, but path is longer
         // so which alternative is better? longer plateau.weight with bigger path.weight or smaller path.weight with smaller plateau.weight
         // assertEquals(Helper.createTList(5, 1, 9, 2, 3, 4), secondAlt.calcNodes());
-        assertEquals(Helper.createTList(5, 6, 7, 8, 4), secondAlt.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6, 7, 8, 4}), secondAlt.calcNodes());
         assertEquals(1667.9, secondAlt.getWeight(), .1);
     }
 
@@ -149,9 +149,9 @@ public class AlternativeRouteTest {
         assertEquals(3, pathInfos.size());
 
         // result is sorted based on the plateau to full weight ratio
-        assertEquals(Helper.createTList(5, 6, 3, 4), pathInfos.get(0).getPath().calcNodes());
-        assertEquals(Helper.createTList(5, 6, 7, 8, 4), pathInfos.get(1).getPath().calcNodes());
-        assertEquals(Helper.createTList(5, 1, 9, 2, 3, 4), pathInfos.get(2).getPath().calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6, 3, 4}), pathInfos.get(0).getPath().calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6, 7, 8, 4}), pathInfos.get(1).getPath().calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 1, 9, 2, 3, 4}), pathInfos.get(2).getPath().calcNodes());
         assertEquals(2416.0, pathInfos.get(2).getPath().getWeight(), .1);
     }
 
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
index a1a23ab1a..3be22f30f 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraBidirectionCHTest.java
@@ -26,7 +26,6 @@ import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.util.CHEdgeIteratorState;
 import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.Helper;
 import com.graphhopper.util.Parameters;
 import org.junit.Test;
 
@@ -110,7 +109,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
                 ghStorage, g2, weighting, TraversalMode.NODE_BASED).
                 createAlgo(g2, opts).calcPath(0, 7);
 
-        assertEquals(Helper.createTList(0, 2, 5, 7), p.calcNodes());
+        assertEquals(IntArrayList.from(0, 2, 5, 7), p.calcNodes());
         assertEquals(1064, p.getTime());
         assertEquals(4.2, p.getDistance(), 1e-5);
     }
@@ -131,7 +130,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
 
         // use base graph for solving normal Dijkstra
         Path p1 = new RoutingAlgorithmFactorySimple().createAlgo(ghStorage, defaultOpts).calcPath(0, 3);
-        assertEquals(Helper.createTList(0, 1, 5, 2, 3), p1.calcNodes());
+        assertEquals(IntArrayList.from(0, 1, 5, 2, 3), p1.calcNodes());
         assertEquals(p1.toString(), 402.29, p1.getDistance(), 1e-2);
         assertEquals(p1.toString(), 144823, p1.getTime());
     }
@@ -152,12 +151,12 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
 
         // use contracted graph
         Path p1 = contractedFactory.createAlgo(getGraph(g, carOptions.getWeighting()), carOptions).calcPath(0, 7);
-        assertEquals(Helper.createTList(0, 4, 6, 7), p1.calcNodes());
+        assertEquals(IntArrayList.from(0, 4, 6, 7), p1.calcNodes());
         assertEquals(p1.toString(), 15000, p1.getDistance(), 1e-6);
 
         // use base graph for solving normal Dijkstra via car
         Path p2 = new RoutingAlgorithmFactorySimple().createAlgo(g, carOptions).calcPath(0, 7);
-        assertEquals(Helper.createTList(0, 4, 6, 7), p2.calcNodes());
+        assertEquals(IntArrayList.from(0, 4, 6, 7), p2.calcNodes());
         assertEquals(p2.toString(), 15000, p2.getDistance(), 1e-6);
         assertEquals(p2.toString(), 2700 * 1000, p2.getTime());
 
@@ -165,7 +164,7 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         Path p3 = new RoutingAlgorithmFactorySimple().createAlgo(g, footOptions).calcPath(0, 7);
         assertEquals(p3.toString(), 17000, p3.getDistance(), 1e-6);
         assertEquals(p3.toString(), 12240 * 1000, p3.getTime());
-        assertEquals(Helper.createTList(0, 4, 5, 7), p3.calcNodes());
+        assertEquals(IntArrayList.from(0, 4, 5, 7), p3.calcNodes());
     }
 
     // 7------8------.---9----0
@@ -206,14 +205,14 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
         // note that node 9 will be visited by both forward and backward searches
         assertEquals(7, algo.getVisitedNodes());
         assertEquals(102, p.getDistance(), 1.e-3);
-        assertEquals(p.toString(), Helper.createTList(1, 8, 9, 0), p.calcNodes());
+        assertEquals(p.toString(), IntArrayList.from(1, 8, 9, 0), p.calcNodes());
 
         // without stalling we visit 11 nodes
         RoutingAlgorithm algoNoSod = createCHAlgo(graph, chGraph, false, defaultOpts);
         Path pNoSod = algoNoSod.calcPath(1, 0);
         assertEquals(11, algoNoSod.getVisitedNodes());
         assertEquals(102, pNoSod.getDistance(), 1.e-3);
-        assertEquals(pNoSod.toString(), Helper.createTList(1, 8, 9, 0), pNoSod.calcNodes());
+        assertEquals(pNoSod.toString(), IntArrayList.from(1, 8, 9, 0), pNoSod.calcNodes());
     }
 
     // t(0)--slow->1--s(2)
@@ -222,8 +221,8 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
     //      \--<---|
     @Test
     public void testDirectionDependentSpeedFwdSearch() {
-        runTestWithDirectionDependentEdgeSpeed(10, 20, 0, 2, Helper.createTList(0, 1, 2), new MotorcycleFlagEncoder());
-        runTestWithDirectionDependentEdgeSpeed(10, 20, 0, 2, Helper.createTList(0, 1, 2), new Bike2WeightFlagEncoder());
+        runTestWithDirectionDependentEdgeSpeed(10, 20, 0, 2, IntArrayList.from(0, 1, 2), new MotorcycleFlagEncoder());
+        runTestWithDirectionDependentEdgeSpeed(10, 20, 0, 2, IntArrayList.from(0, 1, 2), new Bike2WeightFlagEncoder());
     }
 
     // s(0)--fast->1--t(2)
@@ -232,8 +231,8 @@ public class DijkstraBidirectionCHTest extends AbstractRoutingAlgorithmTester {
     //      \--<---|
     @Test
     public void testDirectionDependentSpeedBwdSearch() {
-        runTestWithDirectionDependentEdgeSpeed(20, 10, 2, 0, Helper.createTList(2, 1, 0), new MotorcycleFlagEncoder());
-        runTestWithDirectionDependentEdgeSpeed(20, 10, 2, 0, Helper.createTList(2, 1, 0), new Bike2WeightFlagEncoder());
+        runTestWithDirectionDependentEdgeSpeed(20, 10, 2, 0, IntArrayList.from(2, 1, 0), new MotorcycleFlagEncoder());
+        runTestWithDirectionDependentEdgeSpeed(20, 10, 2, 0, IntArrayList.from(2, 1, 0), new Bike2WeightFlagEncoder());
     }
 
     private void runTestWithDirectionDependentEdgeSpeed(
diff --git a/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java b/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
index 76f8ca1e9..5450a50d8 100644
--- a/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
+++ b/core/src/test/java/com/graphhopper/routing/DijkstraOneToManyTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.TraversalMode;
 import com.graphhopper.routing.weighting.FastestWeighting;
@@ -24,7 +25,6 @@ import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.Helper;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -130,11 +130,11 @@ public class DijkstraOneToManyTest extends AbstractRoutingAlgorithmTester {
         initGraph(storage);
         RoutingAlgorithm algo = createAlgo(storage);
         Path p = algo.calcPath(0, 8);
-        assertEquals(Helper.createTList(0, 7, 8), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 7, 8}), p.calcNodes());
 
         // expand SPT
         p = algo.calcPath(0, 10);
-        assertEquals(Helper.createTList(0, 1, 2, 3, 4, 10), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 1, 2, 3, 4, 10}), p.calcNodes());
     }
 
     @Test
@@ -160,15 +160,15 @@ public class DijkstraOneToManyTest extends AbstractRoutingAlgorithmTester {
     public void testUseCache() {
         RoutingAlgorithm algo = createAlgo(createTestStorage());
         Path p = algo.calcPath(0, 4);
-        assertEquals(Helper.createTList(0, 4), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 4}), p.calcNodes());
 
         // expand SPT
         p = algo.calcPath(0, 7);
-        assertEquals(Helper.createTList(0, 4, 5, 7), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 4, 5, 7}), p.calcNodes());
 
         // use SPT
         p = algo.calcPath(0, 2);
-        assertEquals(Helper.createTList(0, 1, 2), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 1, 2}), p.calcNodes());
     }
 
     @Test
@@ -188,7 +188,7 @@ public class DijkstraOneToManyTest extends AbstractRoutingAlgorithmTester {
             }
         });
         Path p = algo.calcPath(4, 6);
-        assertEquals(Helper.createTList(4, 3, 6), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{4, 3, 6}), p.calcNodes());
 
         // important call!
         algo.clear();
@@ -199,7 +199,7 @@ public class DijkstraOneToManyTest extends AbstractRoutingAlgorithmTester {
             }
         });
         p = algo.calcPath(4, 6);
-        assertEquals(Helper.createTList(4, 5, 6), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{4, 5, 6}), p.calcNodes());
     }
 
     private Graph initGraph(Graph g) {
@@ -244,15 +244,15 @@ public class DijkstraOneToManyTest extends AbstractRoutingAlgorithmTester {
         assertEquals(0, p.distance, 0.00000);
 
         p = algo.calcPath(0, 4);
-        assertEquals(Helper.createTList(0, 4), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 4}), p.calcNodes());
 
         // expand SPT
         p = algo.calcPath(0, 7);
-        assertEquals(Helper.createTList(0, 4, 5, 7), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 4, 5, 7}), p.calcNodes());
 
         // use SPT
         p = algo.calcPath(0, 2);
-        assertEquals(Helper.createTList(0, 1, 2), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 1, 2}), p.calcNodes());
     }
 
 }
diff --git a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
index 86e938f65..e934b652d 100644
--- a/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
+++ b/core/src/test/java/com/graphhopper/routing/EdgeBasedRoutingAlgorithmTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.carrotsearch.hppc.cursors.IntCursor;
 import com.graphhopper.routing.util.*;
 import com.graphhopper.routing.weighting.FastestWeighting;
@@ -27,7 +28,6 @@ import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.TurnCostExtension;
 import com.graphhopper.util.EdgeIteratorState;
-import com.graphhopper.util.Helper;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -140,20 +140,20 @@ public class EdgeBasedRoutingAlgorithmTest {
                 weighting(createWeighting(carEncoder, tcs, 40)).
                 traversalMode(TraversalMode.EDGE_BASED_2DIR).build()).
                 calcPath(5, 1);
-        assertEquals(Helper.createTList(5, 2, 3, 4, 7, 6, 3, 1), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 2, 3, 4, 7, 6, 3, 1}), p.calcNodes());
 
         // test 7-6-5 and reverse
         p = createAlgo(g, AlgorithmOptions.start().
                 weighting(createWeighting(carEncoder, tcs, 40)).
                 traversalMode(TraversalMode.EDGE_BASED_1DIR).build()).
                 calcPath(5, 7);
-        assertEquals(Helper.createTList(5, 6, 7), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6, 7}), p.calcNodes());
 
         p = createAlgo(g, AlgorithmOptions.start().
                 weighting(createWeighting(carEncoder, tcs, 40)).
                 traversalMode(TraversalMode.EDGE_BASED_1DIR).build()).
                 calcPath(7, 5);
-        assertEquals(Helper.createTList(7, 6, 3, 2, 5), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{7, 6, 3, 2, 5}), p.calcNodes());
     }
 
 
@@ -214,7 +214,7 @@ public class EdgeBasedRoutingAlgorithmTest {
                 traversalMode(TraversalMode.EDGE_BASED_2DIR_UTURN).build();
         Path p = createAlgo(g, opts).calcPath(7, 5);
 
-        assertEquals(Helper.createTList(7, 6, 3, 6, 5), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{7, 6, 3, 6, 5}), p.calcNodes());
 
         // no u-turn for 6-3
         opts = AlgorithmOptions.start().
@@ -223,7 +223,7 @@ public class EdgeBasedRoutingAlgorithmTest {
         tcs.addTurnInfo(getEdge(g, 6, 3).getEdge(), 3, getEdge(g, 3, 6).getEdge(), tflags);
         p = createAlgo(g, opts).calcPath(7, 5);
 
-        assertEquals(Helper.createTList(7, 6, 3, 2, 5), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{7, 6, 3, 2, 5}), p.calcNodes());
     }
 
     @Test
@@ -239,7 +239,7 @@ public class EdgeBasedRoutingAlgorithmTest {
         // no restriction and costs
         EdgeIteratorState e3_6 = getEdge(g, 5, 6);
         e3_6.setDistance(2);
-        assertEquals(Helper.createTList(5, 2, 3, 1), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 2, 3, 1}), p.calcNodes());
 
         // now introduce some turn costs
         long tflags = carEncoder.getTurnFlags(false, 2);
@@ -249,7 +249,7 @@ public class EdgeBasedRoutingAlgorithmTest {
                 weighting(createWeighting(carEncoder, tcs, 40)).
                 traversalMode(TraversalMode.EDGE_BASED_1DIR).build()).
                 calcPath(5, 1);
-        assertEquals(Helper.createTList(5, 6, 3, 1), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6, 3, 1}), p.calcNodes());
     }
 
     @Test
@@ -279,7 +279,7 @@ public class EdgeBasedRoutingAlgorithmTest {
                 }.setDefaultUTurnCost(40)).
                 traversalMode(TraversalMode.EDGE_BASED_2DIR).build()).
                 calcPath(5, 1);
-        assertEquals(Helper.createTList(5, 6, 7, 4, 3, 1), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6, 7, 4, 3, 1}), p.calcNodes());
         assertEquals(301, p.getTime(), .1);
     }
 }
diff --git a/core/src/test/java/com/graphhopper/routing/PathBidirRefTest.java b/core/src/test/java/com/graphhopper/routing/PathBidirRefTest.java
index 2fbb6ca6e..465008a07 100644
--- a/core/src/test/java/com/graphhopper/routing/PathBidirRefTest.java
+++ b/core/src/test/java/com/graphhopper/routing/PathBidirRefTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.routing.util.DefaultEdgeFilter;
 import com.graphhopper.routing.util.EdgeFilter;
 import com.graphhopper.routing.util.EncodingManager;
@@ -27,7 +28,6 @@ import com.graphhopper.storage.GraphBuilder;
 import com.graphhopper.storage.SPTEntry;
 import com.graphhopper.util.EdgeExplorer;
 import com.graphhopper.util.EdgeIterator;
-import com.graphhopper.util.Helper;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -56,7 +56,7 @@ public class PathBidirRefTest {
         pw.sptEntry.parent = new SPTEntry(EdgeIterator.NO_EDGE, 1, 10);
         pw.edgeTo = new SPTEntry(EdgeIterator.NO_EDGE, 2, 0);
         Path p = pw.extract();
-        assertEquals(Helper.createTList(1, 2), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{1, 2}), p.calcNodes());
         assertEquals(10, p.getDistance(), 1e-4);
     }
 
@@ -78,7 +78,7 @@ public class PathBidirRefTest {
         pw.edgeTo = new SPTEntry(iter.getEdge(), 2, 20);
         pw.edgeTo.parent = new SPTEntry(EdgeIterator.NO_EDGE, 3, 0);
         Path p = pw.extract();
-        assertEquals(Helper.createTList(1, 2, 3), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{1, 2, 3}), p.calcNodes());
         assertEquals(30, p.getDistance(), 1e-4);
     }
 }
diff --git a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
index d82556518..04d7cf1c3 100644
--- a/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
+++ b/core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.ch;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.carrotsearch.hppc.IntIndexedContainer;
 import com.graphhopper.routing.*;
 import com.graphhopper.routing.util.BikeFlagEncoder;
@@ -191,7 +192,7 @@ public class PrepareContractionHierarchiesTest {
         RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(4, 2);
         assertEquals(3, p.getDistance(), 1e-6);
-        assertEquals(Helper.createTList(4, 3, 5, 2), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{4, 3, 5, 2}), p.calcNodes());
     }
 
     @Test
@@ -214,7 +215,7 @@ public class PrepareContractionHierarchiesTest {
         RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(0, 10);
         assertEquals(10, p.getDistance(), 1e-6);
-        assertEquals(Helper.createTList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), p.calcNodes());
     }
 
     void initRoundaboutGraph(Graph g) {
@@ -280,7 +281,7 @@ public class PrepareContractionHierarchiesTest {
         assertEquals(oldCount + 23, lg.getAllEdges().length());
         RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(4, 7);
-        assertEquals(Helper.createTList(4, 5, 6, 7), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{4, 5, 6, 7}), p.calcNodes());
     }
 
     void initUnpackingGraph(GraphHopperStorage ghStorage, CHGraph g, Weighting w) {
@@ -337,7 +338,7 @@ public class PrepareContractionHierarchiesTest {
         RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(10, 6);
         assertEquals(7, p.getDistance(), 1e-5);
-        assertEquals(Helper.createTList(10, 0, 1, 2, 3, 4, 5, 6), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{10, 0, 1, 2, 3, 4, 5, 6}), p.calcNodes());
     }
 
     @Test
@@ -351,7 +352,7 @@ public class PrepareContractionHierarchiesTest {
         RoutingAlgorithm algo = prepare.createAlgo(lg, new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         Path p = algo.calcPath(10, 6);
         assertEquals(7, p.getDistance(), 1e-1);
-        assertEquals(Helper.createTList(10, 0, 1, 2, 3, 4, 5, 6), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{10, 0, 1, 2, 3, 4, 5, 6}), p.calcNodes());
     }
 
     @Test
@@ -451,7 +452,7 @@ public class PrepareContractionHierarchiesTest {
         ghStorage.freeze();
 
         for (Weighting w : chWeightings) {
-            checkPath(ghStorage, w, 7, 5, Helper.createTList(3, 9, 14, 16, 13, 12));
+            checkPath(ghStorage, w, 7, 5, IntArrayList.from(new int[]{3, 9, 14, 16, 13, 12}));
         }
     }
 
@@ -472,9 +473,9 @@ public class PrepareContractionHierarchiesTest {
 
         ghStorage.freeze();
 
-        checkPath(ghStorage, carWeighting, 7, 5, Helper.createTList(3, 9, 14, 16, 13, 12));
+        checkPath(ghStorage, carWeighting, 7, 5, IntArrayList.from(new int[]{3, 9, 14, 16, 13, 12}));
         // detour around blocked 9,14
-        checkPath(ghStorage, bikeWeighting, 9, 5, Helper.createTList(3, 10, 14, 16, 13, 12));
+        checkPath(ghStorage, bikeWeighting, 9, 5, IntArrayList.from(new int[]{3, 10, 14, 16, 13, 12}));
     }
 
     void checkPath(GraphHopperStorage ghStorage, Weighting w, int expShortcuts, double expDistance, IntIndexedContainer expNodes) {
diff --git a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java
index 56cc0dd07..bab811f35 100644
--- a/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java
+++ b/core/src/test/java/com/graphhopper/routing/subnetwork/PrepareRoutingSubnetworksTest.java
@@ -26,7 +26,6 @@ import com.graphhopper.util.EdgeExplorer;
 import com.graphhopper.util.EdgeIterator;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.GHUtility;
-import com.graphhopper.util.Helper;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -104,10 +103,10 @@ public class PrepareRoutingSubnetworksTest {
         assertEquals(3, components.size());
 
         // start is at 0 => large network
-        assertEquals(Helper.createTList(0, 7, 3, 13, 5), components.get(0));
+        assertEquals(IntArrayList.from(0, 7, 3, 13, 5), components.get(0));
         // next smallest and unvisited node is 1 => big network
-        assertEquals(Helper.createTList(1, 8, 4, 2, 11, 12, 9, 15), components.get(1));
-        assertEquals(Helper.createTList(6, 14, 10), components.get(2));
+        assertEquals(IntArrayList.from(1, 8, 4, 2, 11, 12, 9, 15), components.get(1));
+        assertEquals(IntArrayList.from(6, 14, 10), components.get(2));
     }
 
     @Test
diff --git a/core/src/test/java/com/graphhopper/routing/template/RoundTripRoutingTemplateTest.java b/core/src/test/java/com/graphhopper/routing/template/RoundTripRoutingTemplateTest.java
index 598fa3be9..6f9fbaa5d 100644
--- a/core/src/test/java/com/graphhopper/routing/template/RoundTripRoutingTemplateTest.java
+++ b/core/src/test/java/com/graphhopper/routing/template/RoundTripRoutingTemplateTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.routing.template;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.GHRequest;
 import com.graphhopper.GHResponse;
 import com.graphhopper.routing.*;
@@ -27,7 +28,6 @@ import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.storage.index.LocationIndexTree;
 import com.graphhopper.storage.index.QueryResult;
-import com.graphhopper.util.Helper;
 import com.graphhopper.util.Parameters;
 import com.graphhopper.util.shapes.GHPoint;
 import org.junit.Test;
@@ -94,8 +94,8 @@ public class RoundTripRoutingTemplateTest {
                 queryGraph, new RoutingAlgorithmFactorySimple(), new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         // make sure the resulting paths are connected and form a round trip starting and ending at the start node 0
         assertEquals(2, paths.size());
-        assertEquals(Helper.createTList(0, 7, 6, 5), paths.get(0).calcNodes());
-        assertEquals(Helper.createTList(5, 4, 3, 2, 1, 0), paths.get(1).calcNodes());
+        assertEquals(IntArrayList.from(new int[]{0, 7, 6, 5}), paths.get(0).calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 4, 3, 2, 1, 0}), paths.get(1).calcNodes());
     }
 
     @Test
@@ -120,8 +120,8 @@ public class RoundTripRoutingTemplateTest {
         List<Path> paths = rTripRouting.calcPaths(qGraph, new RoutingAlgorithmFactorySimple(),
                 new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         assertEquals(2, paths.size());
-        assertEquals(Helper.createTList(5, 6, 3, 4), paths.get(0).calcNodes());
-        assertEquals(Helper.createTList(4, 8, 7, 6, 5), paths.get(1).calcNodes());
+        assertEquals(IntArrayList.from(new int[]{5, 6, 3, 4}), paths.get(0).calcNodes());
+        assertEquals(IntArrayList.from(new int[]{4, 8, 7, 6, 5}), paths.get(1).calcNodes());
 
         qGraph = new QueryGraph(g);
         qGraph.lookup(qr4, qr6);
@@ -129,8 +129,8 @@ public class RoundTripRoutingTemplateTest {
         paths = rTripRouting.calcPaths(qGraph, new RoutingAlgorithmFactorySimple(),
                 new AlgorithmOptions(DIJKSTRA_BI, weighting, tMode));
         assertEquals(2, paths.size());
-        assertEquals(Helper.createTList(6, 3, 4), paths.get(0).calcNodes());
-        assertEquals(Helper.createTList(4, 8, 7, 6), paths.get(1).calcNodes());
+        assertEquals(IntArrayList.from(new int[]{6, 3, 4}), paths.get(0).calcNodes());
+        assertEquals(IntArrayList.from(new int[]{4, 8, 7, 6}), paths.get(1).calcNodes());
     }
 
     private Graph createTestGraph(boolean fullGraph) {
diff --git a/reader-json/src/test/java/com/graphhopper/spatialrules/SpatialRuleLookupBuilderTest.java b/core/src/test/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupBuilderTest.java
similarity index 78%
rename from reader-json/src/test/java/com/graphhopper/spatialrules/SpatialRuleLookupBuilderTest.java
rename to core/src/test/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupBuilderTest.java
index 207cd8061..3f515e68b 100644
--- a/reader-json/src/test/java/com/graphhopper/spatialrules/SpatialRuleLookupBuilderTest.java
+++ b/core/src/test/java/com/graphhopper/routing/util/spatialrules/SpatialRuleLookupBuilderTest.java
@@ -15,16 +15,16 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package com.graphhopper.spatialrules;
+package com.graphhopper.routing.util.spatialrules;
 
-import com.graphhopper.json.GHJsonFactory;
+import com.graphhopper.util.ObjectMapperFactory;
 import com.graphhopper.json.geo.JsonFeatureCollection;
-import com.graphhopper.routing.util.spatialrules.*;
 import com.graphhopper.util.shapes.BBox;
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.io.*;
+import java.io.FileReader;
+import java.io.IOException;
 
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
@@ -38,9 +38,9 @@ public class SpatialRuleLookupBuilderTest {
     private static final String COUNTRIES_FILE = "../core/files/spatialrules/countries.geo.json";
 
     @Test
-    public void testIndex() throws FileNotFoundException {
+    public void testIndex() throws IOException {
         final FileReader reader = new FileReader(COUNTRIES_FILE);
-        SpatialRuleLookup spatialRuleLookup = SpatialRuleLookupBuilder.buildIndex(new GHJsonFactory().create().fromJson(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory());
+        SpatialRuleLookup spatialRuleLookup = SpatialRuleLookupBuilder.buildIndex(ObjectMapperFactory.create().readValue(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory());
 
         // Berlin
         Assert.assertEquals(AccessValue.EVENTUALLY_ACCESSIBLE, spatialRuleLookup.lookupRule(52.5243700, 13.4105300).getAccessValue("track", TransportationMode.MOTOR_VEHICLE, AccessValue.ACCESSIBLE));
@@ -57,9 +57,9 @@ public class SpatialRuleLookupBuilderTest {
     }
 
     @Test
-    public void testBounds() throws FileNotFoundException {
+    public void testBounds() throws IOException {
         final FileReader reader = new FileReader(COUNTRIES_FILE);
-        SpatialRuleLookup spatialRuleLookup = SpatialRuleLookupBuilder.buildIndex(new GHJsonFactory().create().fromJson(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory(), .1, new BBox(-180, 180, -90, 90));
+        SpatialRuleLookup spatialRuleLookup = SpatialRuleLookupBuilder.buildIndex(ObjectMapperFactory.create().readValue(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory(), .1, new BBox(-180, 180, -90, 90));
         BBox almostWorldWide = new BBox(-179, 179, -89, 89);
 
         // Might fail if a polygon is defined outside the above coordinates
@@ -67,20 +67,20 @@ public class SpatialRuleLookupBuilderTest {
     }
 
     @Test
-    public void testIntersection() throws FileNotFoundException {
+    public void testIntersection() throws IOException {
         /*
          We are creating a BBox smaller than Germany. We have the German Spatial rule activated by default.
          So the BBox should not contain a Point lying somewhere close in Germany.
         */
         final FileReader reader = new FileReader(COUNTRIES_FILE);
-        SpatialRuleLookup spatialRuleLookup = SpatialRuleLookupBuilder.buildIndex(new GHJsonFactory().create().fromJson(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory(), .1, new BBox(9, 10, 51, 52));
+        SpatialRuleLookup spatialRuleLookup = SpatialRuleLookupBuilder.buildIndex(ObjectMapperFactory.create().readValue(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory(), .1, new BBox(9, 10, 51, 52));
         assertFalse("BBox seems to be incorrectly contracted", spatialRuleLookup.getBounds().contains(49.9, 8.9));
     }
 
     @Test
-    public void testNoIntersection() throws FileNotFoundException {
+    public void testNoIntersection() throws IOException {
         final FileReader reader = new FileReader(COUNTRIES_FILE);
-        SpatialRuleLookup spatialRuleLookup = SpatialRuleLookupBuilder.buildIndex(new GHJsonFactory().create().fromJson(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory(), .1, new BBox(-180, -179, -90, -89));
+        SpatialRuleLookup spatialRuleLookup = SpatialRuleLookupBuilder.buildIndex(ObjectMapperFactory.create().readValue(reader, JsonFeatureCollection.class), "ISO_A3", new CountriesSpatialRuleFactory(), .1, new BBox(-180, -179, -90, -89));
         assertEquals(SpatialRuleLookup.EMPTY, spatialRuleLookup);
     }
 
diff --git a/core/src/test/java/com/graphhopper/storage/StorablePropertiesTest.java b/core/src/test/java/com/graphhopper/storage/StorablePropertiesTest.java
index 0126046b0..7fc7826cb 100644
--- a/core/src/test/java/com/graphhopper/storage/StorablePropertiesTest.java
+++ b/core/src/test/java/com/graphhopper/storage/StorablePropertiesTest.java
@@ -21,6 +21,10 @@ import com.graphhopper.util.Helper;
 import org.junit.Test;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
 
 import static org.junit.Assert.*;
 
@@ -80,4 +84,13 @@ public class StorablePropertiesTest {
 
         Helper.removeDir(new File(dir));
     }
+
+    @Test
+    public void testLoadProperties() throws IOException {
+        Map<String, String> map = new HashMap<>();
+        StorableProperties.loadProperties(map, new StringReader("blup=test\n blup2 = xy"));
+        assertEquals("test", map.get("blup"));
+        assertEquals("xy", map.get("blup2"));
+    }
+
 }
diff --git a/reader-json/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java b/core/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java
similarity index 87%
rename from reader-json/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java
rename to core/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java
index a3d9bade0..061f00154 100644
--- a/reader-json/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java
+++ b/core/src/test/java/com/graphhopper/storage/change/ChangeGraphHelperTest.java
@@ -1,8 +1,7 @@
 package com.graphhopper.storage.change;
 
-import com.graphhopper.json.GHJson;
-import com.graphhopper.json.GHJsonFactory;
-import com.graphhopper.json.JsonFeatureConverter;
+import com.graphhopper.util.ObjectMapperFactory;
+import com.graphhopper.json.geo.JsonFeatureCollection;
 import com.graphhopper.routing.AbstractRoutingAlgorithmTester;
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.util.EncodingManager;
@@ -17,6 +16,7 @@ import com.graphhopper.util.Helper;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
 
@@ -28,17 +28,15 @@ import static org.junit.Assert.*;
 public class ChangeGraphHelperTest {
     private EncodingManager encodingManager;
     private GraphHopperStorage graph;
-    private GHJson ghson;
 
     @Before
     public void setUp() {
         encodingManager = new EncodingManager("car");
         graph = new GraphBuilder(encodingManager).create();
-        ghson = new GHJsonFactory().create();
     }
 
     @Test
-    public void testApplyChanges() {
+    public void testApplyChanges() throws IOException {
         // 0-1-2
         // | |
         // 3-4
@@ -65,8 +63,8 @@ public class ChangeGraphHelperTest {
 
         Reader reader = new InputStreamReader(getClass().getResourceAsStream("overlaydata1.json"), Helper.UTF_CS);
         ChangeGraphHelper instance = new ChangeGraphHelper(graph, locationIndex);
-        JsonFeatureConverter converter = new JsonFeatureConverter(ghson, instance, encodingManager);
-        long updates = converter.applyChanges(reader);
+        JsonFeatureCollection collection = ObjectMapperFactory.create().readValue(reader, JsonFeatureCollection.class);
+        long updates = instance.applyChanges(encodingManager, collection.getFeatures());
         assertEquals(2, updates);
 
         // assert changed speed and access
diff --git a/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeTest.java b/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeTest.java
index 769432fdd..f8b955b86 100644
--- a/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeTest.java
+++ b/core/src/test/java/com/graphhopper/storage/index/LocationIndexTreeTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.storage.index;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.graphhopper.coll.GHIntHashSet;
 import com.graphhopper.routing.util.*;
 import com.graphhopper.storage.Directory;
@@ -108,7 +109,7 @@ public class LocationIndexTreeTest extends AbstractLocationIndexTester {
         LocationIndexTree index = createIndexNoPrepare(graph, 50000);
         index.prepareAlgo();
         LocationIndexTree.InMemConstructionIndex inMemIndex = index.getPrepareInMemIndex();
-        assertEquals(Helper.createTList(4, 4), index.getEntries());
+        assertEquals(IntArrayList.from(new int[]{4, 4}), index.getEntries());
 
         assertEquals(4, inMemIndex.getEntriesOf(0).size());
         assertEquals(10, inMemIndex.getEntriesOf(1).size());
@@ -155,7 +156,7 @@ public class LocationIndexTreeTest extends AbstractLocationIndexTester {
         LocationIndexTree index = createIndexNoPrepare(graph, 500);
         index.prepareAlgo();
         LocationIndexTree.InMemConstructionIndex inMemIndex = index.getPrepareInMemIndex();
-        assertEquals(Helper.createTList(4, 4), index.getEntries());
+        assertEquals(IntArrayList.from(new int[]{4, 4}), index.getEntries());
         assertEquals(3, inMemIndex.getEntriesOf(0).size());
         assertEquals(5, inMemIndex.getEntriesOf(1).size());
         assertEquals(0, inMemIndex.getEntriesOf(2).size());
@@ -194,7 +195,7 @@ public class LocationIndexTreeTest extends AbstractLocationIndexTester {
         LocationIndexTree index = createIndexNoPrepare(createTestGraph(encodingManager), 10000);
         index.prepareAlgo();
         LocationIndexTree.InMemConstructionIndex inMemIndex = index.getPrepareInMemIndex();
-        assertEquals(Helper.createTList(64, 4), index.getEntries());
+        assertEquals(IntArrayList.from(new int[]{64, 4}), index.getEntries());
 
         assertEquals(33, inMemIndex.getEntriesOf(0).size());
         assertEquals(69, inMemIndex.getEntriesOf(1).size());
@@ -211,7 +212,7 @@ public class LocationIndexTreeTest extends AbstractLocationIndexTester {
     @Test
     public void testReverseSpatialKey() {
         LocationIndexTree index = createIndex(createTestGraph(encodingManager), 200);
-        assertEquals(Helper.createTList(64, 64, 64, 4), index.getEntries());
+        assertEquals(IntArrayList.from(new int[]{64, 64, 64, 4}), index.getEntries());
 
         // 10111110111110101010
         String str44 = "00000000000000000000000000000000000000000000";
diff --git a/core/src/test/java/com/graphhopper/util/InstructionListTest.java b/core/src/test/java/com/graphhopper/util/InstructionListTest.java
index 922aea4de..605c3c99b 100644
--- a/core/src/test/java/com/graphhopper/util/InstructionListTest.java
+++ b/core/src/test/java/com/graphhopper/util/InstructionListTest.java
@@ -17,6 +17,7 @@
  */
 package com.graphhopper.util;
 
+import com.carrotsearch.hppc.IntArrayList;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.graphhopper.reader.ReaderWay;
@@ -138,7 +139,7 @@ public class InstructionListTest {
 
         p = new Dijkstra(g, new ShortestWeighting(carEncoder), tMode).calcPath(6, 2);
         assertEquals(42000, p.getDistance(), 1e-2);
-        assertEquals(Helper.createTList(6, 7, 8, 5, 2), p.calcNodes());
+        assertEquals(IntArrayList.from(new int[]{6, 7, 8, 5, 2}), p.calcNodes());
 
         wayList = p.calcInstructions(usTR);
         tmpList = pick("text", wayList.createJson());
@@ -315,7 +316,7 @@ public class InstructionListTest {
         assertEquals(15.1, wayList.get(2).getFirstLat(), 1e-3);
         assertEquals(9.9, wayList.get(2).getFirstLon(), 1e-3);
 
-        String gpxStr = wayList.createGPX("test", 0);
+        String gpxStr = wayList.createGPX("test", 0, Constants.VERSION);
         verifyGPX(gpxStr);
 
         assertTrue(gpxStr, gpxStr.contains("<trkpt lat=\"15.0\" lon=\"10.0\"><time>1970-01-01T00:00:00Z</time>"));
@@ -403,7 +404,7 @@ public class InstructionListTest {
         instructions.add(instr);
         instructions.add(new FinishInstruction(52.555619423589, 13.43886994061328, 0));
 
-        String gpxStr = instructions.createGPX("test", 0, true, true, false, false);
+        String gpxStr = instructions.createGPX("test", 0, true, true, false, false, Constants.VERSION);
 
         assertTrue(gpxStr, gpxStr.contains("<gh:exit_number>3</gh:exit_number>"));
         verifyGPX(gpxStr);
@@ -420,7 +421,7 @@ public class InstructionListTest {
         instructions.add(instruction);
         instructions.add(new FinishInstruction(0.000852, 0.000852, 0));
 
-        String gpxStr = instructions.createGPX("test", 0, true, true, true, true);
+        String gpxStr = instructions.createGPX("test", 0, true, true, true, true, Constants.VERSION);
 
         assertFalse(gpxStr, gpxStr.contains("E-"));
         assertTrue(gpxStr, gpxStr.contains("0.000001"));
@@ -439,7 +440,7 @@ public class InstructionListTest {
                 return fakeList;
             }
         };
-        String gpxStr = il.createGPX("test", 0);
+        String gpxStr = il.createGPX("test", 0, Constants.VERSION);
         verifyGPX(gpxStr);
         assertFalse(gpxStr, gpxStr.contains("NaN"));
         assertFalse(gpxStr, gpxStr.contains("<ele>"));
@@ -447,7 +448,7 @@ public class InstructionListTest {
         fakeList.clear();
         fakeList.add(new GPXEntry(12, 13, 11, 0));
         fakeList.add(new GPXEntry(12.5, 13, 10, 1000));
-        gpxStr = il.createGPX("test", 0, true, true, true, true);
+        gpxStr = il.createGPX("test", 0, true, true, true, true, Constants.VERSION);
 
         assertTrue(gpxStr, gpxStr.contains("<ele>11.0</ele>"));
         assertFalse(gpxStr, gpxStr.contains("NaN"));
@@ -480,7 +481,7 @@ public class InstructionListTest {
         assertEquals(19000, result.get(3).getTime());
         assertEquals(22000, result.get(4).getTime());
 
-        verifyGPX(instructions.createGPX());
+        verifyGPX(instructions.createGPX(Constants.VERSION));
     }
 
     private long flagsForSpeed(EncodingManager encodingManager, int speedKmPerHour) {
diff --git a/core/src/test/java/com/graphhopper/util/UnzipperTest.java b/core/src/test/java/com/graphhopper/util/UnzipperTest.java
new file mode 100644
index 000000000..67ec01f58
--- /dev/null
+++ b/core/src/test/java/com/graphhopper/util/UnzipperTest.java
@@ -0,0 +1,23 @@
+package com.graphhopper.util;
+
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.junit.Assert.assertTrue;
+
+public class UnzipperTest {
+
+    @Test
+    public void testUnzip() throws Exception {
+        String to = "./target/tmp/test";
+        Helper.removeDir(new File(to));
+        new Unzipper().unzip("./src/test/resources/com/graphhopper/util/test.zip", to, false);
+        assertTrue(new File("./target/tmp/test/file2 bäh").exists());
+        assertTrue(new File("./target/tmp/test/folder1").isDirectory());
+        assertTrue(new File("./target/tmp/test/folder1/folder 3").isDirectory());
+        Helper.removeDir(new File(to));
+    }
+
+
+}
diff --git a/core/src/test/java/com/graphhopper/util/shapes/CoordTrigTest.java b/core/src/test/java/com/graphhopper/util/shapes/CoordTrigTest.java
deleted file mode 100644
index 98467e463..000000000
--- a/core/src/test/java/com/graphhopper/util/shapes/CoordTrigTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- *  Licensed to GraphHopper GmbH under one or more contributor
- *  license agreements. See the NOTICE file distributed with this work for
- *  additional information regarding copyright ownership.
- *
- *  GraphHopper GmbH licenses this file to you under the Apache License,
- *  Version 2.0 (the "License"); you may not use this file except in
- *  compliance with the License. You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package com.graphhopper.util.shapes;
-
-/**
- * @author Peter Karich
- */
-public class CoordTrigTest {
-
-}
diff --git a/web/src/test/java/com/graphhopper/http/isochrone/IsochroneResourceIT.java b/web/src/test/java/com/graphhopper/http/isochrone/IsochroneResourceIT.java
index 34d909d12..d8d7d930d 100644
--- a/web/src/test/java/com/graphhopper/http/isochrone/IsochroneResourceIT.java
+++ b/web/src/test/java/com/graphhopper/http/isochrone/IsochroneResourceIT.java
@@ -7,6 +7,7 @@ import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
 import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
+import com.graphhopper.util.shapes.Polygon;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
 import org.junit.ClassRule;
diff --git a/web/src/test/java/com/graphhopper/http/isochrone/Polygon.java b/web/src/test/java/com/graphhopper/http/isochrone/Polygon.java
deleted file mode 100644
index 420818bbf..000000000
--- a/web/src/test/java/com/graphhopper/http/isochrone/Polygon.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package com.graphhopper.http.isochrone;
-
-import com.graphhopper.util.shapes.GHPoint;
-
-/**
- * TODO Use JTS
- */
-public class Polygon {
-
-    private final double[] lat;
-    private final double[] lon;
-
-    private double minLat;
-    private double minLon;
-    private double maxLat;
-    private double maxLon;
-
-    private final double epsilon;
-
-    public Polygon(double[] lat, double[] lon) {
-        if (lat.length != lon.length) {
-            throw new IllegalArgumentException("Points must be of equal length but was " + lat.length + " vs. " + lon.length);
-        }
-        if (lat.length == 0) {
-            throw new IllegalArgumentException("Points must not be empty");
-        }
-        this.lat = lat;
-        this.lon = lon;
-
-        for (int i = 0; i < lat.length; i++) {
-            if (i == 0) {
-                minLat = lat[i];
-                maxLat = lat[i];
-                minLon = lon[i];
-                maxLon = lon[i];
-            } else {
-                if (lat[i] < minLat) {
-                    minLat = lat[i];
-                } else if (lat[i] > maxLat) {
-                    maxLat = lat[i];
-                }
-                if (lon[i] < minLon) {
-                    minLon = lon[i];
-                } else if (lon[i] > maxLon) {
-                    maxLon = lon[i];
-                }
-            }
-        }
-
-        epsilon = (maxLat - minLat) / 10;
-    }
-
-    /**
-     * Wrapper method for {@link Polygon#contains(double, double)}.
-     */
-    public boolean contains(GHPoint point) {
-        return contains(point.lat, point.lon);
-    }
-
-    /**
-     * Implements the ray casting algorithm
-     * Code is inspired from here: http://stackoverflow.com/a/218081/1548788
-     *
-     * @param lat Latitude of the point to be checked
-     * @param lon Longitude of the point to be checked
-     * @return true if point is inside polygon
-     */
-    public boolean contains(double lat, double lon) {
-        if (lat < minLat || lat > maxLat || lon < minLon || lon > maxLon) {
-            return false;
-        }
-
-        double rayStartLat = maxLat - (minLat / 2);
-        double rayStartLon = minLon - epsilon;
-
-        boolean inside = false;
-        int len = this.lat.length;
-        for (int i = 0; i < len; i++) {
-            if (edgesAreIntersecting(rayStartLon, rayStartLat, lon, lat, this.lon[i], this.lat[i], this.lon[(i + 1) % len], this.lat[(i + 1) % len]))
-                inside = !inside;
-        }
-        return inside;
-
-    }
-
-    private boolean edgesAreIntersecting(
-            double v1x1, double v1y1, double v1x2, double v1y2,
-            double v2x1, double v2y1, double v2x2, double v2y2
-    ) {
-
-
-        double d1, d2;
-        double a1, a2, b1, b2, c1, c2;
-
-        // Convert vector 1 to a line (line 1) of infinite length.
-        // We want the line in linear equation standard form: A*x + B*y + C = 0
-        // See: http://en.wikipedia.org/wiki/Linear_equation
-        a1 = v1y2 - v1y1;
-        b1 = v1x1 - v1x2;
-        c1 = (v1x2 * v1y1) - (v1x1 * v1y2);
-
-        // Every point (x,y), that solves the equation above, is on the line,
-        // every point that does not solve it, is not. The equation will have a
-        // positive result if it is on one side of the line and a negative one
-        // if is on the other side of it. We insert (x1,y1) and (x2,y2) of vector
-        // 2 into the equation above.
-        d1 = (a1 * v2x1) + (b1 * v2y1) + c1;
-        d2 = (a1 * v2x2) + (b1 * v2y2) + c1;
-
-        // If d1 and d2 both have the same sign, they are both on the same side
-        // of our line 1 and in that case no intersection is possible. Careful,
-        // 0 is a special case, that's why we don't test ">=" and "<=",
-        // but "<" and ">".
-        if (d1 > 0 && d2 > 0) return false;
-        if (d1 < 0 && d2 < 0) return false;
-
-        // The fact that vector 2 intersected the infinite line 1 above doesn't
-        // mean it also intersects the vector 1. Vector 1 is only a subset of that
-        // infinite line 1, so it may have intersected that line before the vector
-        // started or after it ended. To know for sure, we have to repeat the
-        // the same test the other way round. We start by calculating the
-        // infinite line 2 in linear equation standard form.
-        a2 = v2y2 - v2y1;
-        b2 = v2x1 - v2x2;
-        c2 = (v2x2 * v2y1) - (v2x1 * v2y2);
-
-        // Calculate d1 and d2 again, this time using points of vector 1.
-        d1 = (a2 * v1x1) + (b2 * v1y1) + c2;
-        d2 = (a2 * v1x2) + (b2 * v1y2) + c2;
-
-        // Again, if both have the same sign (and neither one is 0),
-        // no intersection is possible.
-        if (d1 > 0 && d2 > 0) return false;
-        if (d1 < 0 && d2 < 0) return false;
-
-        // If we get here, only two possibilities are left. Either the two
-        // vectors intersect in exactly one point or they are collinear, which
-        // means they intersect in any number of points from zero to infinite.
-        if ((a1 * b2) - (a2 * b1) == 0) return false;
-
-        // If they are not collinear, they must intersect in exactly one point.
-        return true;
-    }
-
-    public double getMinLat() {
-        return minLat;
-    }
-
-    public double getMinLon() {
-        return minLon;
-    }
-
-    public double getMaxLat() {
-        return maxLat;
-    }
-
-    public double getMaxLon() {
-        return maxLon;
-    }
-}
\ No newline at end of file
diff --git a/web/src/test/java/com/graphhopper/http/resources/NearestResourceIT.java b/web/src/test/java/com/graphhopper/http/resources/NearestResourceIT.java
index 4a2c8ff43..eb6aa7c26 100644
--- a/web/src/test/java/com/graphhopper/http/resources/NearestResourceIT.java
+++ b/web/src/test/java/com/graphhopper/http/resources/NearestResourceIT.java
@@ -19,7 +19,7 @@ package com.graphhopper.http.resources;
 
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
-import com.graphhopper.http.resources.NearestResource;
+import com.graphhopper.resources.NearestResource;
 import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;