From 859bb6ca78200ee6c210e12d5659ff80cf716023 Mon Sep 17 00:00:00 2001 From: nmihajlovski Date: Fri, 19 Aug 2016 15:42:06 -0700 Subject: [PATCH] Refactored configuration, restructured "segments" and renamed to "zones". --- .../java/org/rapidoid/config/BasicConfig.java | 38 ++ .../main/java/org/rapidoid/config/Conf.java | 26 +- .../main/java/org/rapidoid/config/Config.java | 346 ++-------------- .../rapidoid/config/ConfigAlternatives.java | 20 +- .../java/org/rapidoid/config/ConfigImpl.java | 388 ++++++++++++++++++ .../src/main/java/org/rapidoid/util/Msc.java | 12 +- .../java/org/rapidoid/web/ScreenBean.java | 2 +- ...default.yml => default-config-default.yml} | 0 .../config-dev.yml => default-config-dev.yml} | 0 ...fig-mysql.yml => default-config-mysql.yml} | 0 ...onfig-test.yml => default-config-test.yml} | 0 .../config.yml => default-config.yml} | 63 +-- .../src/main/resources/rapidoid-classes.txt | 2 + .../rapidoid/config/RefreshingConfigTest.java | 2 +- .../main/java/org/rapidoid/http/FastHttp.java | 3 +- .../java/org/rapidoid/http/HttpUtils.java | 19 +- .../defaults/DefaultErrorHandler.java | 28 +- .../java/org/rapidoid/http/impl/ReqImpl.java | 2 +- .../java/org/rapidoid/http/impl/RespImpl.java | 28 +- .../java/org/rapidoid/http/HttpZoneTest.java | 47 +++ .../rapidoid/httpfast/HttpHandlerTest.java | 4 +- .../HttpZoneTest/testMainZone/GET_a | 8 + .../HttpZoneTest/testMainZone/GET_b | 8 + .../HttpZoneTest/testMainZone/GET_c | 8 + .../org/rapidoid/goodies/ConfigHandler.java | 6 + .../java/org/rapidoid/goodies/Goodies.java | 5 +- 26 files changed, 656 insertions(+), 409 deletions(-) create mode 100644 rapidoid-commons/src/main/java/org/rapidoid/config/BasicConfig.java create mode 100644 rapidoid-commons/src/main/java/org/rapidoid/config/ConfigImpl.java rename rapidoid-commons/src/main/resources/{default/config-default.yml => default-config-default.yml} (100%) rename rapidoid-commons/src/main/resources/{default/config-dev.yml => default-config-dev.yml} (100%) rename rapidoid-commons/src/main/resources/{default/config-mysql.yml => default-config-mysql.yml} (100%) rename rapidoid-commons/src/main/resources/{default/config-test.yml => default-config-test.yml} (100%) rename rapidoid-commons/src/main/resources/{default/config.yml => default-config.yml} (55%) create mode 100644 rapidoid-integration-tests/src/test/java/org/rapidoid/http/HttpZoneTest.java create mode 100644 rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_a create mode 100644 rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_b create mode 100644 rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_c diff --git a/rapidoid-commons/src/main/java/org/rapidoid/config/BasicConfig.java b/rapidoid-commons/src/main/java/org/rapidoid/config/BasicConfig.java new file mode 100644 index 0000000000..822f848c9b --- /dev/null +++ b/rapidoid-commons/src/main/java/org/rapidoid/config/BasicConfig.java @@ -0,0 +1,38 @@ +package org.rapidoid.config; + +/* + * #%L + * rapidoid-commons + * %% + * Copyright (C) 2014 - 2016 Nikolche Mihajlovski and contributors + * %% + * Licensed 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. + * #L% + */ + +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; +import org.rapidoid.lambda.ToMap; +import org.rapidoid.value.Value; + +@Authors("Nikolche Mihajlovski") +@Since("5.2.0") +public interface BasicConfig extends ToMap { + + Value entry(String key); + + boolean has(String key); + + BasicConfig sub(String... keys); + +} diff --git a/rapidoid-commons/src/main/java/org/rapidoid/config/Conf.java b/rapidoid-commons/src/main/java/org/rapidoid/config/Conf.java index 3a8e1ab885..ebba2635a4 100644 --- a/rapidoid-commons/src/main/java/org/rapidoid/config/Conf.java +++ b/rapidoid-commons/src/main/java/org/rapidoid/config/Conf.java @@ -46,7 +46,7 @@ public Config map(String name) throws Exception { } }); - public static final Config ROOT = new Config(); + public static final Config ROOT = new ConfigImpl(); public static final Config USERS = section("users"); public static final Config JOBS = section("jobs"); @@ -160,16 +160,20 @@ public static synchronized void reload() { ROOT.clear(); + loadDefaultConfig(); + + loadConfig(detached); + + ROOT.args(args); + + applyConfig(); + } + + public static void loadConfig(List> detached) { String filenameBase = U.or(ROOT.filenameBase(), "config"); String configFilenamePattern = filenameBase + ".y?ml"; String configProfilePattern = filenameBase + "-%s.y?ml"; - ConfigUtil.load(Msc.path("default", configFilenamePattern), ROOT, true); - - for (String profile : Env.profiles()) { - ConfigUtil.load(Msc.path("default", U.frmt(configProfilePattern, profile)), ROOT, true); - } - ConfigUtil.load(Msc.path(path, configFilenamePattern), ROOT, false); for (String profile : Env.profiles()) { @@ -179,10 +183,14 @@ public static synchronized void reload() { for (List keys : detached) { autoRefresh(keys.isEmpty() ? ROOT : ROOT.sub(keys)); } + } - ROOT.args(args); + public static void loadDefaultConfig() { + ConfigUtil.load("default-config.yml", ROOT, true); - applyConfig(); + for (String profile : Env.profiles()) { + ConfigUtil.load(U.frmt("default-config-%s.yml", profile), ROOT, true); + } } private static void autoRefresh(Config... configs) { diff --git a/rapidoid-commons/src/main/java/org/rapidoid/config/Config.java b/rapidoid-commons/src/main/java/org/rapidoid/config/Config.java index d30861d213..027f2adbeb 100644 --- a/rapidoid-commons/src/main/java/org/rapidoid/config/Config.java +++ b/rapidoid-commons/src/main/java/org/rapidoid/config/Config.java @@ -1,19 +1,5 @@ package org.rapidoid.config; -import org.rapidoid.RapidoidThing; -import org.rapidoid.annotation.Authors; -import org.rapidoid.annotation.Since; -import org.rapidoid.cls.Cls; -import org.rapidoid.commons.Arr; -import org.rapidoid.collection.Coll; -import org.rapidoid.lambda.ToMap; -import org.rapidoid.log.Log; -import org.rapidoid.u.U; -import org.rapidoid.value.Value; -import org.rapidoid.value.Values; - -import java.util.*; - /* * #%L * rapidoid-commons @@ -34,333 +20,61 @@ * #L% */ -@Authors("Nikolche Mihajlovski") -@Since("4.1.0") -public class Config extends RapidoidThing implements ToMap { - - private final Map properties; - - private final List baseKeys; - - private final Config root; - - private final boolean isRoot; - - private volatile String filenameBase = "config"; - - private Config(Map properties, List baseKeys, Config root) { - this.properties = properties; - this.root = root; - this.baseKeys = Collections.unmodifiableList(U.list(baseKeys)); - this.isRoot = false; - } - - public Config() { - this.properties = Coll.synchronizedMap(); - this.root = this; - this.baseKeys = U.list(); - this.isRoot = true; - } - - public Value entry(String key) { - return Values.wrap(new ConfigValueStore(this, key)); - } - - private List keyChain(Iterator keys) { - List keyChain = U.list(this.baseKeys); +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; - while (keys.hasNext()) { - String key = keys.next(); - U.notNull(key, "config key"); - Collections.addAll(keyChain, key.split("\\.")); - } +import java.util.List; +import java.util.Map; +import java.util.Properties; - return keyChain; - } +@Authors("Nikolche Mihajlovski") +@Since("4.1.0") +public interface Config extends BasicConfig { @SuppressWarnings("unchecked") - public Config sub(String... keys) { - U.must(U.notEmpty(keys), "Keys must be specified!"); - return new Config(properties, keyChain(U.iterator(keys)), root()); - } - - public Config sub(List keys) { - U.must(U.notEmpty(keys), "Keys must be specified!"); - return new Config(properties, keyChain(keys.iterator()), root()); - } - - public Object get(String key) { - Object value; - - synchronized (properties) { - value = asMap().get(key); - } - - return value != null ? value : global(key); - } - - private String global(String key) { - String fullKey = fullKey(key, "."); - - String value = System.getProperty(fullKey); - - if (value == null) { - value = System.getenv(fullKey); - } - - if (value == null) { - value = System.getenv(fullKey(key, "_").toUpperCase()); - } - - if (value == null) { - value = System.getenv(fullKey(key, "_").toLowerCase()); - } - - return value; - } - - private String fullKey(String key, String separator) { - return U.join(separator, baseKeys) + separator + key; - } + Config sub(String... keys); - public boolean has(String key) { - synchronized (properties) { - return asMap().containsKey(key); - } - } + Config sub(List keys); - public boolean is(String key) { - Object value; + Object get(String key); - synchronized (properties) { - value = asMap().get(key); - } - - return Boolean.TRUE.equals(Cls.convert(value, Boolean.class)); - } + boolean is(String key); @Override - public Map toMap() { - return Collections.unmodifiableMap(asMap()); - } - - private Map asMap() { - if (isRoot) { - return properties; - - } else { - synchronized (properties) { - Map props = properties; - - for (String key : baseKeys) { - Object value = props.get(key); - - if (value == null) { - value = Coll.synchronizedMap(); - props.put(key, value); - } - - if (value instanceof Map) { - props = (Map) value; - } else { - throw U.rte("Expected a Map for configuration section '%s', but found value of type: %s", - sectionTo(key), value.getClass().getSimpleName()); - } - } - - return props; - } - } - } - - private String sectionTo(String toKey) { - String section = ""; + Map toMap(); - for (String key : baseKeys) { - if (!section.isEmpty()) { - section += "."; - } + void clear(); - section += key; + void delete(); - if (key.equals(toKey)) { - break; - } - } + void set(String key, Object value); - return section; - } + void remove(String key); - public void clear() { - if (isRoot) { - properties.clear(); - } else { - synchronized (properties) { - asMap().clear(); - } - } - } + void assign(Map entries); - public void delete() { - if (isRoot) { - properties.clear(); - } else { - synchronized (properties) { - parent().remove(lastBaseKey()); - } - } - } + boolean isEmpty(); - private String lastBaseKey() { - return baseKeys.get(baseKeys.size() - 1); - } - - public void set(String key, Object value) { - synchronized (properties) { - asMap().put(key, value); - } - } - - public void remove(String key) { - synchronized (properties) { - asMap().remove(key); - } - } - - public void assign(Map entries) { - synchronized (properties) { - clear(); - update(entries); - } - } - - public boolean isEmpty() { - synchronized (properties) { - return asMap().isEmpty(); - } - } - - public void update(Map entries) { - update(entries, false); - } + void update(Map entries); @SuppressWarnings("unchecked") - public void update(Map entries, boolean overridenByEnv) { - synchronized (properties) { - Map conf = asMap(); - - for (Map.Entry e : entries.entrySet()) { - String name = e.getKey(); - Object value = e.getValue(); - - if (value instanceof Map) { - sub(name).update((Map) value, overridenByEnv); - - } else { - if (overridenByEnv) { - value = U.or(global(name), value); - } - - conf.put(name, value); - } - } - } - } - - @Override - public String toString() { - synchronized (properties) { - return asMap().toString(); - } - } - - public void args(String... args) { - if (U.notEmpty(args)) { - for (String arg : args) { - if (!arg.contains("->")) { - String[] parts = arg.split("=", 2); - String name = parts[0]; - - if (parts.length > 1) { - String value = parts[1]; - - if (name.equals("config")) { - filenameBase(value); - } - - setNested(name, value); - } else { - setNested(name, true); - } - } - } - } - } - - private void setNested(String key, Object value) { - String[] keys = key.split("\\."); - Config cfg = keys.length > 1 ? sub(Arr.sub(keys, 0, -1)) : this; - cfg.set(U.last(keys), value); - } - - public Config root() { - return root; - } - - public Config parent() { - return isRoot ? null : root.sub(baseKeys.subList(0, baseKeys.size() - 1)); - } - - public List keys() { - return baseKeys; - } - - public Map toFlatMap() { - Map flatMap = U.map(); - Map map = toMap(); - - traverseToFlat(map, U.list(keys()), flatMap); + void update(Map entries, boolean overridenByEnv); - return flatMap; - } + void args(String... args); - private static void traverseToFlat(Map map, List keys, Map flatMap) { - for (Map.Entry e : map.entrySet()) { - String key = e.getKey(); - Object val = e.getValue(); + Config root(); - if (val instanceof Map) { - Map mapVal = (Map) val; - List keys2 = U.list(keys); - keys2.add(key); - traverseToFlat(mapVal, keys2, flatMap); + Config parent(); - } else { - flatMap.put(U.join(".", keys) + "." + key, String.valueOf(val)); - } - } - } + List keys(); - public Properties toProperties() { - Properties props = new Properties(); - props.putAll(toFlatMap()); - return props; - } + Map toFlatMap(); - public ConfigAlternatives or(Config alternative) { - return new ConfigAlternatives(this, alternative); - } + Properties toProperties(); - public String filenameBase() { - return filenameBase; - } + BasicConfig or(Config alternative); - public synchronized Config filenameBase(String filenameBase) { - if (U.neq(this.filenameBase, filenameBase)) { - Log.info("Changing configuration filename base", "!from", this.filenameBase, "!to", filenameBase); - } + String filenameBase(); - this.filenameBase = filenameBase; - return this; - } + Config filenameBase(String filenameBase); } diff --git a/rapidoid-commons/src/main/java/org/rapidoid/config/ConfigAlternatives.java b/rapidoid-commons/src/main/java/org/rapidoid/config/ConfigAlternatives.java index 252762fa4d..ee2ad3be4e 100644 --- a/rapidoid-commons/src/main/java/org/rapidoid/config/ConfigAlternatives.java +++ b/rapidoid-commons/src/main/java/org/rapidoid/config/ConfigAlternatives.java @@ -23,11 +23,14 @@ import org.rapidoid.RapidoidThing; import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Since; +import org.rapidoid.u.U; import org.rapidoid.value.Value; +import java.util.Map; + @Authors("Nikolche Mihajlovski") @Since("5.1.0") -public class ConfigAlternatives extends RapidoidThing { +public class ConfigAlternatives extends RapidoidThing implements BasicConfig { private final Config primary; @@ -38,15 +41,28 @@ public ConfigAlternatives(Config primary, Config alternative) { this.alternative = alternative; } + @Override public Value entry(String key) { return primary.entry(key).orElse(alternative.entry(key)); } + @Override public boolean has(String key) { return primary.has(key) || alternative.has(key); } - public Config sub(String key) { + @Override + public BasicConfig sub(String... keys) { + U.must(keys.length == 1, "Currently supporting only 1 key!"); + String key = keys[0]; + return primary.has(key) ? primary.sub(key) : alternative.sub(key); } + + @Override + public Map toMap() { + Map map = U.map(alternative.toMap()); + map.putAll(primary.toMap()); + return map; + } } diff --git a/rapidoid-commons/src/main/java/org/rapidoid/config/ConfigImpl.java b/rapidoid-commons/src/main/java/org/rapidoid/config/ConfigImpl.java new file mode 100644 index 0000000000..479774b93c --- /dev/null +++ b/rapidoid-commons/src/main/java/org/rapidoid/config/ConfigImpl.java @@ -0,0 +1,388 @@ +package org.rapidoid.config; + +import org.rapidoid.RapidoidThing; +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; +import org.rapidoid.cls.Cls; +import org.rapidoid.commons.Arr; +import org.rapidoid.collection.Coll; +import org.rapidoid.log.Log; +import org.rapidoid.u.U; +import org.rapidoid.value.Value; +import org.rapidoid.value.Values; + +import java.util.*; + +/* + * #%L + * rapidoid-commons + * %% + * Copyright (C) 2014 - 2016 Nikolche Mihajlovski and contributors + * %% + * Licensed 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. + * #L% + */ + +@Authors("Nikolche Mihajlovski") +@Since("4.1.0") +public class ConfigImpl extends RapidoidThing implements Config { + + private final Map properties; + + private final List baseKeys; + + private final Config root; + + private final boolean isRoot; + + private volatile String filenameBase = "config"; + + private ConfigImpl(Map properties, List baseKeys, Config root) { + this.properties = properties; + this.root = root; + this.baseKeys = Collections.unmodifiableList(U.list(baseKeys)); + this.isRoot = false; + } + + public ConfigImpl() { + this.properties = Coll.synchronizedMap(); + this.root = this; + this.baseKeys = U.list(); + this.isRoot = true; + } + + @Override + public Value entry(String key) { + return Values.wrap(new ConfigValueStore(this, key)); + } + + private List keyChain(Iterator keys) { + List keyChain = U.list(this.baseKeys); + + while (keys.hasNext()) { + String key = keys.next(); + U.notNull(key, "config key"); + Collections.addAll(keyChain, key.split("\\.")); + } + + return keyChain; + } + + @Override + @SuppressWarnings("unchecked") + public Config sub(String... keys) { + U.must(U.notEmpty(keys), "Keys must be specified!"); + return new ConfigImpl(properties, keyChain(U.iterator(keys)), root()); + } + + @Override + public Config sub(List keys) { + U.must(U.notEmpty(keys), "Keys must be specified!"); + return new ConfigImpl(properties, keyChain(keys.iterator()), root()); + } + + @Override + public Object get(String key) { + Object value; + + synchronized (properties) { + value = asMap().get(key); + } + + return value != null ? value : global(key); + } + + private String global(String key) { + String fullKey = fullKey(key, "."); + + String value = System.getProperty(fullKey); + + if (value == null) { + value = System.getenv(fullKey); + } + + if (value == null) { + value = System.getenv(fullKey(key, "_").toUpperCase()); + } + + if (value == null) { + value = System.getenv(fullKey(key, "_").toLowerCase()); + } + + return value; + } + + private String fullKey(String key, String separator) { + return U.join(separator, baseKeys) + separator + key; + } + + @Override + public boolean has(String key) { + synchronized (properties) { + return asMap().containsKey(key); + } + } + + @Override + public boolean is(String key) { + Object value; + + synchronized (properties) { + value = asMap().get(key); + } + + return Boolean.TRUE.equals(Cls.convert(value, Boolean.class)); + } + + @Override + public Map toMap() { + return Collections.unmodifiableMap(asMap()); + } + + private Map asMap() { + if (isRoot) { + return properties; + + } else { + synchronized (properties) { + Map props = properties; + + for (String key : baseKeys) { + Object value = props.get(key); + + if (value == null) { + value = Coll.synchronizedMap(); + props.put(key, value); + } + + if (value instanceof Map) { + props = (Map) value; + } else { + throw U.rte("Expected a Map for configuration section '%s', but found value of type: %s", + sectionTo(key), value.getClass().getSimpleName()); + } + } + + return props; + } + } + } + + private String sectionTo(String toKey) { + String section = ""; + + for (String key : baseKeys) { + if (!section.isEmpty()) { + section += "."; + } + + section += key; + + if (key.equals(toKey)) { + break; + } + } + + return section; + } + + @Override + public void clear() { + if (isRoot) { + properties.clear(); + } else { + synchronized (properties) { + asMap().clear(); + } + } + } + + @Override + public void delete() { + if (isRoot) { + properties.clear(); + } else { + synchronized (properties) { + parent().remove(lastBaseKey()); + } + } + } + + private String lastBaseKey() { + return baseKeys.get(baseKeys.size() - 1); + } + + @Override + public void set(String key, Object value) { + synchronized (properties) { + asMap().put(key, value); + } + } + + @Override + public void remove(String key) { + synchronized (properties) { + asMap().remove(key); + } + } + + @Override + public void assign(Map entries) { + synchronized (properties) { + clear(); + update(entries); + } + } + + @Override + public boolean isEmpty() { + synchronized (properties) { + return asMap().isEmpty(); + } + } + + @Override + public void update(Map entries) { + update(entries, false); + } + + @Override + @SuppressWarnings("unchecked") + public void update(Map entries, boolean overridenByEnv) { + synchronized (properties) { + Map conf = asMap(); + + for (Map.Entry e : entries.entrySet()) { + String name = e.getKey(); + Object value = e.getValue(); + + if (value instanceof Map) { + sub(name).update((Map) value, overridenByEnv); + + } else { + if (overridenByEnv) { + value = U.or(global(name), value); + } + + conf.put(name, value); + } + } + } + } + + @Override + public String toString() { + synchronized (properties) { + return asMap().toString(); + } + } + + @Override + public void args(String... args) { + if (U.notEmpty(args)) { + for (String arg : args) { + if (!arg.contains("->")) { + String[] parts = arg.split("=", 2); + String name = parts[0]; + + if (parts.length > 1) { + String value = parts[1]; + + if (name.equals("config")) { + filenameBase(value); + } + + setNested(name, value); + } else { + setNested(name, true); + } + } + } + } + } + + private void setNested(String key, Object value) { + String[] keys = key.split("\\."); + Config cfg = keys.length > 1 ? sub(Arr.sub(keys, 0, -1)) : this; + cfg.set(U.last(keys), value); + } + + @Override + public Config root() { + return root; + } + + @Override + public Config parent() { + return isRoot ? null : root.sub(baseKeys.subList(0, baseKeys.size() - 1)); + } + + @Override + public List keys() { + return baseKeys; + } + + @Override + public Map toFlatMap() { + Map flatMap = U.map(); + Map map = toMap(); + + traverseToFlat(map, U.list(keys()), flatMap); + + return flatMap; + } + + private static void traverseToFlat(Map map, List keys, Map flatMap) { + for (Map.Entry e : map.entrySet()) { + String key = e.getKey(); + Object val = e.getValue(); + + if (val instanceof Map) { + Map mapVal = (Map) val; + List keys2 = U.list(keys); + keys2.add(key); + traverseToFlat(mapVal, keys2, flatMap); + + } else { + flatMap.put(U.join(".", keys) + "." + key, String.valueOf(val)); + } + } + } + + @Override + public Properties toProperties() { + Properties props = new Properties(); + props.putAll(toFlatMap()); + return props; + } + + @Override + public ConfigAlternatives or(Config alternative) { + return new ConfigAlternatives(this, alternative); + } + + @Override + public String filenameBase() { + return filenameBase; + } + + @Override + public synchronized Config filenameBase(String filenameBase) { + if (U.neq(this.filenameBase, filenameBase)) { + Log.info("Changing configuration filename base", "!from", this.filenameBase, "!to", filenameBase); + } + + this.filenameBase = filenameBase; + return this; + } +} diff --git a/rapidoid-commons/src/main/java/org/rapidoid/util/Msc.java b/rapidoid-commons/src/main/java/org/rapidoid/util/Msc.java index adbbed63c3..ed02fcf879 100644 --- a/rapidoid-commons/src/main/java/org/rapidoid/util/Msc.java +++ b/rapidoid-commons/src/main/java/org/rapidoid/util/Msc.java @@ -921,11 +921,11 @@ public static Map protectSensitiveInfo(Map data, T rep String key = e.getKey().toLowerCase(); - if (key.contains("password") || key.contains("secret") || key.contains("token") || key.contains("private")) { - value = replacement; - - } else if (value instanceof Map) { + if (value instanceof Map) { value = (T) protectSensitiveInfo((Map) value, replacement); + + } else if (sensitiveKey(key)) { + value = replacement; } copy.put(e.getKey(), value); @@ -934,6 +934,10 @@ public static Map protectSensitiveInfo(Map data, T rep return copy; } + public static boolean sensitiveKey(String key) { + return key.contains("password") || key.contains("secret") || key.contains("token") || key.contains("private"); + } + public static int processId() { return U.num(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); } diff --git a/rapidoid-commons/src/main/java/org/rapidoid/web/ScreenBean.java b/rapidoid-commons/src/main/java/org/rapidoid/web/ScreenBean.java index 91eff58e9d..5cd984c4e5 100644 --- a/rapidoid-commons/src/main/java/org/rapidoid/web/ScreenBean.java +++ b/rapidoid-commons/src/main/java/org/rapidoid/web/ScreenBean.java @@ -43,7 +43,7 @@ public class ScreenBean extends RapidoidThing implements Screen { private volatile boolean search; private volatile boolean navbar = true; private volatile boolean fluid; - private volatile boolean cdn = !Env.dev(); + private volatile boolean cdn = Env.production(); private final Set js = Coll.synchronizedSet(); private final Set css = Coll.synchronizedSet(); diff --git a/rapidoid-commons/src/main/resources/default/config-default.yml b/rapidoid-commons/src/main/resources/default-config-default.yml similarity index 100% rename from rapidoid-commons/src/main/resources/default/config-default.yml rename to rapidoid-commons/src/main/resources/default-config-default.yml diff --git a/rapidoid-commons/src/main/resources/default/config-dev.yml b/rapidoid-commons/src/main/resources/default-config-dev.yml similarity index 100% rename from rapidoid-commons/src/main/resources/default/config-dev.yml rename to rapidoid-commons/src/main/resources/default-config-dev.yml diff --git a/rapidoid-commons/src/main/resources/default/config-mysql.yml b/rapidoid-commons/src/main/resources/default-config-mysql.yml similarity index 100% rename from rapidoid-commons/src/main/resources/default/config-mysql.yml rename to rapidoid-commons/src/main/resources/default-config-mysql.yml diff --git a/rapidoid-commons/src/main/resources/default/config-test.yml b/rapidoid-commons/src/main/resources/default-config-test.yml similarity index 100% rename from rapidoid-commons/src/main/resources/default/config-test.yml rename to rapidoid-commons/src/main/resources/default-config-test.yml diff --git a/rapidoid-commons/src/main/resources/default/config.yml b/rapidoid-commons/src/main/resources/default-config.yml similarity index 55% rename from rapidoid-commons/src/main/resources/default/config.yml rename to rapidoid-commons/src/main/resources/default-config.yml index d1951146e6..093f553731 100644 --- a/rapidoid-commons/src/main/resources/default/config.yml +++ b/rapidoid-commons/src/main/resources/default-config.yml @@ -10,39 +10,44 @@ app: search: false navbar: true fluid: false - cdn: false + cdn: auto contextPath: '' home: / menu: {} - segments: - admin: - home: /_ - brand: ' Admin' - search: false - menu: - Overview: /_ - Application: - Routes: /_/routes - Beans: /_/beans - Configuration: /_/config - Deploy: /_/deploy - Terminate / Restart: /_/terminate - Data: - Entities: /_/entities - Details: - Metrics: /_/metrics - Classpath: /_/classpath - JMX: - Memory pool: /_/jmx/mempool - JVM Threads: /_/jmx/threads - Operating system: /_/jmx/os - Garbage collection: /_/jmx/gc - Memory: /_/jmx/memory - Runtime: /_/jmx/runtime - Classes: /_/jmx/classes - Compilation: /_/jmx/compilation -users: {} +main-zone: {} + +admin-zone: + home: /_ + brand: ' Admin' + search: false + fluid: false + menu: + Overview: /_ + Application: + Routes: /_/routes + Beans: /_/beans + Configuration: /_/config + Deploy: /_/deploy + Terminate / Restart: /_/terminate + Data: + Entities: /_/entities + Details: + Metrics: /_/metrics + Classpath: /_/classpath + JMX: + Memory pool: /_/jmx/mempool + JVM Threads: /_/jmx/threads + Operating system: /_/jmx/os + Garbage collection: /_/jmx/gc + Memory: /_/jmx/memory + Runtime: /_/jmx/runtime + Classes: /_/jmx/classes + Compilation: /_/jmx/compilation + +users: + admin: + roles: administrator jobs: executor: diff --git a/rapidoid-commons/src/main/resources/rapidoid-classes.txt b/rapidoid-commons/src/main/resources/rapidoid-classes.txt index e35bfb8295..6b8e8096f7 100644 --- a/rapidoid-commons/src/main/resources/rapidoid-classes.txt +++ b/rapidoid-commons/src/main/resources/rapidoid-classes.txt @@ -105,10 +105,12 @@ org.rapidoid.concurrent.impl.FutureImpl org.rapidoid.concurrent.impl.PromiseImpl org.rapidoid.concurrent.Promise org.rapidoid.concurrent.Promises +org.rapidoid.config.BasicConfig org.rapidoid.config.Conf org.rapidoid.config.Config org.rapidoid.config.ConfigAlternatives org.rapidoid.config.ConfigHelp +org.rapidoid.config.ConfigImpl org.rapidoid.config.ConfigOption org.rapidoid.config.ConfigOptions org.rapidoid.config.ConfigParser diff --git a/rapidoid-commons/src/test/java/org/rapidoid/config/RefreshingConfigTest.java b/rapidoid-commons/src/test/java/org/rapidoid/config/RefreshingConfigTest.java index d061fe5cfc..7000433bcf 100644 --- a/rapidoid-commons/src/test/java/org/rapidoid/config/RefreshingConfigTest.java +++ b/rapidoid-commons/src/test/java/org/rapidoid/config/RefreshingConfigTest.java @@ -39,7 +39,7 @@ public void testRefreshingConfig() { File tmp = createTempFile(); Log.info("Created temporary file", "file", tmp); - Config config = new Config(); + Config config = new ConfigImpl(); ConfigUtil.autoRefresh(config, tmp.getAbsolutePath()); eq(config.toMap(), U.map()); diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java index 36925c6749..43168b0508 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java @@ -6,6 +6,7 @@ import org.rapidoid.bytes.BytesUtil; import org.rapidoid.collection.Coll; import org.rapidoid.config.Config; +import org.rapidoid.config.ConfigImpl; import org.rapidoid.data.BufRange; import org.rapidoid.data.BufRanges; import org.rapidoid.data.KeyValueRanges; @@ -58,7 +59,7 @@ public class FastHttp extends AbstractHttpProcessor { private final Map attributes = Coll.synchronizedMap(); public FastHttp(HttpRoutesImpl... routeGroups) { - this(routeGroups, new Config()); + this(routeGroups, new ConfigImpl()); } public FastHttp(HttpRoutesImpl[] routeGroups, Config serverConfig) { diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/HttpUtils.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/HttpUtils.java index 1944afde7d..d324e7bd2c 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/HttpUtils.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/HttpUtils.java @@ -4,6 +4,7 @@ import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Since; import org.rapidoid.commons.Str; +import org.rapidoid.config.BasicConfig; import org.rapidoid.config.Conf; import org.rapidoid.config.Config; import org.rapidoid.crypto.Crypto; @@ -331,15 +332,18 @@ public static void resultToResponse(Req req, Object result) { } } - public static String getContextPath(Customization customization, String segment) { - Config cfg = customization.appConfig(); + public static String getContextPath(Req req) { + return zone(req).entry("contextPath").or(""); + } - if (segment != null) { - return cfg.or(cfg.sub("segments", segment)).entry("contextPath").or(""); + public static Config zone(Customization custom, String segment) { + return segment != null ? custom.appConfig().root().sub(segment + "-zone") : custom.appConfig(); + } - } else { - return cfg.entry("contextPath").or(""); - } + public static BasicConfig zone(Req req) { + Customization custom = Customization.of(req); + Config zone = zone(custom, req.segment()); + return zone.or(custom.appConfig()); } @SuppressWarnings("unchecked") @@ -378,5 +382,4 @@ public static void clearUserData(Req req) { token.remove(_EXPIRES); } } - } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/customize/defaults/DefaultErrorHandler.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/customize/defaults/DefaultErrorHandler.java index a511fdfef3..396ef00787 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/customize/defaults/DefaultErrorHandler.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/customize/defaults/DefaultErrorHandler.java @@ -3,18 +3,13 @@ import org.rapidoid.RapidoidThing; import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Since; -import org.rapidoid.http.MediaType; -import org.rapidoid.config.Config; -import org.rapidoid.http.HttpUtils; -import org.rapidoid.http.NotFound; -import org.rapidoid.http.Req; -import org.rapidoid.http.Resp; +import org.rapidoid.config.BasicConfig; +import org.rapidoid.http.*; import org.rapidoid.http.customize.Customization; import org.rapidoid.http.customize.ErrorHandler; import org.rapidoid.log.Log; import org.rapidoid.u.U; import org.rapidoid.util.Msc; -import org.rapidoid.value.Value; import java.util.Collections; import java.util.Map; @@ -52,14 +47,15 @@ public Object handleError(Req req, Resp resp, Throwable error) { if (result instanceof Throwable) { Throwable errResult = (Throwable) result; - return renderError(req, resp, errResult, custom); + return renderError(req, resp, errResult); } else { return result; } } - private Object renderError(Req req, Resp resp, Throwable error, Customization custom) { + private Object renderError(Req req, Resp resp, Throwable error) { + if (resp.contentType() == MediaType.JSON_UTF_8) { return HttpUtils.getErrorInfo(resp, error); @@ -67,7 +63,7 @@ private Object renderError(Req req, Resp resp, Throwable error, Customization cu return HttpUtils.getErrorMessageAndSetCode(resp, error); } else { - return page(req, resp, error, custom); + return page(req, resp, error); } } @@ -134,21 +130,19 @@ protected Object defaultErrorHandling(Req req, Throwable error) { return error; } - protected Object page(Req req, Resp resp, Throwable error, Customization custom) { - - Config segments = custom.appConfig().sub("segments"); - Value home = custom.appConfig().sub("app").entry("home").str(); + protected Object page(Req req, Resp resp, Throwable error) { if (error instanceof SecurityException) { return resp.code(403).view("login").mvc(true).model("embedded", req.attr("_embedded", false)); + } else { - String seg = req.segment(); - String homeUri = seg != null ? segments.sub(seg).entry("home").str().orElse(home).or("/") : "/"; + BasicConfig zone = HttpUtils.zone(req); + String home = zone.entry("home").or("/"); Map errorInfo = HttpUtils.getErrorInfo(resp, error); resp.model().put("error", errorInfo); - resp.model().put("home", homeUri); + resp.model().put("home", home); return resp.mvc(true).view("error"); } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java index a7587676aa..26a46e1bcb 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/ReqImpl.java @@ -750,7 +750,7 @@ public String contextPath() { if (contextPath == null) { synchronized (this) { if (contextPath == null) { - contextPath = HttpUtils.getContextPath(Customization.of(this), segment()); + contextPath = HttpUtils.getContextPath(this); } } } diff --git a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java index c1f54a0067..5ef9e53a29 100644 --- a/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java +++ b/rapidoid-http-fast/src/main/java/org/rapidoid/http/impl/RespImpl.java @@ -5,13 +5,12 @@ import org.rapidoid.annotation.Since; import org.rapidoid.cls.Cls; import org.rapidoid.collection.Coll; -import org.rapidoid.http.MediaType; +import org.rapidoid.config.BasicConfig; import org.rapidoid.config.Conf; -import org.rapidoid.config.Config; -import org.rapidoid.config.ConfigAlternatives; import org.rapidoid.ctx.Ctxs; import org.rapidoid.ctx.UserInfo; import org.rapidoid.http.HttpUtils; +import org.rapidoid.http.MediaType; import org.rapidoid.http.Req; import org.rapidoid.http.Resp; import org.rapidoid.http.customize.Customization; @@ -398,13 +397,10 @@ private Screen createScreen() { } private void initScreen(Screen screen) { - Config app = Customization.of(req).appConfig(); - Config segments = app.sub("segments"); - Config segment = segments.sub(req.segment()); - ConfigAlternatives cfg = segment.or(app); + BasicConfig zone = HttpUtils.zone(req); - Object brand = cfg.entry("brand").str().getOrNull(); - String title = cfg.entry("title").str().getOrNull(); + String brand = zone.entry("brand").str().getOrNull(); + String title = zone.entry("title").str().getOrNull(); String siteName = req.host(); if (U.isEmpty(siteName) @@ -416,19 +412,19 @@ private void initScreen(Screen screen) { screen.brand(U.or(brand, siteName)); screen.title(U.or(title, siteName)); - screen.home(cfg.entry("home").str().or("/")); + screen.home(zone.entry("home").str().or("/")); - screen.search(cfg.entry("search").bool().or(false)); - screen.navbar(cfg.entry("navbar").bool().or(brand != null)); - screen.fluid(cfg.entry("fluid").bool().or(false)); + screen.search(zone.entry("search").bool().or(false)); + screen.navbar(zone.entry("navbar").bool().or(brand != null)); + screen.fluid(zone.entry("fluid").bool().or(false)); - String cdn = cfg.entry("cdn").str().or("auto"); + String cdn = zone.entry("cdn").or("auto"); if (!"auto".equalsIgnoreCase(cdn)) { screen.cdn(Cls.bool(cdn)); } - if (cfg.has("menu")) { - screen.menu(cfg.sub("menu").toMap()); + if (zone.has("menu")) { + screen.menu(zone.sub("menu").toMap()); } } diff --git a/rapidoid-integration-tests/src/test/java/org/rapidoid/http/HttpZoneTest.java b/rapidoid-integration-tests/src/test/java/org/rapidoid/http/HttpZoneTest.java new file mode 100644 index 0000000000..5dd6b1c825 --- /dev/null +++ b/rapidoid-integration-tests/src/test/java/org/rapidoid/http/HttpZoneTest.java @@ -0,0 +1,47 @@ +package org.rapidoid.http; + +/* + * #%L + * rapidoid-integration-tests + * %% + * Copyright (C) 2014 - 2016 Nikolche Mihajlovski and contributors + * %% + * Licensed 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. + * #L% + */ + +import org.junit.Test; +import org.rapidoid.annotation.Authors; +import org.rapidoid.annotation.Since; +import org.rapidoid.setup.On; + +import java.util.TreeMap; + +@Authors("Nikolche Mihajlovski") +@Since("5.2.0") +public class HttpZoneTest extends IntegrationTestCommons { + + @Test + public void testMainZone() { + ReqHandler zoneHandler = req -> new TreeMap<>(HttpUtils.zone(req).toMap()); + + On.get("/a").json(zoneHandler); + On.get("/b").segment("admin").json(zoneHandler); + On.get("/c").segment("other").json(zoneHandler); + + onlyGet("/a"); + onlyGet("/b"); + onlyGet("/c"); + } + +} diff --git a/rapidoid-integration-tests/src/test/java/org/rapidoid/httpfast/HttpHandlerTest.java b/rapidoid-integration-tests/src/test/java/org/rapidoid/httpfast/HttpHandlerTest.java index e56a3a8674..077d91d0cf 100644 --- a/rapidoid-integration-tests/src/test/java/org/rapidoid/httpfast/HttpHandlerTest.java +++ b/rapidoid-integration-tests/src/test/java/org/rapidoid/httpfast/HttpHandlerTest.java @@ -23,7 +23,7 @@ import org.junit.Test; import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Since; -import org.rapidoid.config.Config; +import org.rapidoid.config.ConfigImpl; import org.rapidoid.http.FastHttp; import org.rapidoid.http.IntegrationTestCommons; import org.rapidoid.http.Req; @@ -39,7 +39,7 @@ public class HttpHandlerTest extends IntegrationTestCommons { @Test public void testFastHttpHandler() { - Customization customization = new Customization("example", My.custom(), new Config(), new Config()); + Customization customization = new Customization("example", My.custom(), new ConfigImpl(), new ConfigImpl()); HttpRoutesImpl routes = new HttpRoutesImpl(customization); FastHttp http = new FastHttp(routes); diff --git a/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_a b/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_a new file mode 100644 index 0000000000..0a4e78cbd3 --- /dev/null +++ b/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_a @@ -0,0 +1,8 @@ +HTTP/1.1 200 OK +Connection: keep-alive +Server: Rapidoid +Date: XXXXX GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 95 + +{"cdn":"auto","contextPath":"","fluid":false,"home":"/","menu":{},"navbar":true,"search":false} \ No newline at end of file diff --git a/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_b b/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_b new file mode 100644 index 0000000000..80bf327450 --- /dev/null +++ b/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_b @@ -0,0 +1,8 @@ +HTTP/1.1 200 OK +Connection: keep-alive +Server: Rapidoid +Date: XXXXX GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 647 + +{"brand":" Admin","cdn":"auto","contextPath":"","fluid":false,"home":"/_","menu":{"Overview":"/_","Application":{"Routes":"/_/routes","Beans":"/_/beans","Configuration":"/_/config","Deploy":"/_/deploy","Terminate / Restart":"/_/terminate"},"Data":{"Entities":"/_/entities"},"Details":{"Metrics":"/_/metrics","Classpath":"/_/classpath"},"JMX":{"Memory pool":"/_/jmx/mempool","JVM Threads":"/_/jmx/threads","Operating system":"/_/jmx/os","Garbage collection":"/_/jmx/gc","Memory":"/_/jmx/memory","Runtime":"/_/jmx/runtime","Classes":"/_/jmx/classes","Compilation":"/_/jmx/compilation"}},"navbar":true,"search":false} \ No newline at end of file diff --git a/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_c b/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_c new file mode 100644 index 0000000000..0a4e78cbd3 --- /dev/null +++ b/rapidoid-integration-tests/src/test/resources/test-results/HttpZoneTest/testMainZone/GET_c @@ -0,0 +1,8 @@ +HTTP/1.1 200 OK +Connection: keep-alive +Server: Rapidoid +Date: XXXXX GMT +Content-Type: application/json; charset=utf-8 +Content-Length: 95 + +{"cdn":"auto","contextPath":"","fluid":false,"home":"/","menu":{},"navbar":true,"search":false} \ No newline at end of file diff --git a/rapidoid-web/src/main/java/org/rapidoid/goodies/ConfigHandler.java b/rapidoid-web/src/main/java/org/rapidoid/goodies/ConfigHandler.java index 1c46072046..1be63a8fb5 100644 --- a/rapidoid-web/src/main/java/org/rapidoid/goodies/ConfigHandler.java +++ b/rapidoid-web/src/main/java/org/rapidoid/goodies/ConfigHandler.java @@ -91,6 +91,12 @@ private String styleOf(String key) { } else if (U.eq(key, "jdbc")) { return "bg-salmon"; + } else if (U.eq(key, "token")) { + return "bg-purple"; + + } else if (U.eq(key, "http")) { + return "bg-teal"; + } else if (U.eq(key, "jobs")) { return "bg-steel"; diff --git a/rapidoid-web/src/main/java/org/rapidoid/goodies/Goodies.java b/rapidoid-web/src/main/java/org/rapidoid/goodies/Goodies.java index 574e7bc1f8..12bdeb4708 100644 --- a/rapidoid-web/src/main/java/org/rapidoid/goodies/Goodies.java +++ b/rapidoid-web/src/main/java/org/rapidoid/goodies/Goodies.java @@ -194,9 +194,10 @@ public static void adminCenter(Setup setup) { if (Conf.USERS.isEmpty() && Env.dev()) { String pass = generatedAdminPassword(); + Config admin = Conf.USERS.sub("admin"); - admin.set("roles", "administrator"); admin.set("password", pass); + Msc.logSection("ADMIN CREDENTIALS: username = " + AnsiColor.bold("admin") + ", password = " + AnsiColor.bold(pass)); } } @@ -215,7 +216,7 @@ private static void jpaGoodies(Setup setup) { if (Msc.hasJPA()) { for (Class type : JPA.getEntityJavaTypes()) { String uri = GUI.typeUri(type); - String contextPath = HttpUtils.getContextPath(setup.custom(), setup.segment()); + String contextPath = HttpUtils.zone(setup.custom(), setup.segment()).entry("home").or("/_"); X.scaffold(setup, Msc.uri(contextPath, uri), type); } }