Repository: graphhopper/graphhopper
Pull request #1879 (2020-01-28 21:21:05)

Change GraphHopper.init(CmdArgs)` to `init(GraphHopperConfig)`
The goal of this PR is to change `config.yml` from

```yaml
graphhopper:
  graph.flag_encoders: car
  prepare.ch.weightings: fastest
  prepare.lm.weightings: shortest
```

to

```yaml
graphhopper:
  graph.flag_encoders: car
  prepare.ch.profiles:
    - vehicle: car
      weighting: fastest
  prepare.ch.profiles:
    - vehicle: car
      weighting: shortest
```

To understand why this is useful, see this issue: #493

What makes this complicated is that so far we deserialized `config.yml` into a `Map<String,String>` (a `CmdArgs` object to be more specific). However, here the config options are no longer key-value pairs, but we have two lists of objects (one for CH and one for LM profiles). Therefore we need to deserialize `config.yml` into a custom object (I have called this `GraphHopperConfig` here). Why is this a problem? Because so far we also allowed configuring `GraphHopper` using command line arguments (system properties), which are just key-value pairs. We even merged `config.yml` with the command line arguments (easy to do because both were a list of key-value pairs).

Here I tried to keep the changes to a minimum: I am extracting the two profile lists from the config file and put everything else into a `CmdArgs` object (just as we did before). This way we can no longer use the command line arguments to overwrite the CH/LM profiles in `config.yml`, but all other properties can still be set this way.

One special case where we do the entire configuration via the command line is in `Measurement`, e.g. here:

https://github.com/graphhopper/graphhopper/blob/2e55c309e3f661c2f1306469bc571382ce561ae5/benchmark/benchmark.sh#L38-L55

but we could either use a `config.yml` file here as well and/or somehow implement some command line arguments that allow adding CH/LM profiles via the command line for the most common cases.

**Update**: For now this PR only changes the `CmdArgs` parameter of the `init` method into a `GraphHopperConfig` parameter which simply wraps `CmdArgs`

easbar: Any (better) ideas how to deal with this? @karussell, @michaz? Whats of course a bit ugly here is that we are handling different config options in different ways (ch/lm profiles vs. the others). On the other hand so far I could not come up with a better way to specify the profiles in the key-value format and turning the whole config into a dedicated Java object (instead of using the generic `Map<String,String>`) has its own problems: Merging with command line arguments would be harder (but doable) and the configuration would be less flexible/hard-coded. Maybe this would even be an advantage (easier for validation & we could put all the default values into the same place), but there would have to be some way to extend the (standard) config options (at least as long as `GraphHopper` is meant to be extended)

Anyway this probably leads to far for now, I am just wondering is it acceptable to extract the two profile lists for now (needed for #1776)? 
karussell: Just an idea: to avoid the extra classes around GraphHopperConfig for now is the other (strange) format we already use in other places:

```yml
  prepare.ch.profiles: vehicle=car|weighting=fastest,vehicle=car|weighting=shortest
```

> The goal of this PR is to change config.yml from

(here you mean "to" instead of "from"?)
easbar: > (here you mean "to" instead of "from"?)

Yes -> fixed

> prepare.ch.profiles: vehicle=car|weighting=fastest,vehicle=car|weighting=shortest

Yes we *could* do this as well. It would be a bit longer with edge_based=true/false and u_turn_costs, but it could be done (even though not very pretty). The point where this would not be possible any longer is when we want to have this overwriting mechanism for (parts of) the custom models, as discussed here #493. Then again so far we have not decided if we really want this or not...
With your suggestion (compared to the current master) we could at least do:

```
prepare.ch.profiles: vehicle=my_car|weighting=custom,vehicle=car|weighting=fastest
```

Then again avoiding some pojo classes does not seem to be a very good argument to use an ugly string format like this to me (but at least we could stay with the key-value format in the config for now).
michaz: We absolutely need Dropwizard-style hierarchical configuration, sooner or later. I totally support starting the migration work for this case (lists of profiles).
easbar: Ok, then we'll do this. Some questions related to this:

* where should we put the config objects? currently we put jackson stuff into `graphhopper-api` (e.g. `CmdArgsDeserializer`). So we would have to put the configuration classes also into `graphhopper-api` (like `GraphHopperConfig` and `CHProfileConfig` here). In this case we do not have access to core, for example we cannot use constants like `Weighting.INFINITE_U_TURN_COSTS` in `GraphHopperConfig`. So far I do not think this is too much of an issue and can be worked around, but at the same time I do not fully understand why we are trying to keep the jackson dependency out of the core?

* What about default values? I am wondering should we keep default values for everything within the config objects such that we could do `GraphHopper.init(new GraphHopperConfig())` to start with the default configuration? The defaults would probably be 'duplicated' in `config-example.yml`, but how else would it be possible to use defaults when using the `config.yml` configuration *and* the Java API. Putting default values into the config objects is better than putting them inside `GraphHopper` and related classes. Here I am thinking about the direction we are going not really what to do right now, unless we decide to do this switch all at once instead of starting step by step like in this PR. In this PR for example the question would be if `GraphHopperConfig#chProfiles` should be filled with a single default profile by default.
karussell: > I do not fully understand why we are trying to keep the jackson dependency out of the core?

Because of Android we want to keep the dependencies down to a minimum (apps should be small) and also often there they use GSON for deserialization. On the other hand I'm not sure if "core" really should do (de)serialization or if it should only operate on POJOs or models. This requirement has led to some ugly workarounds but keeping only the necessary inside core seems like a good constraint (no server, no json).

> The defaults would probably be 'duplicated' in config-example.yml
> ..
> GraphHopperConfig#chProfiles should be filled with a single default profile by default.

Yes, either duplication or can think about changing the default to "no CH profiles" on the Java side and provide an example config with a preconfigured car profile. The only major work could be to change all the unit/integration tests then
devemux86: > Because of Android we want to keep the dependencies down to a minimum (apps should be small) and also often there they use GSON for deserialization.

Indeed and even JTS was not needed for the regular routing until recently(?). :slightly_smiling_face: 
easbar: > Because of Android we want to keep the dependencies down to a minimum
Ok

> Yes, either duplication or can think about changing the default to "no CH profiles" on the Java side and provide an example config with a preconfigured car profile. 

Yes better no CH profiles by default, because `GraphHopper` can also be configured without calling `init` and then its ugly because there is no `EncodingManager` initially (we discussed this).
Still I think when have more fields in `GraphHopperConfig` this would be a good place to put default values (for other fields that actually have a more interesting default value than 'empty list')
michaz: > Indeed and even JTS was not needed for the regular routing until recently(?).

Yeah. On the other hand, not using libraries has costs, too. Even modularizing has costs. If we really want to keep JTS out of core, but _also_ don't want to own custom implementations of geometry operations, we would need to provide a core that doesn't do geometry operations. I.e. factor spatial rules and such out of the core.

It's all possible, but costly.

It's a trade-off.
michaz: Acutally, I like that idea. Does the graph import happen on a desktop machine, @devemux86 ? Could it?
devemux86: > Does the graph import happen on a desktop machine, @devemux86?

