001/*
002 * Copyright 2013 Atteo.
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 */
016package org.atteo.moonshine;
017
018import java.io.IOException;
019
020import javax.annotation.Nullable;
021
022import org.atteo.evo.config.Configuration;
023import org.atteo.evo.config.IncorrectConfigurationException;
024import org.atteo.evo.filtering.EnvironmentPropertyResolver;
025import org.atteo.evo.filtering.PropertyResolver;
026import org.atteo.evo.filtering.SystemPropertyResolver;
027import org.atteo.evo.filtering.XmlPropertyResolver;
028import org.atteo.moonshine.directories.FileAccessor;
029import org.atteo.moonshine.logging.Logback;
030import org.atteo.moonshine.logging.Logging;
031import org.atteo.moonshine.services.Service;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035import com.beust.jcommander.JCommander;
036import com.google.inject.Guice;
037import com.google.inject.Injector;
038import com.google.inject.Module;
039
040import ch.qos.logback.classic.jul.LevelChangePropagator;
041
042/**
043 * Moonshine container starting class.
044 * <h3>The following operations are performed:</h3>
045 * <p>
046 * <ul>
047 * <li>first, 'Bootstrapping Moonshine' message is logged through SLF4J, this should trigger
048 * logging framework initialization, by default Moonshine is configured to use {@link Logback}
049 * and also provides the default logback.xml file which logs WARN and ERROR messages to the console</li>
050 * <li>{@link Runtime#addShutdownHook(Thread) shutdown hook} is registered, so Moonshine can shutdown
051 * cleanly before virtual machine stops</li>
052 * <li>{@link Logging#earlyBootstrap()} is called, the default implementation for Logback redirects
053 * JUL logs through SLF4J and also initializes {@link LevelChangePropagator}</li>
054 * <li>command line parameters are parsed</li>
055 * <li>{@link FileAccessor file accessor} is initialized</li>
056 * <li>{@link Logging#initialize(FileAccessor, Properties)} is called, the default implementation
057 * for Logback loads logback-moonshine.xml which should load final logging configuration, by default Moonshine
058 * contains logback-moonshine.xml file which logs INFO messages to the file in ${logHome}
059 * directory</li>
060 * <li>configuration is loaded from a number of configuration files, by default '/default-config.xml'
061 * classpath resource and ${configHome}/config.xml file, the result is {@link Config} object,</li>
062 * <li>list of configured services is obtained by executing {@link Config#getSubServices()},</li>
063 * <li> {@link Guice} {@link Injector injector} is created based on the modules returned from each Service's
064 * {@link Service#configure()} method,</li>
065 *  <li>{@link Injector#injectMembers members injection} is performed on each service,</li>
066 *  <li>{@link Service#start() start} is executed on each service.</li>
067 * </ul>
068 * </p>
069 *
070 * <h3>Configuration files</h3>
071 * <p>
072 * Two configuration files are searched for. First the classpath is searched for '/default-config.xml'.
073 * The idea is that this file contains default configuration prepared by the application programmer.
074 * Next application configuration directory '${configHome} is searched for config.xml file which should
075 * contain the configuration prepared by the application administrator. If config.xml file does not exist
076 * it is created with the reference to the XSD schema to which the file should conform.
077 * </p>
078 *
079 * <h3>Property resolvers</h3>
080 * <p>
081 * Configuration files are merged and then filtered with a number of predefined {@link PropertyResolver}s.
082 * In the files @code ${name}} placeholder will be replaced in the following ways:
083 * <ul>
084 *   <li>all Java system properties, see {@link SystemPropertyResolver},</li>
085 *   <li>environment variables can be referenced using env prefix, ex ${env.PATH},
086 * see {@link EnvironmentPropertyResolver},</li>
087 *   <li>all elements in the XML configuration file can be referenced using dot to separate tag names,
088 * see {@link XmlPropertyResolver},</li>
089 *   <li>custom properties under {@code <properties>} section in the configuration file,</li>
090 *   <li>properties are resolved recursively, for instance: ${env.${VARNAME}}</li>
091 *   <li>you can add your own custom {@link PropertyResolver}s using
092 * {@link Moonshine.Builder#addPropertyResolver(PropertyResolver)}.</li>
093 * </ul>
094 * Read the description of the {@link Configuration} engine to learn more about merging, filtering
095 * and validating the configuration file.
096 * </p>
097 *
098 * </li>
099 * </ul>
100 * </p>
101 */
102public interface Moonshine extends AutoCloseable {
103    public interface RestrictedBuilder {
104        /**
105         * Enables or disables registering shutdown hook with {@link Runtime#addShutdownHook(Thread)}.
106         */
107        RestrictedBuilder shutdownHook(boolean shutdownHook);
108
109        /**
110         * Sets home directory for default file accessor.
111         */
112        RestrictedBuilder homeDirectory(String homeDirectory);
113
114        /**
115         * Skips default configuration files.
116         * <p>
117         * By default '/default-config.xml' classpath resource and ${configHome}/config.xml configuration
118         * files are read. This option ignores them.
119         * </p>
120         */
121        RestrictedBuilder skipDefaultConfigurationFiles();
122
123        /**
124         * Adds configuration from given resource.
125         */
126        RestrictedBuilder addConfigurationFromResource(String resource);
127
128        /**
129         * Adds configuration from given resource, if it exists.
130         *
131         * <p>
132         * Ignores the resource, if it does not exist.
133         * </p>
134         */
135        RestrictedBuilder addOptionalConfigurationFromResource(String config);
136
137        /**
138         * Adds custom Guice module.
139         */
140        RestrictedBuilder addModule(Module module);
141
142        /**
143         * Adds configuration from given string.
144         */
145        RestrictedBuilder addConfigurationFromString(String string);
146
147        /**
148         * Add property resolver.
149         */
150        RestrictedBuilder addPropertyResolver(PropertyResolver propertyResolver);
151
152        /**
153         * Add read-only config directory.
154         */
155        RestrictedBuilder addConfigDir(String path);
156
157        /**
158         * Add read-only data directory.
159         */
160        RestrictedBuilder addDataDir(String path);
161    }
162
163    public interface Builder extends RestrictedBuilder {
164        /**
165         * Sets application name.
166         */
167        Builder applicationName(String applicationName);
168
169        /**
170         * Sets program arguments.
171         *
172         * <p>
173         * Usually this will be the value of argv parameter from main(String[]) method.
174         * </p>
175         */
176        Builder arguments(String[] arguments);
177
178        /**
179         * Do not register uncaught exception handler.
180         */
181        Builder skipUncaughtExceptionHandler();
182
183        /**
184         * Adds parameter processor.
185         *
186         * <p>
187         * Moonshine parses the parameters using {@link JCommander}. The provided object
188         * will be added to the list of parameter processors with {@link JCommander#addObject(Object)}.
189         * </p>
190         */
191        Builder addParameterProcessor(ParameterProcessor parameterProcessor);
192
193        @Override
194        Builder shutdownHook(boolean shutdownHook);
195
196        /**
197         * Sets logging framework implementation.
198         */
199        Builder loggingFramework(Logging logging);
200
201        @Override
202        Builder homeDirectory(String homeDirectory);
203
204        @Override
205        Builder skipDefaultConfigurationFiles();
206
207        @Override
208        Builder addConfigurationFromResource(String resource);
209
210        @Override
211        Builder addOptionalConfigurationFromResource(String config);
212
213        @Override
214        Builder addModule(Module module);
215
216        @Override
217        Builder addConfigurationFromString(String string);
218
219        @Override
220        Builder addPropertyResolver(PropertyResolver propertyResolver);
221
222        @Override
223        Builder addConfigDir(String path);
224
225        @Override
226        Builder addDataDir(String path);
227
228        /**
229         * Builds Moonshine based on this builder parameters.
230         * <p>
231         * Can return null, if based on provided configuration Moonshine container
232         * is not supposed to be started. For instance, when '--help' command line parameter
233         * was specified help message should be logged and program should exit immediately.
234         * </p>
235         * @return created Moonshine container, or null if intended behavior is to skip container creation
236         * @throws IOException when configuration could not be accessed
237         * @throws IncorrectConfigurationException when configuration is incorrect
238         */
239        @Nullable
240        Moonshine build() throws MoonshineException, IOException;
241    }
242
243    public static class Factory {
244        public static Builder builder() {
245            return new MoonshineImplementation();
246        }
247
248        public static void logException(MoonshineException e) {
249            Logger logger = LoggerFactory.getLogger("Moonshine");
250
251            if (e instanceof ConfigurationException) {
252                logger.error("Incorrect configuration file: " + e.getMessage());
253                logger.debug("Incorrect configuration file", e);
254            } else if (e instanceof CommandLineParameterException) {
255                logger.error(e.getMessage());
256                logger.debug(e.getMessage(), e);
257            } else {
258                logger.error("Fatal error: " + e.getMessage());
259                logger.debug("Fatal error", e);
260            }
261        }
262    }
263
264    /**
265     * Starts all configured services.
266     */
267    void start();
268
269    /**
270     * Stops all configured services.
271     */
272    void stop();
273
274    /**
275     * Returns the global injector of the Moonshine.
276     * @return Moonshine global injector
277     */
278    Injector getGlobalInjector();
279
280    /**
281     * Stops Moonshine framework.
282     */
283    @Override
284    void close();
285}