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