On desktop (e.g. via desktop [Cruiser](https://wiki.openstreetmap.org/wiki/Cruiser), as users prefer UI than scripts :slightly_smiling_face:).

Though in GH forum there were cases for imports on mobile phones too?
devemux86: > Even modularizing has costs.

I understand that, as I maintain already multiple modules in [VTM](https://github.com/mapsforge/vtm) to avoid loading core map module with many dependencies. :)

> we could move everything import-related, and probably the geometry processing, into `web`

It would be interesting if import could exist separately and then readers (android, desktop, web, ...) use the graphs on each platform.
michaz: > On desktop

Good to know, thanks! 🤔
devemux86: > Does the graph import happen on a desktop machine, @devemux86?

To clarify, I mean the graph creation happens on desktop, because of the high cpu / ram specifications.

The graph reading can happen anywhere, even on mobile phones with the much limited heap memory.
michaz: Yep, I see!
michaz: Oops, hijacked the ticket.
michaz: I think my problem so far (you remember I struggled with finding the right split of modules previously..) was that ``core`` wanted to be "everything except the web server and the data readers".

If it could be different, and ``core`` would be "the minimal set of things you need, as a Java programmer, to solve routing problems on a pre-generated graph", I would know much better how to move...
easbar: I removed the LM/CH profile lists and for now this PR only wraps the `CmdArgs` we pass into `GraphHopper.init()` with a new `GraphHopperConfig` class object. So nothing changes really, but we will be able to use a hierarchical GH config file this way, by adjusting the `GraphHopperConfigDeserializer`. 

easbar: @michaz can you have a quick look regarding the Dropwizard and Gtfs stuff especially?

Patch:
diff --git a/api/src/main/java/com/graphhopper/GraphHopperConfig.java b/api/src/main/java/com/graphhopper/GraphHopperConfig.java
new file mode 100644
index 000000000..fecfe700a
--- /dev/null
+++ b/api/src/main/java/com/graphhopper/GraphHopperConfig.java
@@ -0,0 +1,89 @@
+/*
+ *  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;
+
+import com.graphhopper.util.PMap;
+
+import java.util.Map;
+
+/**
+ * This class represents the global configuration for the GraphHopper class, which is typically configured via the
+ * `config.yml` file. So far we are mapping the key-value pairs in the config file to a string-string map, but soon
+ * we will start adding hierarchical configurations (lists, nested objects etc.). We will also start adding the
+ * different configuration options as fields of this class including the default values.
+ */
+public class GraphHopperConfig {
+    private final PMap map;
+
+    public GraphHopperConfig() {
+        this(new PMap());
+    }
+
+    public GraphHopperConfig(PMap pMap) {
+        this.map = pMap;
+    }
+
+    public GraphHopperConfig put(String key, Object value) {
+        map.put(key, value);
+        return this;
+    }
+
+    public boolean has(String key) {
+        return map.has(key);
+    }
+
+    public boolean getBool(String key, boolean _default) {
+        return map.getBool(key, _default);
+    }
+
+    public int getInt(String key, int _default) {
+        return map.getInt(key, _default);
+    }
+
+    public long getLong(String key, long _default) {
+        return map.getLong(key, _default);
+    }
+
+    public float getFloat(String key, float _default) {
+        return map.getFloat(key, _default);
+    }
+
+    public double getDouble(String key, double _default) {
+        return map.getDouble(key, _default);
+    }
+
+    public String get(String key, String _default) {
+        return map.get(key, _default);
+    }
+
+    public PMap asPMap() {
+        return map;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("properties:\n");
+        for (Map.Entry<String, String> entry : map.toMap().entrySet()) {
+            sb.append(entry.getKey()).append(": ").append(entry.getValue());
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+}
diff --git a/api/src/main/java/com/graphhopper/util/CmdArgs.java b/api/src/main/java/com/graphhopper/util/CmdArgs.java
deleted file mode 100644
index ebebfdecd..000000000
--- a/api/src/main/java/com/graphhopper/util/CmdArgs.java
+++ /dev/null
@@ -1,89 +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 java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import static com.graphhopper.util.Helper.toLowerCase;
-
-/**
- * Stores command line options in a map. The capitalization of the key is ignored.
- * <p>
- *
- * @author Peter Karich
- */
-public class CmdArgs extends PMap {
-
-    public CmdArgs() {
-    }
-
-    public CmdArgs(Map<String, String> map) {
-        super(map);
-    }
-
-    /**
-     * This method creates a CmdArgs object from the specified string array (a list of key=value pairs).
-     */
-    public static CmdArgs read(String[] args) {
-        Map<String, String> map = new LinkedHashMap<>();
-        for (String arg : args) {
-            int index = arg.indexOf("=");
-            if (index <= 0) {
-                continue;
-            }
-
-            String key = arg.substring(0, index);
-            if (key.startsWith("-")) {
-                key = key.substring(1);
-            }
-
-            if (key.startsWith("-")) {
-                key = key.substring(1);
-            }
-
-            String value = arg.substring(index + 1);
-            String old = map.put(toLowerCase(key), value);
-            if (old != null)
-                throw new IllegalArgumentException("Pair '" + toLowerCase(key) + "'='" + value + "' not possible to " +
-                        "add to the CmdArgs-object as the key already exists with '" + old + "'");
-        }
-
-        return new CmdArgs(map);
-    }
-
-    public static CmdArgs readFromSystemProperties() {
-        CmdArgs cmdArgs = new CmdArgs();
-        for (Entry<Object, Object> e : System.getProperties().entrySet()) {
-            String k = ((String) e.getKey());
-            String v = ((String) e.getValue());
-            if (k.startsWith("graphhopper.")) {
-                k = k.substring("graphhopper.".length());
-                cmdArgs.put(k, v);
-            }
-        }
-        return cmdArgs;
-    }
-
-    @Override
-    public CmdArgs put(String key, Object str) {
-        super.put(key, str);
-        return this;
-    }
-}
diff --git a/api/src/main/java/com/graphhopper/util/PMap.java b/api/src/main/java/com/graphhopper/util/PMap.java
index c40ce658c..00178d6a7 100644
--- a/api/src/main/java/com/graphhopper/util/PMap.java
+++ b/api/src/main/java/com/graphhopper/util/PMap.java
@@ -18,8 +18,11 @@
 package com.graphhopper.util;
 
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 
+import static com.graphhopper.util.Helper.toLowerCase;
+
 /**
  * A properties map (String to String) with convenient accessors
  * <p>
@@ -59,6 +62,35 @@ public class PMap {
         }
     }
 
+    /**
+     * Reads a PMap from a string array consisting of key=value pairs
+     */
+    public static PMap read(String[] args) {
+        Map<String, String> map = new LinkedHashMap<>();
+        for (String arg : args) {
+            int index = arg.indexOf("=");
+            if (index <= 0) {
+                continue;
+            }
+
+            String key = arg.substring(0, index);
+            if (key.startsWith("-")) {
+                key = key.substring(1);
+            }
+
+            if (key.startsWith("-")) {
+                key = key.substring(1);
+            }
+
+            String value = arg.substring(index + 1);
+            String old = map.put(toLowerCase(key), value);
+            if (old != null)
+                throw new IllegalArgumentException("Pair '" + toLowerCase(key) + "'='" + value + "' not possible to " +
+                        "add to the PMap-object as the key already exists with '" + old + "'");
+        }
+        return new PMap(map);
+    }
+
     public PMap put(PMap map) {
         this.map.putAll(map.map);
         return this;
diff --git a/core/files/changelog.txt b/core/files/changelog.txt
index 50fa9cfb7..2d1f4e754 100644
--- a/core/files/changelog.txt
+++ b/core/files/changelog.txt
@@ -1,4 +1,5 @@
 1.0
+    GraphHopper.init(CmdArgs)->GraphHopper.init(GraphHopperConfig), see #1879
     revert compression of landmark preparation data, see #1749 and #1376
     removed GenericWeighting and DataFlagEncoder as a normal CarFlagEncoder does the job too
     add required EncodedValues like road_class to EncodingManager if not added from user
diff --git a/core/src/main/java/com/graphhopper/GraphHopper.java b/core/src/main/java/com/graphhopper/GraphHopper.java
index f60cc21de..636a0c9c1 100644
--- a/core/src/main/java/com/graphhopper/GraphHopper.java
+++ b/core/src/main/java/com/graphhopper/GraphHopper.java
@@ -487,19 +487,18 @@ public class GraphHopper implements GraphHopperAPI {
     }
 
     /**
-     * Reads the configuration from a CmdArgs object which can be manually filled, or via
-     * CmdArgs.read(String[] args)
+     * Reads the configuration from a {@link GraphHopperConfig} object which can be manually filled, or more typically
+     * is read from `config.yml`.
      */
-    public GraphHopper init(CmdArgs args) {
-        args.merge(CmdArgs.readFromSystemProperties());
-        if (args.has("osmreader.osm"))
+    public GraphHopper init(GraphHopperConfig ghConfig) {
+        if (ghConfig.has("osmreader.osm"))
             throw new IllegalArgumentException("Instead osmreader.osm use datareader.file, for other changes see core/files/changelog.txt");
 
-        String tmpOsmFile = args.get("datareader.file", "");
+        String tmpOsmFile = ghConfig.get("datareader.file", "");
         if (!isEmpty(tmpOsmFile))
             dataReaderFile = tmpOsmFile;
 
-        String graphHopperFolder = args.get("graph.location", "");
+        String graphHopperFolder = ghConfig.get("graph.location", "");
         if (isEmpty(graphHopperFolder) && isEmpty(ghLocation)) {
             if (isEmpty(dataReaderFile))
                 throw new IllegalArgumentException("If no graph.location is provided you need to specify an OSM file.");
@@ -509,55 +508,55 @@ public class GraphHopper implements GraphHopperAPI {
 
         // graph
         setGraphHopperLocation(graphHopperFolder);
-        defaultSegmentSize = args.getInt("graph.dataaccess.segment_size", defaultSegmentSize);
+        defaultSegmentSize = ghConfig.getInt("graph.dataaccess.segment_size", defaultSegmentSize);
 
-        String graphDATypeStr = args.get("graph.dataaccess", "RAM_STORE");
+        String graphDATypeStr = ghConfig.get("graph.dataaccess", "RAM_STORE");
         dataAccessType = DAType.fromString(graphDATypeStr);
 
-        sortGraph = args.getBool("graph.do_sort", sortGraph);
-        removeZipped = args.getBool("graph.remove_zipped", removeZipped);
+        sortGraph = ghConfig.getBool("graph.do_sort", sortGraph);
+        removeZipped = ghConfig.getBool("graph.remove_zipped", removeZipped);
         EncodingManager.Builder emBuilder = new EncodingManager.Builder();
-        String flagEncodersStr = args.get("graph.flag_encoders", "");
-        String encodedValueStr = args.get("graph.encoded_values", "");
+        String flagEncodersStr = ghConfig.get("graph.flag_encoders", "");
+        String encodedValueStr = ghConfig.get("graph.encoded_values", "");
         if (!flagEncodersStr.isEmpty() || !encodedValueStr.isEmpty()) {
             if (!encodedValueStr.isEmpty())
                 emBuilder.addAll(tagParserFactory, encodedValueStr);
             registerCustomEncodedValues(emBuilder);
             if (!flagEncodersStr.isEmpty())
                 emBuilder.addAll(flagEncoderFactory, flagEncodersStr);
-            emBuilder.setEnableInstructions(args.getBool("datareader.instructions", true));
-            emBuilder.setPreferredLanguage(args.get("datareader.preferred_language", ""));
-            emBuilder.setDateRangeParser(DateRangeParser.createInstance(args.get("datareader.date_range_parser_day", "")));
+            emBuilder.setEnableInstructions(ghConfig.getBool("datareader.instructions", true));
+            emBuilder.setPreferredLanguage(ghConfig.get("datareader.preferred_language", ""));
+            emBuilder.setDateRangeParser(DateRangeParser.createInstance(ghConfig.get("datareader.date_range_parser_day", "")));
             // overwrite EncodingManager object from configuration file
             setEncodingManager(emBuilder.build());
         }
 
-        if (args.get("graph.locktype", "native").equals("simple"))
+        if (ghConfig.get("graph.locktype", "native").equals("simple"))
             lockFactory = new SimpleFSLockFactory();
         else
             lockFactory = new NativeFSLockFactory();
 
         // elevation
-        String eleProviderStr = toLowerCase(args.get("graph.elevation.provider", "noop"));
-        this.smoothElevation = args.getBool("graph.elevation.smoothing", false);
+        String eleProviderStr = toLowerCase(ghConfig.get("graph.elevation.provider", "noop"));
+        this.smoothElevation = ghConfig.getBool("graph.elevation.smoothing", false);
 
         // keep fallback until 0.8
-        boolean eleCalcMean = args.has("graph.elevation.calcmean")
-                ? args.getBool("graph.elevation.calcmean", false)
-                : args.getBool("graph.elevation.calc_mean", false);
+        boolean eleCalcMean = ghConfig.has("graph.elevation.calcmean")
+                ? ghConfig.getBool("graph.elevation.calcmean", false)
+                : ghConfig.getBool("graph.elevation.calc_mean", false);
 
-        String cacheDirStr = args.get("graph.elevation.cache_dir", "");
+        String cacheDirStr = ghConfig.get("graph.elevation.cache_dir", "");
         if (cacheDirStr.isEmpty())
-            cacheDirStr = args.get("graph.elevation.cachedir", "");
+            cacheDirStr = ghConfig.get("graph.elevation.cachedir", "");
 
-        String baseURL = args.get("graph.elevation.base_url", "");
+        String baseURL = ghConfig.get("graph.elevation.base_url", "");
         if (baseURL.isEmpty())
-            args.get("graph.elevation.baseurl", "");
+            ghConfig.get("graph.elevation.baseurl", "");
 
-        boolean removeTempElevationFiles = args.getBool("graph.elevation.cgiar.clear", true);
-        removeTempElevationFiles = args.getBool("graph.elevation.clear", removeTempElevationFiles);
+        boolean removeTempElevationFiles = ghConfig.getBool("graph.elevation.cgiar.clear", true);
+        removeTempElevationFiles = ghConfig.getBool("graph.elevation.clear", removeTempElevationFiles);
 
-        DAType elevationDAType = DAType.fromString(args.get("graph.elevation.dataaccess", "MMAP"));
+        DAType elevationDAType = DAType.fromString(ghConfig.get("graph.elevation.dataaccess", "MMAP"));
         ElevationProvider tmpProvider = ElevationProvider.NOOP;
         if (eleProviderStr.equalsIgnoreCase("srtm")) {
             tmpProvider = new SRTMProvider(cacheDirStr);
@@ -579,27 +578,27 @@ public class GraphHopper implements GraphHopperAPI {
         setElevationProvider(tmpProvider);
 
         // optimizable prepare
-        minNetworkSize = args.getInt("prepare.min_network_size", minNetworkSize);
-        minOneWayNetworkSize = args.getInt("prepare.min_one_way_network_size", minOneWayNetworkSize);
+        minNetworkSize = ghConfig.getInt("prepare.min_network_size", minNetworkSize);
+        minOneWayNetworkSize = ghConfig.getInt("prepare.min_one_way_network_size", minOneWayNetworkSize);
 
         // prepare CH, LM, ...
         for (RoutingAlgorithmFactoryDecorator decorator : algoDecorators) {
-            decorator.init(args);
+            decorator.init(ghConfig);
         }
 
         // osm import
-        dataReaderWayPointMaxDistance = args.getDouble(Routing.INIT_WAY_POINT_MAX_DISTANCE, dataReaderWayPointMaxDistance);
+        dataReaderWayPointMaxDistance = ghConfig.getDouble(Routing.INIT_WAY_POINT_MAX_DISTANCE, dataReaderWayPointMaxDistance);
 
-        dataReaderWorkerThreads = args.getInt("datareader.worker_threads", dataReaderWorkerThreads);
+        dataReaderWorkerThreads = ghConfig.getInt("datareader.worker_threads", dataReaderWorkerThreads);
 
         // index
-        preciseIndexResolution = args.getInt("index.high_resolution", preciseIndexResolution);
-        maxRegionSearch = args.getInt("index.max_region_search", maxRegionSearch);
+        preciseIndexResolution = ghConfig.getInt("index.high_resolution", preciseIndexResolution);
+        maxRegionSearch = ghConfig.getInt("index.max_region_search", maxRegionSearch);
 
         // routing
-        maxVisitedNodes = args.getInt(Routing.INIT_MAX_VISITED_NODES, Integer.MAX_VALUE);
-        maxRoundTripRetries = args.getInt(RoundTrip.INIT_MAX_RETRIES, maxRoundTripRetries);
-        nonChMaxWaypointDistance = args.getInt(Parameters.NON_CH.MAX_NON_CH_POINT_DISTANCE, Integer.MAX_VALUE);
+        maxVisitedNodes = ghConfig.getInt(Routing.INIT_MAX_VISITED_NODES, Integer.MAX_VALUE);
+        maxRoundTripRetries = ghConfig.getInt(RoundTrip.INIT_MAX_RETRIES, maxRoundTripRetries);
+        nonChMaxWaypointDistance = ghConfig.getInt(Parameters.NON_CH.MAX_NON_CH_POINT_DISTANCE, Integer.MAX_VALUE);
 
         return this;
     }
diff --git a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactoryDecorator.java
index 36f9cbfe6..6d12026c0 100644
--- a/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/RoutingAlgorithmFactoryDecorator.java
@@ -17,14 +17,14 @@
  */
 package com.graphhopper.routing;
 
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.routing.util.HintsMap;
-import com.graphhopper.util.CmdArgs;
 
 /**
  * @author Peter Karich
  */
 public interface RoutingAlgorithmFactoryDecorator {
-    void init(CmdArgs args);
+    void init(GraphHopperConfig ghConfig);
 
     boolean isEnabled();
 
diff --git a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
index 1b07588d9..4ee7984a1 100644
--- a/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/ch/CHAlgoFactoryDecorator.java
@@ -17,13 +17,13 @@
  */
 package com.graphhopper.routing.ch;
 
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.routing.RoutingAlgorithmFactory;
 import com.graphhopper.routing.RoutingAlgorithmFactoryDecorator;
 import com.graphhopper.routing.util.HintsMap;
 import com.graphhopper.storage.CHProfile;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.StorableProperties;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.PMap;
 import com.graphhopper.util.Parameters.CH;
 import org.slf4j.Logger;
@@ -65,17 +65,17 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
     }
 
     @Override
-    public void init(CmdArgs args) {
+    public void init(GraphHopperConfig ghConfig) {
         // throw explicit error for deprecated configs
-        if (!args.get("prepare.threads", "").isEmpty())
+        if (!ghConfig.get("prepare.threads", "").isEmpty())
             throw new IllegalStateException("Use " + CH.PREPARE + "threads instead of prepare.threads");
-        if (!args.get("prepare.chWeighting", "").isEmpty() || !args.get("prepare.chWeightings", "").isEmpty())
+        if (!ghConfig.get("prepare.chWeighting", "").isEmpty() || !ghConfig.get("prepare.chWeightings", "").isEmpty())
             throw new IllegalStateException("Use " + CH.PREPARE + "weightings and a comma separated list instead of prepare.chWeighting or prepare.chWeightings");
 
-        setPreparationThreads(args.getInt(CH.PREPARE + "threads", getPreparationThreads()));
+        setPreparationThreads(ghConfig.getInt(CH.PREPARE + "threads", getPreparationThreads()));
 
         // default is enabled & fastest
-        String chWeightingsStr = args.get(CH.PREPARE + "weightings", "");
+        String chWeightingsStr = ghConfig.get(CH.PREPARE + "weightings", "");
         if (chWeightingsStr.contains("edge_based")) {
             throw new IllegalArgumentException("Adding 'edge_based` to " + (CH.PREPARE + "weightings") + " is not allowed, to enable edge-based CH use " + (CH.PREPARE + "edge_based") + " instead.");
         }
@@ -90,13 +90,13 @@ public class CHAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         boolean enableThis = !chProfileStrings.isEmpty();
         setEnabled(enableThis);
         if (enableThis)
-            setDisablingAllowed(args.getBool(CH.INIT_DISABLING_ALLOWED, isDisablingAllowed()));
+            setDisablingAllowed(ghConfig.getBool(CH.INIT_DISABLING_ALLOWED, isDisablingAllowed()));
 
-        String edgeBasedCHStr = args.get(CH.PREPARE + "edge_based", "off").trim();
+        String edgeBasedCHStr = ghConfig.get(CH.PREPARE + "edge_based", "off").trim();
         edgeBasedCHStr = edgeBasedCHStr.equals("false") ? "off" : edgeBasedCHStr;
         edgeBasedCHMode = EdgeBasedCHMode.valueOf(edgeBasedCHStr.toUpperCase(Locale.ROOT));
 
-        pMap = args;
+        pMap = ghConfig.asPMap();
     }
 
     @Override
diff --git a/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java b/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
index 06ea512e8..47858db3e 100644
--- a/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
+++ b/core/src/main/java/com/graphhopper/routing/lm/LMAlgoFactoryDecorator.java
@@ -19,6 +19,7 @@ package com.graphhopper.routing.lm;
 
 import com.graphhopper.GHRequest;
 import com.graphhopper.GHResponse;
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.routing.AlgorithmOptions;
 import com.graphhopper.routing.RoutingAlgorithm;
 import com.graphhopper.routing.RoutingAlgorithmFactory;
@@ -30,7 +31,6 @@ import com.graphhopper.storage.Graph;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.StorableProperties;
 import com.graphhopper.storage.index.LocationIndex;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.PMap;
 import com.graphhopper.util.Parameters;
 import com.graphhopper.util.Parameters.Landmark;
@@ -74,19 +74,19 @@ public class LMAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
     }
 
     @Override
-    public void init(CmdArgs args) {
-        setPreparationThreads(args.getInt(Parameters.Landmark.PREPARE + "threads", getPreparationThreads()));
+    public void init(GraphHopperConfig ghConfig) {
+        setPreparationThreads(ghConfig.getInt(Parameters.Landmark.PREPARE + "threads", getPreparationThreads()));
 
-        landmarkCount = args.getInt(Parameters.Landmark.COUNT, landmarkCount);
-        activeLandmarkCount = args.getInt(Landmark.ACTIVE_COUNT_DEFAULT, Math.min(8, landmarkCount));
-        logDetails = args.getBool(Landmark.PREPARE + "log_details", false);
-        minNodes = args.getInt(Landmark.PREPARE + "min_network_size", -1);
+        landmarkCount = ghConfig.getInt(Parameters.Landmark.COUNT, landmarkCount);
+        activeLandmarkCount = ghConfig.getInt(Landmark.ACTIVE_COUNT_DEFAULT, Math.min(8, landmarkCount));
+        logDetails = ghConfig.getBool(Landmark.PREPARE + "log_details", false);
+        minNodes = ghConfig.getInt(Landmark.PREPARE + "min_network_size", -1);
 
-        for (String loc : args.get(Landmark.PREPARE + "suggestions_location", "").split(",")) {
+        for (String loc : ghConfig.get(Landmark.PREPARE + "suggestions_location", "").split(",")) {
             if (!loc.trim().isEmpty())
                 lmSuggestionsLocations.add(loc.trim());
         }
-        String lmWeightingsStr = args.get(Landmark.PREPARE + "weightings", "");
+        String lmWeightingsStr = ghConfig.get(Landmark.PREPARE + "weightings", "");
         if (!lmWeightingsStr.isEmpty() && !lmWeightingsStr.equalsIgnoreCase("no") && !lmWeightingsStr.equalsIgnoreCase("false")) {
             List<String> tmpLMWeightingList = Arrays.asList(lmWeightingsStr.split(","));
             setWeightingsAsStrings(tmpLMWeightingList);
@@ -95,7 +95,7 @@ public class LMAlgoFactoryDecorator implements RoutingAlgorithmFactoryDecorator
         boolean enableThis = !weightingsAsStrings.isEmpty();
         setEnabled(enableThis);
         if (enableThis)
-            setDisablingAllowed(args.getBool(Landmark.INIT_DISABLING_ALLOWED, isDisablingAllowed()));
+            setDisablingAllowed(ghConfig.getBool(Landmark.INIT_DISABLING_ALLOWED, isDisablingAllowed()));
     }
 
     public int getLandmarks() {
diff --git a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphHopperGtfs.java b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphHopperGtfs.java
index aca5bb3b5..16604ee1f 100644
--- a/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphHopperGtfs.java
+++ b/reader-gtfs/src/main/java/com/graphhopper/reader/gtfs/GraphHopperGtfs.java
@@ -20,6 +20,7 @@ package com.graphhopper.reader.gtfs;
 
 import com.conveyal.gtfs.GTFSFeed;
 import com.conveyal.gtfs.model.Transfer;
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.reader.DataReader;
 import com.graphhopper.reader.dem.ElevationProvider;
 import com.graphhopper.reader.osm.GraphHopperOSM;
@@ -32,7 +33,6 @@ import com.graphhopper.storage.RAMDirectory;
 import com.graphhopper.storage.index.LocationIndex;
 import com.graphhopper.storage.index.LocationIndexTree;
 import com.graphhopper.storage.index.QueryResult;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.PointList;
 
 import java.io.File;
@@ -44,11 +44,11 @@ import java.util.zip.ZipFile;
 
 public class GraphHopperGtfs extends GraphHopperOSM {
 
-    private final CmdArgs cmdArgs;
+    private final GraphHopperConfig ghConfig;
     private GtfsStorage gtfsStorage;
 
-    public GraphHopperGtfs(CmdArgs cmdArgs) {
-        this.cmdArgs = cmdArgs;
+    public GraphHopperGtfs(GraphHopperConfig ghConfig) {
+        this.ghConfig = ghConfig;
     }
 
     @Override
@@ -58,7 +58,7 @@ public class GraphHopperGtfs extends GraphHopperOSM {
 
     @Override
     protected DataReader importData() throws IOException {
-        if (cmdArgs.has("datareader.file")) {
+        if (ghConfig.has("datareader.file")) {
             return super.importData();
         } else {
             getGraphHopperStorage().create(1000);
@@ -123,7 +123,7 @@ public class GraphHopperGtfs extends GraphHopperOSM {
             getGtfsStorage().create();
             GraphHopperStorage graphHopperStorage = getGraphHopperStorage();
             int idx = 0;
-            List<String> gtfsFiles = cmdArgs.has("gtfs.file") ? Arrays.asList(cmdArgs.get("gtfs.file", "").split(",")) : Collections.emptyList();
+            List<String> gtfsFiles = ghConfig.has("gtfs.file") ? Arrays.asList(ghConfig.get("gtfs.file", "").split(",")) : Collections.emptyList();
             for (String gtfsFile : gtfsFiles) {
                 try {
                     getGtfsStorage().loadGtfsFromZipFile("gtfs_" + idx++, new ZipFile(gtfsFile));
diff --git a/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java b/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java
index 4b1e35d7d..ef66ec995 100644
--- a/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java
+++ b/tools/src/main/java/com/graphhopper/tools/CHMeasurement.java
@@ -20,6 +20,7 @@ package com.graphhopper.tools;
 import com.graphhopper.GHRequest;
 import com.graphhopper.GHResponse;
 import com.graphhopper.GraphHopper;
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.reader.osm.GraphHopperOSM;
 import com.graphhopper.routing.ch.CHAlgoFactoryDecorator;
 import com.graphhopper.routing.lm.LMAlgoFactoryDecorator;
@@ -57,71 +58,72 @@ public class CHMeasurement {
         // example args:
         // map=berlin.pbf stats_file=stats.dat period_updates=0 lazy_updates=100 neighbor_updates=0 contract_nodes=100 log_messages=20 edge_quotient_weight=1.0 orig_edge_quotient_weight=3.0 hierarchy_depth_weight=2.0 sigma_factor=3.0 min_max_settled_edges=100 reset_interval=10000 landmarks=0 cleanup=true turncosts=true threshold=0.1 seed=456 comp_iterations=10 perf_iterations=100 quick=false
         long start = nanoTime();
-        CmdArgs cmdArgs = CmdArgs.read(args);
-        LOGGER.info("Running analysis with parameters {}", cmdArgs);
-        String osmFile = cmdArgs.get("map", "local/maps/unterfranken-latest.osm.pbf");
-        cmdArgs.put("datareader.file", osmFile);
-        final String statsFile = cmdArgs.get("stats_file", null);
-        final int periodicUpdates = cmdArgs.getInt("period_updates", 0);
-        final int lazyUpdates = cmdArgs.getInt("lazy_updates", 100);
-        final int neighborUpdates = cmdArgs.getInt("neighbor_updates", 0);
-        final int contractedNodes = cmdArgs.getInt("contract_nodes", 100);
-        final int logMessages = cmdArgs.getInt("log_messages", 20);
-        final float edgeQuotientWeight = cmdArgs.getFloat("edge_quotient_weight", 1.0f);
-        final float origEdgeQuotientWeight = cmdArgs.getFloat("orig_edge_quotient_weight", 3.0f);
-        final float hierarchyDepthWeight = cmdArgs.getFloat("hierarchy_depth_weight", 2.0f);
-        final double sigmaFactor = cmdArgs.getFloat("sigma_factor", 3.0f);
-        final int minMaxSettledEdges = cmdArgs.getInt("min_max_settled_edges", 100);
-        final int resetInterval = cmdArgs.getInt("reset_interval", 10_000);
-        final int landmarks = cmdArgs.getInt("landmarks", 0);
-        final boolean cleanup = cmdArgs.getBool("cleanup", true);
-        final boolean withTurnCosts = cmdArgs.getBool("turncosts", true);
-        final int uTurnCosts = cmdArgs.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS);
-        final double errorThreshold = cmdArgs.getDouble("threshold", 0.1);
-        final long seed = cmdArgs.getLong("seed", 456);
-        final int compIterations = cmdArgs.getInt("comp_iterations", 100);
-        final int perfIterations = cmdArgs.getInt("perf_iterations", 1000);
-        final boolean quick = cmdArgs.getBool("quick", false);
+        PMap map = PMap.read(args);
+        GraphHopperConfig ghConfig = new GraphHopperConfig(map);
+        LOGGER.info("Running analysis with parameters {}", ghConfig);
+        String osmFile = ghConfig.get("map", "local/maps/unterfranken-latest.osm.pbf");
+        ghConfig.put("datareader.file", osmFile);
+        final String statsFile = ghConfig.get("stats_file", null);
+        final int periodicUpdates = ghConfig.getInt("period_updates", 0);
+        final int lazyUpdates = ghConfig.getInt("lazy_updates", 100);
+        final int neighborUpdates = ghConfig.getInt("neighbor_updates", 0);
+        final int contractedNodes = ghConfig.getInt("contract_nodes", 100);
+        final int logMessages = ghConfig.getInt("log_messages", 20);
+        final float edgeQuotientWeight = ghConfig.getFloat("edge_quotient_weight", 1.0f);
+        final float origEdgeQuotientWeight = ghConfig.getFloat("orig_edge_quotient_weight", 3.0f);
+        final float hierarchyDepthWeight = ghConfig.getFloat("hierarchy_depth_weight", 2.0f);
+        final double sigmaFactor = ghConfig.getFloat("sigma_factor", 3.0f);
+        final int minMaxSettledEdges = ghConfig.getInt("min_max_settled_edges", 100);
+        final int resetInterval = ghConfig.getInt("reset_interval", 10_000);
+        final int landmarks = ghConfig.getInt("landmarks", 0);
+        final boolean cleanup = ghConfig.getBool("cleanup", true);
+        final boolean withTurnCosts = ghConfig.getBool("turncosts", true);
+        final int uTurnCosts = ghConfig.getInt(Parameters.Routing.U_TURN_COSTS, INFINITE_U_TURN_COSTS);
+        final double errorThreshold = ghConfig.getDouble("threshold", 0.1);
+        final long seed = ghConfig.getLong("seed", 456);
+        final int compIterations = ghConfig.getInt("comp_iterations", 100);
+        final int perfIterations = ghConfig.getInt("perf_iterations", 1000);
+        final boolean quick = ghConfig.getBool("quick", false);
 
         final GraphHopper graphHopper = new GraphHopperOSM();
         if (withTurnCosts) {
-            cmdArgs.put("graph.flag_encoders", "car|turn_costs=true");
-            cmdArgs.put("prepare.ch.weightings", "fastest");
-            cmdArgs.put("prepare.ch.edge_based", "edge_or_node");
+            ghConfig.put("graph.flag_encoders", "car|turn_costs=true");
+            ghConfig.put("prepare.ch.weightings", "fastest");
+            ghConfig.put("prepare.ch.edge_based", "edge_or_node");
             if (landmarks > 0) {
-                cmdArgs.put("prepare.lm.weightings", "fastest");
-                cmdArgs.put("prepare.lm.landmarks", landmarks);
+                ghConfig.put("prepare.lm.weightings", "fastest");
+                ghConfig.put("prepare.lm.landmarks", landmarks);
             }
         } else {
-            cmdArgs.put("graph.flag_encoders", "car");
-            cmdArgs.put("prepare.ch.weightings", "no");
+            ghConfig.put("graph.flag_encoders", "car");
+            ghConfig.put("prepare.ch.weightings", "no");
         }
         CHAlgoFactoryDecorator chDecorator = graphHopper.getCHFactoryDecorator();
         chDecorator.setDisablingAllowed(true);
-        cmdArgs.put(PERIODIC_UPDATES, periodicUpdates);
-        cmdArgs.put(LAST_LAZY_NODES_UPDATES, lazyUpdates);
-        cmdArgs.put(NEIGHBOR_UPDATES, neighborUpdates);
-        cmdArgs.put(CONTRACTED_NODES, contractedNodes);
-        cmdArgs.put(LOG_MESSAGES, logMessages);
-        cmdArgs.put(EDGE_QUOTIENT_WEIGHT, edgeQuotientWeight);
-        cmdArgs.put(ORIGINAL_EDGE_QUOTIENT_WEIGHT, origEdgeQuotientWeight);
-        cmdArgs.put(HIERARCHY_DEPTH_WEIGHT, hierarchyDepthWeight);
-        cmdArgs.put(SIGMA_FACTOR, sigmaFactor);
-        cmdArgs.put(MIN_MAX_SETTLED_EDGES, minMaxSettledEdges);
-        cmdArgs.put(SETTLED_EDGES_RESET_INTERVAL, resetInterval);
+        ghConfig.put(PERIODIC_UPDATES, periodicUpdates);
+        ghConfig.put(LAST_LAZY_NODES_UPDATES, lazyUpdates);
+        ghConfig.put(NEIGHBOR_UPDATES, neighborUpdates);
+        ghConfig.put(CONTRACTED_NODES, contractedNodes);
+        ghConfig.put(LOG_MESSAGES, logMessages);
+        ghConfig.put(EDGE_QUOTIENT_WEIGHT, edgeQuotientWeight);
+        ghConfig.put(ORIGINAL_EDGE_QUOTIENT_WEIGHT, origEdgeQuotientWeight);
+        ghConfig.put(HIERARCHY_DEPTH_WEIGHT, hierarchyDepthWeight);
+        ghConfig.put(SIGMA_FACTOR, sigmaFactor);
+        ghConfig.put(MIN_MAX_SETTLED_EDGES, minMaxSettledEdges);
+        ghConfig.put(SETTLED_EDGES_RESET_INTERVAL, resetInterval);
 
         LMAlgoFactoryDecorator lmDecorator = graphHopper.getLMFactoryDecorator();
         lmDecorator.setEnabled(landmarks > 0);
         lmDecorator.setDisablingAllowed(true);
 
-        LOGGER.info("Initializing graph hopper with args: {}", cmdArgs);
-        graphHopper.init(cmdArgs);
+        LOGGER.info("Initializing graph hopper with args: {}", ghConfig);
+        graphHopper.init(ghConfig);
 
         if (cleanup) {
             graphHopper.clean();
         }
 
-        PMap results = new PMap(cmdArgs);
+        PMap results = new PMap(ghConfig.asPMap());
 
         StopWatch sw = new StopWatch();
         sw.start();
diff --git a/tools/src/main/java/com/graphhopper/tools/Measurement.java b/tools/src/main/java/com/graphhopper/tools/Measurement.java
index b5e8be35b..7472cab87 100644
--- a/tools/src/main/java/com/graphhopper/tools/Measurement.java
+++ b/tools/src/main/java/com/graphhopper/tools/Measurement.java
@@ -18,10 +18,7 @@
 package com.graphhopper.tools;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.graphhopper.GHRequest;
-import com.graphhopper.GHResponse;
-import com.graphhopper.GraphHopper;
-import com.graphhopper.PathWrapper;
+import com.graphhopper.*;
 import com.graphhopper.coll.GHBitSet;
 import com.graphhopper.coll.GHBitSetImpl;
 import com.graphhopper.reader.DataReader;
@@ -63,15 +60,15 @@ public class Measurement {
     private int maxNode;
 
     public static void main(String[] strs) throws IOException {
-        CmdArgs cmdArgs = CmdArgs.read(strs);
-        int repeats = cmdArgs.getInt("measurement.repeats", 1);
+        PMap args = PMap.read(strs);
+        int repeats = args.getInt("measurement.repeats", 1);
         for (int i = 0; i < repeats; ++i)
-            new Measurement().start(cmdArgs);
+            new Measurement().start(args);
     }
 
     // creates properties file in the format key=value
     // Every value is one y-value in a separate diagram with an identical x-value for every Measurement.start call
-    void start(CmdArgs args) throws IOException {
+    void start(PMap args) throws IOException {
         final String graphLocation = args.get("graph.location", "");
         final boolean useJson = args.getBool("measurement.json", false);
         boolean cleanGraph = args.getBool("measurement.clean", false);
@@ -141,7 +138,7 @@ public class Measurement {
             }
         };
 
-        hopper.init(args).
+        hopper.init(new GraphHopperConfig(args)).
                 // use server to allow path simplification
                         forServer();
         if (cleanGraph) {
diff --git a/tools/src/main/java/com/graphhopper/tools/QueryTorture.java b/tools/src/main/java/com/graphhopper/tools/QueryTorture.java
index bd525614b..24f5eb1c8 100644
--- a/tools/src/main/java/com/graphhopper/tools/QueryTorture.java
+++ b/tools/src/main/java/com/graphhopper/tools/QueryTorture.java
@@ -60,17 +60,17 @@ public class QueryTorture {
     }
 
     public static void main(String[] args) {
-        new QueryTorture().start(CmdArgs.read(args));
+        new QueryTorture().start(PMap.read(args));
     }
 
-    public void start(CmdArgs cmdArgs) {
-        String logfile = cmdArgs.get("logfile", "");
-        int workers = cmdArgs.getInt("workers", 1);
-        baseUrl = cmdArgs.get("baseurl", "");
-        tooShortDistance = cmdArgs.getDouble("too_short_distance", 50d);
-        maxQueries = cmdArgs.getLong("maxqueries", 1000L);
-        timeout = cmdArgs.getInt("timeout", 3000);
-        logRequest = cmdArgs.getBool("log_request", false);
+    public void start(PMap args) {
+        String logfile = args.get("logfile", "");
+        int workers = args.getInt("workers", 1);
+        baseUrl = args.get("baseurl", "");
+        tooShortDistance = args.getDouble("too_short_distance", 50d);
+        maxQueries = args.getLong("maxqueries", 1000L);
+        timeout = args.getInt("timeout", 3000);
+        logRequest = args.getBool("log_request", false);
         statusUpdateCnt = maxQueries / 10;
         if (Helper.isEmpty(baseUrl))
             throw new IllegalArgumentException("baseUrl cannot be empty!?");
diff --git a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
index 0b091d86b..a0a8c10e0 100644
--- a/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
+++ b/tools/src/main/java/com/graphhopper/ui/MiniGraphUI.java
@@ -19,6 +19,7 @@ package com.graphhopper.ui;
 
 import com.carrotsearch.hppc.IntIndexedContainer;
 import com.graphhopper.GraphHopper;
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.coll.GHBitSet;
 import com.graphhopper.coll.GHTBitSet;
 import com.graphhopper.reader.osm.GraphHopperOSM;
@@ -35,7 +36,7 @@ import com.graphhopper.routing.weighting.Weighting;
 import com.graphhopper.storage.*;
 import com.graphhopper.storage.index.LocationIndexTree;
 import com.graphhopper.storage.index.QueryResult;
-import com.graphhopper.util.CmdArgs;
+import com.graphhopper.util.PMap;
 import com.graphhopper.util.Parameters;
 import com.graphhopper.util.Parameters.Algorithms;
 import com.graphhopper.util.PointList;
@@ -385,8 +386,9 @@ public class MiniGraphUI {
     }
 
     public static void main(String[] strs) throws Exception {
-        CmdArgs args = CmdArgs.read(strs);
-        GraphHopper hopper = new GraphHopperOSM().init(args).importOrLoad();
+        PMap args = PMap.read(strs);
+        GraphHopperConfig ghConfig = new GraphHopperConfig(args);
+        GraphHopper hopper = new GraphHopperOSM().init(ghConfig).importOrLoad();
         boolean debug = args.getBool("minigraphui.debug", false);
         new MiniGraphUI(hopper, debug).visualize();
     }
diff --git a/web-api/src/main/java/com/graphhopper/jackson/CmdArgsDeserializer.java b/web-api/src/main/java/com/graphhopper/jackson/CmdArgsDeserializer.java
deleted file mode 100644
index bdfeac5b2..000000000
--- a/web-api/src/main/java/com/graphhopper/jackson/CmdArgsDeserializer.java
+++ /dev/null
@@ -1,41 +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.jackson;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.graphhopper.util.CmdArgs;
-
-import java.io.IOException;
-import java.util.Map;
-
-public class CmdArgsDeserializer extends JsonDeserializer<CmdArgs> {
-
-    private static final TypeReference<Map<String,String>> MAP_STRING_STRING
-            = new TypeReference<Map<String,String>>() {};
-
-    @Override
-    public CmdArgs deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
-        jsonParser.setCodec(new ObjectMapper());
-        Map<String, String> args = jsonParser.readValueAs(MAP_STRING_STRING);
-        return new CmdArgs(args);
-    }
-}
diff --git a/web-api/src/main/java/com/graphhopper/jackson/GraphHopperConfigMixIn.java b/web-api/src/main/java/com/graphhopper/jackson/GraphHopperConfigMixIn.java
new file mode 100644
index 000000000..d91de0a13
--- /dev/null
+++ b/web-api/src/main/java/com/graphhopper/jackson/GraphHopperConfigMixIn.java
@@ -0,0 +1,30 @@
+/*
+ *  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.jackson;
+
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.graphhopper.GraphHopperConfig;
+
+public interface GraphHopperConfigMixIn {
+
+    // We can add explicit configuration properties to GraphHopperConfig (for example to allow lists or nested objects),
+    // everything else is stored in a HashMap
+    @JsonAnySetter
+    GraphHopperConfig put(String key, Object value);
+}
diff --git a/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java b/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java
index a3deb5d82..03ea1f2c2 100644
--- a/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java
+++ b/web-api/src/main/java/com/graphhopper/jackson/GraphHopperModule.java
@@ -18,11 +18,7 @@
 package com.graphhopper.jackson;
 
 import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.graphhopper.GHRequest;
-import com.graphhopper.GHResponse;
-import com.graphhopper.MultiException;
-import com.graphhopper.PathWrapper;
-import com.graphhopper.util.CmdArgs;
+import com.graphhopper.*;
 import com.graphhopper.util.InstructionList;
 import com.graphhopper.util.details.PathDetail;
 import com.graphhopper.util.shapes.BBox;
@@ -32,6 +28,7 @@ public class GraphHopperModule extends SimpleModule {
 
     public GraphHopperModule() {
         setMixInAnnotation(GHRequest.class, GHRequestMixIn.class);
+        setMixInAnnotation(GraphHopperConfig.class, GraphHopperConfigMixIn.class);
         addDeserializer(GHResponse.class, new GHResponseDeserializer());
         addDeserializer(PathWrapper.class, new PathWrapperDeserializer());
         addDeserializer(BBox.class, new BBoxDeserializer());
@@ -41,7 +38,6 @@ public class GraphHopperModule extends SimpleModule {
         addDeserializer(PathDetail.class, new PathDetailDeserializer());
         addSerializer(PathDetail.class, new PathDetailSerializer());
         addSerializer(InstructionList.class, new InstructionListSerializer());
-        addDeserializer(CmdArgs.class, new CmdArgsDeserializer());
         addSerializer(MultiException.class, new MultiExceptionSerializer());
     }
 
diff --git a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
index 37c4f0b0a..a288b93c1 100644
--- a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
+++ b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundle.java
@@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.util.StdDateFormat;
 import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
 import com.graphhopper.GraphHopper;
 import com.graphhopper.GraphHopperAPI;
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.http.health.GraphHopperHealthCheck;
 import com.graphhopper.jackson.Jackson;
 import com.graphhopper.reader.gtfs.GraphHopperGtfs;
@@ -38,7 +39,6 @@ import com.graphhopper.resources.*;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.index.LocationIndex;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.TranslationMap;
 import io.dropwizard.ConfiguredBundle;
 import io.dropwizard.setup.Bootstrap;
@@ -178,8 +178,6 @@ public class GraphHopperBundle implements ConfiguredBundle<GraphHopperBundleConf
 
     @Override
     public void run(GraphHopperBundleConfiguration configuration, Environment environment) {
-        configuration.getGraphHopperConfiguration().merge(CmdArgs.readFromSystemProperties());
-
         // If the "?type=gpx" parameter is present, sets a corresponding media type header
         environment.jersey().register(new TypeGPXFilter());
 
@@ -196,7 +194,7 @@ public class GraphHopperBundle implements ConfiguredBundle<GraphHopperBundleConf
         environment.jersey().register(new AbstractBinder() {
             @Override
             protected void configure() {
-                bind(configuration.getGraphHopperConfiguration()).to(CmdArgs.class);
+                bind(configuration.getGraphHopperConfiguration()).to(GraphHopperConfig.class);
                 bind(graphHopperManaged.getGraphHopper()).to(GraphHopper.class);
                 bind(graphHopperManaged.getGraphHopper()).to(GraphHopperAPI.class);
 
diff --git a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundleConfiguration.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundleConfiguration.java
index b933aa7bd..b99a1f58d 100644
--- a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundleConfiguration.java
+++ b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperBundleConfiguration.java
@@ -18,10 +18,10 @@
 
 package com.graphhopper.http;
 
-import com.graphhopper.util.CmdArgs;
+import com.graphhopper.GraphHopperConfig;
 
 public interface GraphHopperBundleConfiguration {
 
-    CmdArgs getGraphHopperConfiguration();
+    GraphHopperConfig getGraphHopperConfiguration();
 
 }
diff --git a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java
index 3d2207093..9849721ac 100644
--- a/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java
+++ b/web-bundle/src/main/java/com/graphhopper/http/GraphHopperManaged.java
@@ -21,25 +21,20 @@ package com.graphhopper.http;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.graphhopper.GraphHopper;
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.json.geo.JsonFeatureCollection;
 import com.graphhopper.reader.gtfs.GraphHopperGtfs;
 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.locationtech.jts.geom.Envelope;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import java.io.*;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
@@ -55,7 +50,7 @@ public class GraphHopperManaged implements Managed {
     private final Logger logger = LoggerFactory.getLogger(getClass());
     private final GraphHopper graphHopper;
 
-    public GraphHopperManaged(CmdArgs configuration, ObjectMapper objectMapper) {
+    public GraphHopperManaged(GraphHopperConfig configuration, ObjectMapper objectMapper) {
         ObjectMapper localObjectMapper = objectMapper.copy();
         localObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
         String splitAreaLocation = configuration.get(Parameters.Landmark.PREPARE + "split_area_location", "");
diff --git a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
index 9e09ff291..456f592be 100644
--- a/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
+++ b/web-bundle/src/main/java/com/graphhopper/resources/InfoResource.java
@@ -17,8 +17,8 @@
  */
 package com.graphhopper.resources;
 
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.storage.GraphHopperStorage;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Constants;
 import com.graphhopper.util.shapes.BBox;
 
@@ -37,12 +37,12 @@ import java.util.*;
 @Produces(MediaType.APPLICATION_JSON)
 public class InfoResource {
 
-    private final CmdArgs config;
+    private final GraphHopperConfig config;
     private final GraphHopperStorage storage;
     private final boolean hasElevation;
 
     @Inject
-    public InfoResource(CmdArgs config, GraphHopperStorage storage, @Named("hasElevation") Boolean hasElevation) {
+    public InfoResource(GraphHopperConfig config, GraphHopperStorage storage, @Named("hasElevation") Boolean hasElevation) {
         this.config = config;
         this.storage = storage;
         this.hasElevation = hasElevation;
diff --git a/web/src/main/java/com/graphhopper/http/GraphHopperServerConfiguration.java b/web/src/main/java/com/graphhopper/http/GraphHopperServerConfiguration.java
index b09ab2989..ad0121c47 100644
--- a/web/src/main/java/com/graphhopper/http/GraphHopperServerConfiguration.java
+++ b/web/src/main/java/com/graphhopper/http/GraphHopperServerConfiguration.java
@@ -19,24 +19,21 @@
 package com.graphhopper.http;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.gtfs.dropwizard.RealtimeBundleConfiguration;
-import com.graphhopper.gtfs.dropwizard.FeedConfiguration;
 import com.graphhopper.gtfs.dropwizard.RealtimeConfiguration;
-import com.graphhopper.util.CmdArgs;
 import io.dropwizard.Configuration;
 import io.dropwizard.bundles.assets.AssetsBundleConfiguration;
 import io.dropwizard.bundles.assets.AssetsConfiguration;
 
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
-import java.util.ArrayList;
-import java.util.List;
 
 public class GraphHopperServerConfiguration extends Configuration implements GraphHopperBundleConfiguration, RealtimeBundleConfiguration, AssetsBundleConfiguration {
 
     @NotNull
     @JsonProperty
-    private final CmdArgs graphhopper = new CmdArgs();
+    private final GraphHopperConfig graphhopper = new GraphHopperConfig();
 
     @Valid
     @JsonProperty
@@ -49,7 +46,7 @@ public class GraphHopperServerConfiguration extends Configuration implements Gra
     }
 
     @Override
-    public CmdArgs getGraphHopperConfiguration() {
+    public GraphHopperConfig getGraphHopperConfiguration() {
         return graphhopper;
     }
 


Test patch:
diff --git a/core/src/test/java/com/graphhopper/routing/lm/LMAlgoFactoryDecoratorTest.java b/core/src/test/java/com/graphhopper/routing/lm/LMAlgoFactoryDecoratorTest.java
index 442ea430c..26aea37f4 100644
--- a/core/src/test/java/com/graphhopper/routing/lm/LMAlgoFactoryDecoratorTest.java
+++ b/core/src/test/java/com/graphhopper/routing/lm/LMAlgoFactoryDecoratorTest.java
@@ -1,5 +1,6 @@
 package com.graphhopper.routing.lm;
 
+import com.graphhopper.GraphHopperConfig;
 import com.graphhopper.routing.util.CarFlagEncoder;
 import com.graphhopper.routing.util.EncodingManager;
 import com.graphhopper.routing.util.FlagEncoder;
@@ -7,7 +8,6 @@ import com.graphhopper.routing.weighting.FastestWeighting;
 import com.graphhopper.routing.weighting.ShortestWeighting;
 import com.graphhopper.storage.GraphHopperStorage;
 import com.graphhopper.storage.RAMDirectory;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Parameters;
 import org.junit.Test;
 
@@ -40,21 +40,21 @@ public class LMAlgoFactoryDecoratorTest {
 
     @Test
     public void testPrepareWeightingNo() {
-        CmdArgs args = new CmdArgs();
-        args.put(Parameters.Landmark.PREPARE + "weightings", "fastest");
+        GraphHopperConfig ghConfig = new GraphHopperConfig();
+        ghConfig.put(Parameters.Landmark.PREPARE + "weightings", "fastest");
         LMAlgoFactoryDecorator dec = new LMAlgoFactoryDecorator();
-        dec.init(args);
+        dec.init(ghConfig);
         assertTrue(dec.isEnabled());
 
         // See #1076
-        args.put(Parameters.Landmark.PREPARE + "weightings", "no");
+        ghConfig.put(Parameters.Landmark.PREPARE + "weightings", "no");
         dec = new LMAlgoFactoryDecorator();
-        dec.init(args);
+        dec.init(ghConfig);
         assertFalse(dec.isEnabled());
 
-        args.put(Parameters.Landmark.PREPARE + "weightings", "false");
+        ghConfig.put(Parameters.Landmark.PREPARE + "weightings", "false");
         dec = new LMAlgoFactoryDecorator();
-        dec.init(args);
+        dec.init(ghConfig);
         assertFalse(dec.isEnabled());
     }
 }
\ No newline at end of file
diff --git a/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java b/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java
index 52439164b..e9d965015 100644
--- a/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java
+++ b/reader-gtfs/src/test/java/com/graphhopper/AnotherAgencyIT.java
@@ -22,7 +22,6 @@ import com.carrotsearch.hppc.IntHashSet;
 import com.graphhopper.reader.gtfs.*;
 import com.graphhopper.routing.util.AllEdgesIterator;
 import com.graphhopper.routing.weighting.FastestWeighting;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.EdgeIteratorState;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.TranslationMap;
@@ -50,14 +49,14 @@ public class AnotherAgencyIT {
 
     @BeforeClass
     public static void init() {
-        CmdArgs cmdArgs = new CmdArgs();
-        cmdArgs.put("graph.flag_encoders", "car,foot");
-        cmdArgs.put("graph.location", GRAPH_LOC);
-        cmdArgs.put("datareader.file", "files/beatty.osm");
-        cmdArgs.put("gtfs.file", "files/sample-feed.zip,files/another-sample-feed.zip");
+        GraphHopperConfig ghConfig = new GraphHopperConfig();
+        ghConfig.put("graph.flag_encoders", "car,foot");
+        ghConfig.put("graph.location", GRAPH_LOC);
+        ghConfig.put("datareader.file", "files/beatty.osm");
+        ghConfig.put("gtfs.file", "files/sample-feed.zip,files/another-sample-feed.zip");
         Helper.removeDir(new File(GRAPH_LOC));
-        graphHopperGtfs = new GraphHopperGtfs(cmdArgs);
-        graphHopperGtfs.init(cmdArgs);
+        graphHopperGtfs = new GraphHopperGtfs(ghConfig);
+        graphHopperGtfs.init(ghConfig);
         graphHopperGtfs.importOrLoad();
         ptRouteResource = PtRouteResource.createFactory(new TranslationMap().doImport(), graphHopperGtfs, graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage())
                 .createWithoutRealtimeFeed();
diff --git a/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java b/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java
index a875abf7c..388c6e0bf 100644
--- a/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java
+++ b/reader-gtfs/src/test/java/com/graphhopper/ExtendedRouteTypeIT.java
@@ -21,7 +21,6 @@ package com.graphhopper;
 import com.graphhopper.reader.gtfs.GraphHopperGtfs;
 import com.graphhopper.reader.gtfs.PtRouteResource;
 import com.graphhopper.reader.gtfs.Request;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.TranslationMap;
 import org.junit.AfterClass;
@@ -45,13 +44,13 @@ public class ExtendedRouteTypeIT {
 
     @BeforeClass
     public static void init() {
-        CmdArgs cmdArgs = new CmdArgs();
-        cmdArgs.put("graph.flag_encoders", "car,foot");
-        cmdArgs.put("graph.location", GRAPH_LOC);
-        cmdArgs.put("gtfs.file", "files/another-sample-feed-extended-route-type.zip");
+        GraphHopperConfig ghConfig = new GraphHopperConfig();
+        ghConfig.put("graph.flag_encoders", "car,foot");
+        ghConfig.put("graph.location", GRAPH_LOC);
+        ghConfig.put("gtfs.file", "files/another-sample-feed-extended-route-type.zip");
         Helper.removeDir(new File(GRAPH_LOC));
-        graphHopperGtfs = new GraphHopperGtfs(cmdArgs);
-        graphHopperGtfs.init(cmdArgs);
+        graphHopperGtfs = new GraphHopperGtfs(ghConfig);
+        graphHopperGtfs.init(ghConfig);
         graphHopperGtfs.importOrLoad();
         ptRouteResource = PtRouteResource.createFactory(new TranslationMap().doImport(), graphHopperGtfs, graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage())
                 .createWithoutRealtimeFeed();
diff --git a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java
index ee7f4bae3..d40e63231 100644
--- a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java
+++ b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperGtfsIT.java
@@ -21,7 +21,6 @@ package com.graphhopper;
 import com.graphhopper.reader.gtfs.GraphHopperGtfs;
 import com.graphhopper.reader.gtfs.PtRouteResource;
 import com.graphhopper.reader.gtfs.Request;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.Instruction;
 import com.graphhopper.util.TranslationMap;
@@ -51,13 +50,13 @@ public class GraphHopperGtfsIT {
 
     @BeforeClass
     public static void init() {
-        CmdArgs cmdArgs = new CmdArgs();
-        cmdArgs.put("graph.flag_encoders", "car,foot");
-        cmdArgs.put("graph.location", GRAPH_LOC);
-        cmdArgs.put("gtfs.file", "files/sample-feed.zip");
+        GraphHopperConfig ghConfig = new GraphHopperConfig();
+        ghConfig.put("graph.flag_encoders", "car,foot");
+        ghConfig.put("graph.location", GRAPH_LOC);
+        ghConfig.put("gtfs.file", "files/sample-feed.zip");
         Helper.removeDir(new File(GRAPH_LOC));
-        graphHopperGtfs = new GraphHopperGtfs(cmdArgs);
-        graphHopperGtfs.init(cmdArgs);
+        graphHopperGtfs = new GraphHopperGtfs(ghConfig);
+        graphHopperGtfs.init(ghConfig);
         graphHopperGtfs.importOrLoad();
         ptRouteResource = PtRouteResource.createFactory(new TranslationMap().doImport(), graphHopperGtfs, graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage())
                 .createWithoutRealtimeFeed();
diff --git a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java
index c1b5408cb..cf119f763 100644
--- a/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java
+++ b/reader-gtfs/src/test/java/com/graphhopper/GraphHopperMultimodalIT.java
@@ -22,7 +22,6 @@ import com.graphhopper.reader.gtfs.GraphHopperGtfs;
 import com.graphhopper.reader.gtfs.PtRouteResource;
 import com.graphhopper.reader.gtfs.Request;
 import com.graphhopper.storage.index.LocationIndex;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.TranslationMap;
 import org.junit.AfterClass;
@@ -46,14 +45,14 @@ public class GraphHopperMultimodalIT {
 
     @BeforeClass
     public static void init() {
-        CmdArgs cmdArgs = new CmdArgs();
-        cmdArgs.put("graph.flag_encoders", "car,foot");
-        cmdArgs.put("datareader.file", "files/beatty.osm");
-        cmdArgs.put("gtfs.file", "files/sample-feed.zip");
-        cmdArgs.put("graph.location", GRAPH_LOC);
+        GraphHopperConfig ghConfig = new GraphHopperConfig();
+        ghConfig.put("graph.flag_encoders", "car,foot");
+        ghConfig.put("datareader.file", "files/beatty.osm");
+        ghConfig.put("gtfs.file", "files/sample-feed.zip");
+        ghConfig.put("graph.location", GRAPH_LOC);
         Helper.removeDir(new File(GRAPH_LOC));
-        graphHopperGtfs = new GraphHopperGtfs(cmdArgs);
-        graphHopperGtfs.init(cmdArgs);
+        graphHopperGtfs = new GraphHopperGtfs(ghConfig);
+        graphHopperGtfs.init(ghConfig);
         graphHopperGtfs.importOrLoad();
         locationIndex = graphHopperGtfs.getLocationIndex();
         graphHopper = PtRouteResource.createFactory(new TranslationMap().doImport(), graphHopperGtfs, locationIndex, graphHopperGtfs.getGtfsStorage())
diff --git a/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java b/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java
index 8efdb9505..415eb8a60 100644
--- a/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java
+++ b/reader-gtfs/src/test/java/com/graphhopper/RealtimeIT.java
@@ -22,7 +22,6 @@ import com.google.transit.realtime.GtfsRealtime;
 import com.graphhopper.reader.gtfs.GraphHopperGtfs;
 import com.graphhopper.reader.gtfs.PtRouteResource;
 import com.graphhopper.reader.gtfs.Request;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.TranslationMap;
 import org.junit.AfterClass;
@@ -48,18 +47,18 @@ public class RealtimeIT {
 
     @BeforeClass
     public static void init() {
-        CmdArgs cmdArgs = new CmdArgs();
-        cmdArgs.put("graph.flag_encoders", "car,foot");
-        cmdArgs.put("gtfs.file", "files/sample-feed.zip");
-        cmdArgs.put("graph.location", GRAPH_LOC);
+        GraphHopperConfig ghConfig = new GraphHopperConfig();
+        ghConfig.put("graph.flag_encoders", "car,foot");
+        ghConfig.put("gtfs.file", "files/sample-feed.zip");
+        ghConfig.put("graph.location", GRAPH_LOC);
         Helper.removeDir(new File(GRAPH_LOC));
-        graphHopperGtfs = new GraphHopperGtfs(cmdArgs);
-        graphHopperGtfs.init(cmdArgs);
+        graphHopperGtfs = new GraphHopperGtfs(ghConfig);
+        graphHopperGtfs.init(ghConfig);
         graphHopperGtfs.importOrLoad();
         graphHopperGtfs.close();
         // Re-load read only
-        graphHopperGtfs = new GraphHopperGtfs(cmdArgs);
-        graphHopperGtfs.init(cmdArgs);
+        graphHopperGtfs = new GraphHopperGtfs(ghConfig);
+        graphHopperGtfs.init(ghConfig);
         graphHopperGtfs.importOrLoad();
         graphHopperFactory = PtRouteResource.createFactory(new TranslationMap().doImport(), graphHopperGtfs, graphHopperGtfs.getLocationIndex(), graphHopperGtfs.getGtfsStorage());
     }
diff --git a/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java b/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
index 3c2a10b09..36f62a55c 100644
--- a/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
+++ b/reader-osm/src/test/java/com/graphhopper/reader/osm/GraphHopperOSMTest.java
@@ -18,10 +18,7 @@
 package com.graphhopper.reader.osm;
 
 import com.carrotsearch.hppc.IntArrayList;
-import com.graphhopper.GHRequest;
-import com.graphhopper.GHResponse;
-import com.graphhopper.GraphHopper;
-import com.graphhopper.PathWrapper;
+import com.graphhopper.*;
 import com.graphhopper.coll.GHBitSet;
 import com.graphhopper.coll.GHBitSetImpl;
 import com.graphhopper.reader.DataReader;
@@ -142,7 +139,7 @@ public class GraphHopperOSMTest {
         gh = new GraphHopperOSM().
                 setGraphHopperLocation(ghLoc).
                 setDataReaderFile(testOsm).
-                init(new CmdArgs().put("graph.flag_encoders", "car").put(Parameters.CH.PREPARE + "weightings", "no"));
+                init(new GraphHopperConfig().put("graph.flag_encoders", "car").put(Parameters.CH.PREPARE + "weightings", "no"));
 
         assertFalse(gh.getAlgorithmFactory(new HintsMap("fastest")) instanceof PrepareContractionHierarchies);
         gh.close();
@@ -422,7 +419,7 @@ public class GraphHopperOSMTest {
     @Test
     public void testFailsForWrongConfig() {
         instance = new GraphHopperOSM().init(
-                new CmdArgs().
+                new GraphHopperConfig().
                         put("datareader.file", testOsm3).
                         put("datareader.dataaccess", "RAM").
                         put("graph.flag_encoders", "foot,car").
@@ -435,7 +432,7 @@ public class GraphHopperOSMTest {
         // different config (flagEncoder list)
         try {
             GraphHopper tmpGH = new GraphHopperOSM().init(
-                    new CmdArgs().
+                    new GraphHopperConfig().
                             put("datareader.file", testOsm3).
                             put("datareader.dataaccess", "RAM").
                             put("graph.flag_encoders", "foot").
@@ -449,7 +446,7 @@ public class GraphHopperOSMTest {
 
         // different order is no longer okay, see #350
         try {
-            GraphHopper tmpGH = new GraphHopperOSM().init(new CmdArgs().
+            GraphHopper tmpGH = new GraphHopperOSM().init(new GraphHopperConfig().
                     put("datareader.file", testOsm3).
                     put("datareader.dataaccess", "RAM").
                     put(Parameters.CH.PREPARE + "weightings", "no").
@@ -463,7 +460,7 @@ public class GraphHopperOSMTest {
 
         // different encoded values should fail to load
         instance = new GraphHopperOSM().init(
-                new CmdArgs().
+                new GraphHopperConfig().
                         put("datareader.file", testOsm3).
                         put("datareader.dataaccess", "RAM").
                         put("graph.encoded_values", "road_class").
@@ -484,7 +481,7 @@ public class GraphHopperOSMTest {
                 return 0;
             }
         })).init(
-                new CmdArgs().
+                new GraphHopperConfig().
                         put("datareader.file", testOsm3).
                         put("datareader.dataaccess", "RAM").
                         put(Parameters.CH.PREPARE + "weightings", "no")).
@@ -500,7 +497,7 @@ public class GraphHopperOSMTest {
     @Test
     public void testFailsForWrongEVConfig() {
         instance = new GraphHopperOSM().init(
-                new CmdArgs().
+                new GraphHopperConfig().
                         put("datareader.file", testOsm3).
                         put("datareader.dataaccess", "RAM").
                         put("graph.flag_encoders", "foot,car").
@@ -515,7 +512,7 @@ public class GraphHopperOSMTest {
 
         // different encoded values should fail to load
         instance = new GraphHopperOSM().init(
-                new CmdArgs().
+                new GraphHopperConfig().
                         put("datareader.file", testOsm3).
                         put("datareader.dataaccess", "RAM").
                         put("graph.encoded_values", "road_environment,road_class").
@@ -645,7 +642,7 @@ public class GraphHopperOSMTest {
     @Test
     public void testVia() {
         instance = new GraphHopperOSM().setStoreOnFlush(true).
-                init(new CmdArgs().
+                init(new GraphHopperConfig().
                         put("datareader.file", testOsm3).
                         put("prepare.min_network_size", "1").
                         put("graph.flag_encoders", "car")).
@@ -849,7 +846,7 @@ public class GraphHopperOSMTest {
         final RoutingAlgorithmFactory af = new RoutingAlgorithmFactorySimple();
         instance.addAlgorithmFactoryDecorator(new RoutingAlgorithmFactoryDecorator() {
             @Override
-            public void init(CmdArgs args) {
+            public void init(GraphHopperConfig ghConfig) {
             }
 
             @Override
@@ -870,7 +867,7 @@ public class GraphHopperOSMTest {
         final AtomicInteger cnt = new AtomicInteger(0);
         instance.addAlgorithmFactoryDecorator(new RoutingAlgorithmFactoryDecorator() {
             @Override
-            public void init(CmdArgs args) {
+            public void init(GraphHopperConfig ghConfig) {
             }
 
             public RoutingAlgorithmFactory getDecoratedAlgorithmFactory(RoutingAlgorithmFactory algoFactory, HintsMap map) {
diff --git a/web/src/test/java/com/graphhopper/http/GraphHopperLandmarksTest.java b/web/src/test/java/com/graphhopper/http/GraphHopperLandmarksTest.java
index a98ab0a24..718aaaeb4 100644
--- a/web/src/test/java/com/graphhopper/http/GraphHopperLandmarksTest.java
+++ b/web/src/test/java/com/graphhopper/http/GraphHopperLandmarksTest.java
@@ -18,7 +18,6 @@
 package com.graphhopper.http;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -42,7 +41,7 @@ public class GraphHopperLandmarksTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.flag_encoders", "car").
                 put("prepare.ch.weightings", "fastest").
                 put("prepare.lm.weightings", "fastest").
@@ -52,7 +51,7 @@ public class GraphHopperLandmarksTest {
                 put("routing.ch.disabling_allowed", true).
                 put("routing.lm.disabling_allowed", true).
                 put("graph.location", DIR).
-                put("prepare.lm.min_network_size", 2)); // force landmark creation even for tiny networks
+                put("prepare.lm.min_network_size", 2); // force landmark creation even for tiny networks
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/SpatialRulesTest.java b/web/src/test/java/com/graphhopper/http/SpatialRulesTest.java
index b1c6bc2c9..fbff27b3a 100644
--- a/web/src/test/java/com/graphhopper/http/SpatialRulesTest.java
+++ b/web/src/test/java/com/graphhopper/http/SpatialRulesTest.java
@@ -18,7 +18,6 @@
 package com.graphhopper.http;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -43,14 +42,14 @@ public class SpatialRulesTest {
     static {
      // The EncodedValue "country" requires the setting "spatial_rules.borders_directory" as "country" does not load via DefaultTagParserFactory
         // TODO should we automatically detect this somehow and include a default country file?
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.flag_encoders", "car").
                 put("graph.encoded_values", "country,road_environment,road_class,road_access,max_speed").
                 put("prepare.ch.weightings", "no").
                 put("spatial_rules.borders_directory", "../core/files/spatialrules").
                 put("spatial_rules.max_bbox", "11.4,11.7,49.9,50.1").
                 put("datareader.file", "../core/files/north-bayreuth.osm.gz").
-                put("graph.location", DIR));
+                put("graph.location", DIR);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/ChangeGraphResourceTest.java b/web/src/test/java/com/graphhopper/http/resources/ChangeGraphResourceTest.java
index e15686745..865acd1ce 100644
--- a/web/src/test/java/com/graphhopper/http/resources/ChangeGraphResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/ChangeGraphResourceTest.java
@@ -20,7 +20,6 @@ package com.graphhopper.http.resources;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.Parameters;
 import io.dropwizard.testing.junit.DropwizardAppRule;
@@ -43,12 +42,12 @@ public class ChangeGraphResourceTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put(Parameters.CH.PREPARE + "weightings", "no").
                 put("graph.flag_encoders", "car").
                 put("web.change_graph.enabled", "true").
                 put("graph.location", DIR).
-                put("datareader.file", "../core/files/andorra.osm.pbf"));
+                put("datareader.file", "../core/files/andorra.osm.pbf");
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/GtfsTest.java b/web/src/test/java/com/graphhopper/http/resources/GtfsTest.java
index 193d07adb..900b7ad81 100644
--- a/web/src/test/java/com/graphhopper/http/resources/GtfsTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/GtfsTest.java
@@ -21,7 +21,6 @@ import com.graphhopper.GHResponse;
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
 import com.graphhopper.resources.InfoResource;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -44,11 +43,11 @@ public class GtfsTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.flag_encoders", "foot").
                 put("datareader.file", "../reader-gtfs/files/beatty.osm").
                 put("gtfs.file", "../reader-gtfs/files/sample-feed.zip").
-                put("graph.location", DIR));
+                put("graph.location", DIR);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/I18nResourceTest.java b/web/src/test/java/com/graphhopper/http/resources/I18nResourceTest.java
index 60387208a..8454c4259 100644
--- a/web/src/test/java/com/graphhopper/http/resources/I18nResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/I18nResourceTest.java
@@ -2,7 +2,6 @@ package com.graphhopper.http.resources;
 
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -21,11 +20,11 @@ public class I18nResourceTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("prepare.ch.weightings", "no").
                 put("graph.flag_encoders", "car").
                 put("datareader.file", "../core/files/andorra.osm.pbf").
-                put("graph.location", DIR));
+                put("graph.location", DIR);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/IsochroneResourceTest.java b/web/src/test/java/com/graphhopper/http/resources/IsochroneResourceTest.java
index 9d6e4058f..5cea44281 100644
--- a/web/src/test/java/com/graphhopper/http/resources/IsochroneResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/IsochroneResourceTest.java
@@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
 import com.graphhopper.json.geo.JsonFeatureCollection;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -45,11 +44,11 @@ public class IsochroneResourceTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("prepare.ch.weightings", "no").
                 put("graph.flag_encoders", "car").
                 put("datareader.file", "../core/files/andorra.osm.pbf").
-                put("graph.location", DIR));
+                put("graph.location", DIR);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/MvtResourceTest.java b/web/src/test/java/com/graphhopper/http/resources/MvtResourceTest.java
index eeda7b418..b8caef0ca 100644
--- a/web/src/test/java/com/graphhopper/http/resources/MvtResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/MvtResourceTest.java
@@ -19,7 +19,6 @@ package com.graphhopper.http.resources;
 
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.wdtinc.mapbox_vector_tile.adapt.jts.MvtReader;
 import com.wdtinc.mapbox_vector_tile.adapt.jts.TagKeyValueMapConverter;
@@ -53,14 +52,14 @@ public class MvtResourceTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.flag_encoders", "car").
                 put("graph.encoded_values", "road_class,road_environment,max_speed,surface").
                 put("prepare.ch.weightings", "no").
                 put("prepare.min_network_size", "0").
                 put("prepare.min_one_way_network_size", "0").
                 put("datareader.file", "../core/files/andorra.osm.pbf").
-                put("graph.location", DIR));
+                put("graph.location", DIR);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/NearestResourceTest.java b/web/src/test/java/com/graphhopper/http/resources/NearestResourceTest.java
index f27ecb63c..06b734408 100644
--- a/web/src/test/java/com/graphhopper/http/resources/NearestResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/NearestResourceTest.java
@@ -20,7 +20,6 @@ package com.graphhopper.http.resources;
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
 import com.graphhopper.resources.NearestResource;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -42,11 +41,11 @@ public class NearestResourceTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.flag_encoders", "car").
                 put("prepare.ch.weightings", "fastest").
                 put("datareader.file", "../core/files/andorra.osm.pbf").
-                put("graph.location", dir));
+                put("graph.location", dir);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/NearestResourceWithEleTest.java b/web/src/test/java/com/graphhopper/http/resources/NearestResourceWithEleTest.java
index eb016196a..989d607af 100644
--- a/web/src/test/java/com/graphhopper/http/resources/NearestResourceWithEleTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/NearestResourceWithEleTest.java
@@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.Parameters;
 import io.dropwizard.testing.junit.DropwizardAppRule;
@@ -43,14 +42,14 @@ public class NearestResourceWithEleTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.elevation.provider", "srtm").
                 put("graph.elevation.cachedir", "../core/files/").
                 put(Parameters.CH.PREPARE + "weightings", "no").
                 put("prepare.min_one_way_network_size", "0").
                 put("graph.flag_encoders", "car").
                 put("datareader.file", "../core/files/monaco.osm.gz").
-                put("graph.location", dir));
+                put("graph.location", dir);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/PtIsochroneTest.java b/web/src/test/java/com/graphhopper/http/resources/PtIsochroneTest.java
index c59da6a28..5a094db23 100644
--- a/web/src/test/java/com/graphhopper/http/resources/PtIsochroneTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/PtIsochroneTest.java
@@ -21,7 +21,6 @@ package com.graphhopper.http.resources;
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
 import com.graphhopper.resources.PtIsochroneResource;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -50,10 +49,10 @@ public class PtIsochroneTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs()
-        .put("graph.flag_encoders", "foot")
-        .put("graph.location", GRAPH_LOC)
-        .put("gtfs.file", "../reader-gtfs/files/sample-feed.zip"));
+        config.getGraphHopperConfiguration()
+                .put("graph.flag_encoders", "foot")
+                .put("graph.location", GRAPH_LOC)
+                .put("gtfs.file", "../reader-gtfs/files/sample-feed.zip");
         Helper.removeDir(new File(GRAPH_LOC));
     }
 
diff --git a/web/src/test/java/com/graphhopper/http/resources/RouteResourceIssue1574Test.java b/web/src/test/java/com/graphhopper/http/resources/RouteResourceIssue1574Test.java
index 828733d97..3979728c8 100644
--- a/web/src/test/java/com/graphhopper/http/resources/RouteResourceIssue1574Test.java
+++ b/web/src/test/java/com/graphhopper/http/resources/RouteResourceIssue1574Test.java
@@ -20,7 +20,6 @@ package com.graphhopper.http.resources;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -44,13 +43,13 @@ public class RouteResourceIssue1574Test {
 
     static {
         // this is the reason we put this test into an extra file: we can only reproduce the bug of issue 1574 by increasing the one-way-network size
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.flag_encoders", "car").
                 put("prepare.ch.weightings", "fastest").
                 put("prepare.min_network_size", "0").
                 put("prepare.min_one_way_network_size", "12").
                 put("datareader.file", "../core/files/andorra.osm.pbf").
-                put("graph.location", DIR));
+                put("graph.location", DIR);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java b/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java
index f297f0214..e0a1379b0 100644
--- a/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/RouteResourceTest.java
@@ -28,7 +28,6 @@ import com.graphhopper.http.GraphHopperServerConfiguration;
 import com.graphhopper.routing.profiles.RoadClass;
 import com.graphhopper.routing.profiles.RoadEnvironment;
 import com.graphhopper.routing.profiles.Surface;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.InstructionList;
 import com.graphhopper.util.Parameters;
@@ -60,7 +59,7 @@ public class RouteResourceTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.flag_encoders", "car").
                 put("prepare.ch.weightings", "fastest").
                 put("routing.ch.disabling_allowed", "true").
@@ -68,7 +67,7 @@ public class RouteResourceTest {
                 put("prepare.min_one_way_network_size", "0").
                 put("datareader.file", "../core/files/andorra.osm.pbf").
                 put("graph.encoded_values", "road_class,surface,road_environment,max_speed").
-                put("graph.location", DIR));
+                put("graph.location", DIR);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/RouteResourceWithEleTest.java b/web/src/test/java/com/graphhopper/http/resources/RouteResourceWithEleTest.java
index cb13a5c89..07cc03517 100644
--- a/web/src/test/java/com/graphhopper/http/resources/RouteResourceWithEleTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/RouteResourceWithEleTest.java
@@ -20,7 +20,6 @@ package com.graphhopper.http.resources;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import com.graphhopper.util.Parameters;
 import io.dropwizard.testing.junit.DropwizardAppRule;
@@ -42,14 +41,14 @@ public class RouteResourceWithEleTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("graph.elevation.provider", "srtm").
                 put("graph.elevation.cachedir", "../core/files/").
                 put(Parameters.CH.PREPARE + "weightings", "no").
                 put("prepare.min_one_way_network_size", "0").
                 put("graph.flag_encoders", "car").
                 put("datareader.file", "../core/files/monaco.osm.gz").
-                put("graph.location", dir));
+                put("graph.location", dir);
     }
 
     @ClassRule
diff --git a/web/src/test/java/com/graphhopper/http/resources/SPTResourceTest.java b/web/src/test/java/com/graphhopper/http/resources/SPTResourceTest.java
index 38b089e84..b52e68cc5 100644
--- a/web/src/test/java/com/graphhopper/http/resources/SPTResourceTest.java
+++ b/web/src/test/java/com/graphhopper/http/resources/SPTResourceTest.java
@@ -20,7 +20,6 @@ package com.graphhopper.http.resources;
 
 import com.graphhopper.http.GraphHopperApplication;
 import com.graphhopper.http.GraphHopperServerConfiguration;
-import com.graphhopper.util.CmdArgs;
 import com.graphhopper.util.Helper;
 import io.dropwizard.testing.junit.DropwizardAppRule;
 import org.junit.AfterClass;
@@ -42,12 +41,12 @@ public class SPTResourceTest {
     private static final GraphHopperServerConfiguration config = new GraphHopperServerConfiguration();
 
     static {
-        config.getGraphHopperConfiguration().merge(new CmdArgs().
+        config.getGraphHopperConfiguration().
                 put("prepare.ch.weightings", "no").
                 put("graph.flag_encoders", "car").
                 put("graph.encoded_values", "max_speed,road_class").
                 put("datareader.file", "../core/files/andorra.osm.pbf").
-                put("graph.location", DIR));
+                put("graph.location", DIR);
     }
 
     @ClassRule