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