package org.rapidoid.env;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.collection.Coll;
import org.rapidoid.commons.Err;
import org.rapidoid.config.RapidoidInitializer;
import org.rapidoid.log.Log;
import org.rapidoid.scan.ClasspathUtil;
import org.rapidoid.u.U;
import org.rapidoid.util.LazyInit;
import org.rapidoid.util.Msc;
import org.rapidoid.util.MscOpts;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
/*
* #%L
* rapidoid-commons
* %%
* Copyright (C) 2014 - 2017 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("5.2.0")
public class Environment extends RapidoidInitializer {
private volatile Set<String> profiles;
private volatile Set<String> profilesView;
private volatile EnvMode mode;
private volatile List<String> args;
private volatile List<String> argsView;
private final EnvProperties properties = new EnvProperties();
private final LazyInit<Map<String, Object>> argsAsMap = new LazyInit<>(new Callable<Map<String, Object>>() {
@Override
public Map<String, Object> call() throws Exception {
U.notNull(args, "environment args");
return Msc.parseArgs(args);
}
});
public Environment() {
reset();
}
public Set<String> profiles() {
makeSureIsInitialized();
return profilesView;
}
public EnvMode mode() {
makeSureIsInitialized();
return mode;
}
private void makeSureIsInitialized() {
if (profiles == null || mode == null) {
synchronized (this) {
if (profiles == null || mode == null) {
initModeAndProfiles();
}
}
}
}
public synchronized void reset() {
profiles = null;
mode = null;
argsAsMap.reset();
setArgs(); // no args
}
@SuppressWarnings("ConstantConditions")
private void initModeAndProfiles() {
if (U.isEmpty(profiles)) {
profiles = Coll.synchronizedSet();
profiles.addAll(retrieveProfiles());
profilesView = Collections.unmodifiableSet(profiles);
}
boolean production = Msc.isPlatform() || Env.hasInitial("mode", "production") || profiles.contains("production");
boolean test = Env.hasInitial("mode", "test") || profiles.contains("test");
boolean dev = Env.hasInitial("mode", "dev") || profiles.contains("dev");
if (!production && !test && !dev) {
mode = inferMode();
if (!silent()) Log.info("No production/dev/test mode was configured, inferring mode", "!mode", mode);
} else {
boolean onlyOne = (!production || !dev) && (!dev || !test) && (!production || !test);
U.must(onlyOne, "Only one of the ('production', 'dev', 'test') profiles can be specified!");
if (production) {
mode = EnvMode.PRODUCTION;
} else if (dev) {
mode = EnvMode.DEV;
} else if (test) {
mode = EnvMode.TEST;
} else {
throw Err.notExpected();
}
}
String modeProfile = mode.name().toLowerCase();
if (!silent()) Log.info("Automatically activating mode-specific profile", "!profile", modeProfile);
profiles.add(modeProfile);
if (Msc.isPlatform()) {
profiles.add("platform");
}
RapidoidEnv.touch();
if (!silent()) Log.info("Initialized environment", "!mode", mode, "!profiles", profiles);
if (mode != EnvMode.TEST) {
U.must(!MscOpts.isTestingHttps(), "The HTTPS testing can only be activated in TEST mode!");
}
}
private static boolean silent() {
return Msc.isSilent();
}
private static EnvMode inferMode() {
if (Msc.isInsideTest()) {
return EnvMode.TEST;
} else {
if (Msc.dockerized()) return EnvMode.PRODUCTION;
return ClasspathUtil.getClasspathFolders().isEmpty() ? EnvMode.PRODUCTION : EnvMode.DEV;
}
}
private static List<String> retrieveProfiles() {
List<String> profiles;
String profilesLst = Env.initial("profiles");
if (U.notEmpty(profilesLst)) {
profiles = U.list(profilesLst.split("\\s*\\,\\s*"));
if (!silent()) Log.info("Configuring active profiles", "!profiles", profiles);
return profiles;
} else {
if (!Msc.isPlatform()) {
if (!silent()) Log.info("No profiles were specified, activating 'default' profile");
profiles = U.list("default");
} else {
profiles = U.list();
}
}
return profiles;
}
public void setArgs(String... args) {
this.args = Coll.synchronizedList(args);
this.argsView = Collections.unmodifiableList(this.args);
this.argsAsMap.reset();
}
public List<String> args() {
return argsView;
}
public boolean hasProfile(String profileName) {
return profiles().contains(profileName);
}
public boolean hasAnyProfile(String... profileNames) {
for (String profileName : profileNames) {
if (hasProfile(profileName)) {
return true;
}
}
return false;
}
public synchronized void setProfiles(String... activeProfiles) {
if (U.isEmpty(profiles)) {
profiles = Coll.synchronizedSet();
profilesView = Collections.unmodifiableSet(profiles);
}
Collections.addAll(this.profiles, activeProfiles);
this.mode = null;
initModeAndProfiles();
if (!silent())
Log.info("Activating custom profiles", "!activating", activeProfiles, "!resulting profiles", this.profiles, "!resulting mode", this.mode);
}
public boolean isInitialized() {
return mode != null;
}
public EnvProperties properties() {
RapidoidEnv.touch();
return properties;
}
public Map<String, Object> argsAsMap() {
RapidoidEnv.touch();
return argsAsMap.get();
}
}