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