001/*
002 * (C) Copyright 2006-2017 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 *     jcarsique
019 *     Kevin Leturc <kleturc@nuxeo.com>
020 */
021package org.nuxeo.common;
022
023import java.io.File;
024import java.net.URL;
025import java.util.Properties;
026
027import org.apache.commons.lang3.StringUtils;
028import org.apache.commons.lang3.builder.ToStringBuilder;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031
032/**
033 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
034 */
035public class Environment {
036
037    private static Log logger = LogFactory.getLog(Environment.class);
038
039    /**
040     * Constants that identifies possible hosts for the framework.
041     */
042    public static final String JBOSS_HOST = "JBoss";
043
044    // Jetty or GF3 embedded
045    public static final String NXSERVER_HOST = "NXServer";
046
047    public static final String TOMCAT_HOST = "Tomcat";
048
049    public static final String NUXEO_HOME_DIR = "nuxeo.home.dir";
050
051    /**
052     * @since 5.6
053     */
054    public static final String NUXEO_HOME = "nuxeo.home";
055
056    /**
057     * @since 5.4.2
058     */
059    public static final String NUXEO_RUNTIME_HOME = "nuxeo.runtime.home";
060
061    public static final String NUXEO_DATA_DIR = "nuxeo.data.dir";
062
063    /**
064     * @since 5.9.4
065     */
066    public static final String DEFAULT_DATA_DIR = "data";
067
068    public static final String NUXEO_LOG_DIR = "nuxeo.log.dir";
069
070    /**
071     * @since 5.9.4
072     */
073    public static final String DEFAULT_LOG_DIR = "log";
074
075    public static final String NUXEO_PID_DIR = "nuxeo.pid.dir";
076
077    public static final String NUXEO_TMP_DIR = "nuxeo.tmp.dir";
078
079    /**
080     * @since 5.9.4
081     */
082    public static final String DEFAULT_TMP_DIR = "tmp";
083
084    public static final String NUXEO_CONFIG_DIR = "nuxeo.config.dir";
085
086    /**
087     * @since 5.9.4
088     */
089    public static final String DEFAULT_CONFIG_DIR = "config";
090
091    public static final String NUXEO_WEB_DIR = "nuxeo.web.dir";
092
093    /**
094     * @since 5.9.4
095     */
096    public static final String DEFAULT_WEB_DIR = "web";
097
098    /**
099     * @since 5.9.4
100     */
101    public static final String NUXEO_MP_DIR = "nuxeo.mp.dir";
102
103    /**
104     * @since 5.9.4
105     */
106    public static final String DEFAULT_MP_DIR = "packages";
107
108    /**
109     * @since 5.6
110     */
111    public static final String NUXEO_CONTEXT_PATH = "org.nuxeo.ecm.contextPath";
112
113    /**
114     * The application layout (optional): directory containing nuxeo runtime osgi bundles.
115     */
116    public static final String BUNDLES_DIR = "nuxeo.osgi.app.bundles";
117
118    public static final String BUNDLES = "nuxeo.osgi.bundles";
119
120    private static volatile Environment DEFAULT;
121
122    protected final File home;
123
124    protected File data;
125
126    protected File log;
127
128    protected File config;
129
130    protected File web;
131
132    protected File temp;
133
134    protected final Properties properties;
135
136    protected String[] args;
137
138    protected boolean isAppServer;
139
140    protected String hostAppName;
141
142    protected String hostAppVersion;
143
144    protected Iterable<URL> configProvider;
145
146    // Handy parameter to distinguish from (Runtime)home
147    private File serverHome = null;
148
149    // Handy parameter to distinguish from (Server)home
150    private File runtimeHome = null;
151
152    public static final String SERVER_STATUS_KEY = "server.status.key";
153
154    public static final String DISTRIBUTION_NAME = "org.nuxeo.distribution.name";
155
156    public static final String DISTRIBUTION_VERSION = "org.nuxeo.distribution.version";
157
158    /**
159     * @since 7.10
160     */
161    public static final String DISTRIBUTION_SERVER = "org.nuxeo.distribution.server";
162
163    /**
164     * @since 7.10
165     */
166    public static final String DISTRIBUTION_DATE = "org.nuxeo.distribution.date";
167
168    /**
169     * @since 7.10
170     */
171    public static final String DISTRIBUTION_PACKAGE = "org.nuxeo.distribution.package";
172
173    /**
174     * @since 7.10
175     */
176    public static final String PRODUCT_NAME = "org.nuxeo.ecm.product.name";
177
178    /**
179     * @since 7.10
180     */
181    public static final String PRODUCT_VERSION = "org.nuxeo.ecm.product.version";
182
183    // proxy
184    /**
185     * @since 6.0
186     */
187    public static final String NUXEO_HTTP_PROXY_HOST = "nuxeo.http.proxy.host";
188
189    /**
190     * @since 6.0
191     */
192    public static final String NUXEO_HTTP_PROXY_PORT = "nuxeo.http.proxy.port";
193
194    /**
195     * @since 6.0
196     */
197    public static final String NUXEO_HTTP_PROXY_LOGIN = "nuxeo.http.proxy.login";
198
199    /**
200     * @since 6.0
201     */
202    public static final String NUXEO_HTTP_PROXY_PASSWORD = "nuxeo.http.proxy.password";
203
204    /**
205     * @since 7.4
206     */
207    public static final String CRYPT_ALGO = "server.crypt.algorithm";
208
209    /**
210     * @since 7.4
211     */
212    public static final String CRYPT_KEY = "server.crypt.secretkey";
213
214    /**
215     * @since 7.4
216     */
217    public static final String CRYPT_KEYALIAS = "server.crypt.keyalias";
218
219    /**
220     * @since 7.4
221     */
222    public static final String CRYPT_KEYSTORE_PATH = "server.crypt.keystore.path";
223
224    /**
225     * @since 7.4
226     */
227    public static final String CRYPT_KEYSTORE_PASS = "server.crypt.keystore.pass";
228
229    /**
230     * @since 7.4
231     */
232    public static final String JAVA_DEFAULT_KEYSTORE = "javax.net.ssl.keyStore";
233
234    /**
235     * @since 7.4
236     */
237    public static final String JAVA_DEFAULT_KEYSTORE_PASS = "javax.net.ssl.keyStorePassword";
238
239    /**
240     * Call to that constructor should be followed by a call to {@link #init()}. Depending on the available System
241     * properties, you may want to also call {@link #loadProperties(Properties)} or {@link #setServerHome(File)} methods
242     * before {@link #init()}; here is the recommended order:
243     *
244     * <pre>
245     * Environment env = new Environment(home);
246     * Environment.setDefault(env);
247     * env.loadProperties(properties);
248     * env.setServerHome(home);
249     * env.init();
250     * </pre>
251     *
252     * @param home Root path used for most defaults. It is recommended to make it match the server home rather than the
253     *            runtime home.
254     * @see #init()
255     */
256    public Environment(File home) {
257        this(home, null);
258    }
259
260    /**
261     * Call to that constructor should be followed by a call to {@link #init()}. Depending on the available System
262     * properties, you may want to also call {@link #setServerHome(File)} method before {@link #init()}; here is the
263     * recommended order:
264     *
265     * <pre>
266     * Environment env = new Environment(home, properties);
267     * Environment.setDefault(env);
268     * env.setServerHome(home);
269     * env.init();
270     * </pre>
271     *
272     * @param home Root path used for most defaults. It is recommended to make it match the server home rather than the
273     *            runtime home.
274     * @param properties Source properties for initialization. It is used as an {@code Hashtable}: ie only the custom
275     *            values are read, the properties default values are ignored if any.
276     * @see #init()
277     */
278    public Environment(File home, Properties properties) {
279        this.home = home.getAbsoluteFile();
280        this.properties = new Properties();
281        if (properties != null) {
282            loadProperties(properties);
283        }
284    }
285
286    public static synchronized void setDefault(Environment env) {
287        DEFAULT = env;
288    }
289
290    public static Environment getDefault() {
291        if (DEFAULT == null) {
292            tryInitEnvironment();
293        }
294        return DEFAULT;
295    }
296
297    private static synchronized void tryInitEnvironment() {
298        String homeDir = System.getProperty(NUXEO_HOME);
299        if (homeDir != null) {
300            File home = new File(homeDir);
301            if (home.isDirectory()) {
302                DEFAULT = new Environment(home);
303                DEFAULT.init();
304            }
305        }
306    }
307
308    public File getHome() {
309        return home;
310    }
311
312    public boolean isApplicationServer() {
313        return isAppServer;
314    }
315
316    public void setIsApplicationServer(boolean isAppServer) {
317        this.isAppServer = isAppServer;
318    }
319
320    public String getHostApplicationName() {
321        return hostAppName;
322    }
323
324    public String getHostApplicationVersion() {
325        return hostAppVersion;
326    }
327
328    public void setHostApplicationName(String name) {
329        hostAppName = name;
330    }
331
332    public void setHostApplicationVersion(String version) {
333        hostAppVersion = version;
334    }
335
336    public File getTemp() {
337        if (temp == null) {
338            setTemp(properties.getProperty(NUXEO_TMP_DIR, DEFAULT_TMP_DIR));
339        }
340        return temp;
341    }
342
343    /**
344     * Resolve the path against {@link Environment#serverHome} if not absolute.
345     *
346     * @since 8.1
347     */
348    public void setTemp(String temp) {
349        setTemp(getServerHome().toPath().resolve(temp).toFile());
350    }
351
352    public void setTemp(File temp) {
353        this.temp = temp.getAbsoluteFile();
354        setProperty(NUXEO_TMP_DIR, temp.getAbsolutePath());
355        temp.mkdirs();
356    }
357
358    public File getConfig() {
359        if (config == null) {
360            setConfig(properties.getProperty(NUXEO_CONFIG_DIR, DEFAULT_CONFIG_DIR));
361        }
362        return config;
363    }
364
365    /**
366     * Resolve the path against {@link Environment#runtimeHome} if not absolute.
367     *
368     * @since 8.1
369     */
370    public void setConfig(String config) {
371        File configFile = getRuntimeHome().toPath().resolve(config).toFile();
372        setConfig(configFile);
373    }
374
375    public void setConfig(File config) {
376        this.config = config.getAbsoluteFile();
377        setProperty(NUXEO_CONFIG_DIR, config.getAbsolutePath());
378        config.mkdirs();
379    }
380
381    public File getLog() {
382        if (log == null) {
383            setLog(properties.getProperty(NUXEO_LOG_DIR, DEFAULT_LOG_DIR));
384        }
385        return log;
386    }
387
388    /**
389     * Resolve the path against {@link Environment#serverHome} if not absolute.
390     *
391     * @since 8.1
392     */
393    public void setLog(String log) {
394        setLog(getServerHome().toPath().resolve(log).toFile());
395    }
396
397    public void setLog(File log) {
398        this.log = log.getAbsoluteFile();
399        setProperty(NUXEO_LOG_DIR, log.getAbsolutePath());
400        log.mkdirs();
401    }
402
403    public File getData() {
404        if (data == null) {
405            setData(properties.getProperty(NUXEO_DATA_DIR, DEFAULT_DATA_DIR));
406        }
407        return data;
408    }
409
410    /**
411     * Resolve the path against {@link Environment#runtimeHome} if not absolute.
412     *
413     * @since 8.1
414     */
415    public void setData(String data) {
416        setData(getRuntimeHome().toPath().resolve(data).toFile());
417    }
418
419    public void setData(File data) {
420        this.data = data.getAbsoluteFile();
421        setProperty(NUXEO_DATA_DIR, data.getAbsolutePath());
422        data.mkdirs();
423    }
424
425    public File getWeb() {
426        if (web == null) {
427            setWeb(properties.getProperty(NUXEO_WEB_DIR, DEFAULT_WEB_DIR));
428        }
429        return web;
430    }
431
432    /**
433     * Resolve the path against {@link Environment#runtimeHome} if not absolute.
434     *
435     * @since 8.1
436     */
437    public void setWeb(String web) {
438        setWeb(getRuntimeHome().toPath().resolve(web).toFile());
439    }
440
441    public void setWeb(File web) {
442        this.web = web;
443        setProperty(NUXEO_WEB_DIR, web.getAbsolutePath());
444    }
445
446    /**
447     * @since 5.4.2
448     */
449    public File getRuntimeHome() {
450        initRuntimeHome();
451        return runtimeHome;
452    }
453
454    /**
455     * @since 5.4.2
456     */
457    public void setRuntimeHome(File runtimeHome) {
458        this.runtimeHome = runtimeHome.getAbsoluteFile();
459        setProperty(NUXEO_RUNTIME_HOME, runtimeHome.getAbsolutePath());
460    }
461
462    public String[] getCommandLineArguments() {
463        return args;
464    }
465
466    public void setCommandLineArguments(String[] args) {
467        this.args = args;
468    }
469
470    public String getProperty(String key) {
471        return properties.getProperty(key);
472    }
473
474    public String getProperty(String key, String defaultValue) {
475        String val = properties.getProperty(key);
476        return val == null ? defaultValue : val;
477    }
478
479    /**
480     * If setting a path property, consider using {@link #setPath(String, String)}
481     */
482    public void setProperty(String key, String value) {
483        properties.setProperty(key, value);
484    }
485
486    public Properties getProperties() {
487        return properties;
488    }
489
490    public void loadProperties(Properties props) {
491        properties.putAll(props);
492    }
493
494    public boolean isJBoss() {
495        return JBOSS_HOST.equals(hostAppName);
496    }
497
498    public boolean isJetty() {
499        return NXSERVER_HOST.equals(hostAppName);
500    }
501
502    public boolean isTomcat() {
503        return TOMCAT_HOST.equals(hostAppName);
504    }
505
506    /**
507     * Initialization with System properties to avoid issues due to home set with runtime home instead of server home.
508     * If {@link #NUXEO_HOME} System property is not set, or if you want to set a custom server home, then you should
509     * call {@link #setServerHome(File)} before.
510     *
511     * @since 5.4.1
512     */
513    public void init() {
514        initServerHome();
515        initRuntimeHome();
516
517        String dataDir = System.getProperty(NUXEO_DATA_DIR);
518        if (StringUtils.isNotEmpty(dataDir)) {
519            setData(new File(dataDir));
520        }
521
522        String configDir = System.getProperty(NUXEO_CONFIG_DIR);
523        if (StringUtils.isNotEmpty(configDir)) {
524            setConfig(new File(configDir));
525        }
526
527        String logDir = System.getProperty(NUXEO_LOG_DIR);
528        if (StringUtils.isNotEmpty(logDir)) {
529            setLog(new File(logDir));
530        }
531
532        String tmpDir = System.getProperty(NUXEO_TMP_DIR);
533        if (StringUtils.isNotEmpty(tmpDir)) {
534            setTemp(new File(tmpDir));
535        }
536
537        String mpDir = System.getProperty(NUXEO_MP_DIR);
538        setPath(NUXEO_MP_DIR, StringUtils.isNotEmpty(mpDir) ? mpDir : DEFAULT_MP_DIR, getServerHome());
539    }
540
541    private void initRuntimeHome() {
542        if (runtimeHome != null) {
543            return;
544        }
545        String runtimeDir = System.getProperty(NUXEO_RUNTIME_HOME);
546        if (runtimeDir != null && !runtimeDir.isEmpty()) {
547            setRuntimeHome(new File(runtimeDir));
548        } else {
549            setRuntimeHome(home);
550        }
551    }
552
553    /**
554     * This method always returns the server home (or {@link #getHome()} if {@link #NUXEO_HOME_DIR} is not set).
555     *
556     * @since 5.4.2
557     * @return Server home
558     */
559    public File getServerHome() {
560        initServerHome();
561        return serverHome;
562    }
563
564    /**
565     * @since 5.4.2
566     */
567    public void setServerHome(File serverHome) {
568        this.serverHome = serverHome.getAbsoluteFile();
569        setProperty(NUXEO_HOME_DIR, serverHome.getAbsolutePath());
570    }
571
572    private void initServerHome() {
573        if (serverHome != null) {
574            return;
575        }
576        String homeDir = System.getProperty(NUXEO_HOME, System.getProperty(NUXEO_HOME_DIR));
577        if (homeDir != null && !homeDir.isEmpty()) {
578            setServerHome(new File(homeDir));
579        } else {
580            logger.warn(String.format("Could not set the server home from %s or %s system properties, will use %s",
581                    NUXEO_HOME, NUXEO_HOME_DIR, home));
582            setServerHome(home);
583        }
584        logger.debug(this);
585    }
586
587    public void setConfigurationProvider(Iterable<URL> configProvider) {
588        this.configProvider = configProvider;
589    }
590
591    public Iterable<URL> getConfigurationProvider() {
592        return configProvider;
593    }
594
595    @Override
596    public String toString() {
597        return ToStringBuilder.reflectionToString(this);
598    }
599
600    /**
601     * Add a file path as a property
602     *
603     * @param key Property key
604     * @param value Property value: an absolute or relative file
605     * @param baseDir The directory against which the file will be resolved if not absolute
606     * @since 8.1
607     * @see #setProperty(String, String)
608     * @see #setPath(String, String, File)
609     * @see #setPath(String, File)
610     */
611    public void setPath(String key, File value, File baseDir) {
612        setProperty(key, baseDir.toPath().resolve(value.toPath()).toFile().getAbsolutePath());
613    }
614
615    /**
616     * Add a file path as a property
617     *
618     * @param key Property key
619     * @param value Property value: an absolute or relative file path
620     * @param baseDir The directory against which the file will be resolved if not absolute
621     * @since 8.1
622     * @see #setProperty(String, String)
623     * @see #setPath(String, File, File)
624     * @see #setPath(String, File)
625     */
626    public void setPath(String key, String value, File baseDir) {
627        setProperty(key, baseDir.toPath().resolve(value).toFile().getAbsolutePath());
628    }
629
630    /**
631     * Add a file path as a property
632     *
633     * @param key Property key
634     * @param value Property value: an absolute or relative file; if relative, it will be resolved against {@link #home}
635     * @since 8.1
636     * @see #setProperty(String, String)
637     * @see #setPath(String, File, File)
638     */
639    public void setPath(String key, File value) {
640        setPath(key, value, home);
641    }
642
643    /**
644     * Add a file path as a property
645     *
646     * @param key Property key
647     * @param value Property value: an absolute or relative file path; if relative, it will be resolved against
648     *            {@link #home}
649     * @since 8.1
650     * @see #setProperty(String, String)
651     * @see #setPath(String, String, File)
652     */
653    public void setPath(String key, String value) {
654        setPath(key, value, home);
655    }
656
657    /**
658     * @return the file which path is associated with the given key. The file is guaranteed to be absolute if it has
659     *         been set with {@link #setPath(String, File)}
660     * @since 8.1
661     */
662    public File getPath(String key) {
663        return getPath(key, null);
664    }
665
666    /**
667     * @param key the property key
668     * @param defaultValue the default path, absolute or relative to server home
669     * @return the file which path is associated with the given key. The file is guaranteed to be absolute if it has
670     *         been set with {@link #setPath(String, File)}
671     * @since 8.1
672     */
673    public File getPath(String key, String defaultValue) {
674        String path = properties.getProperty(key);
675        if (path != null) {
676            return new File(path);
677        } else if (defaultValue != null) {
678            return getServerHome().toPath().resolve(defaultValue).toFile();
679        }
680        return null;
681    }
682}