001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.microservice;
014
015import java.io.*;
016import java.util.*;
017import java.util.jar.*;
018import java.util.logging.*;
019
020import org.apache.juneau.config.*;
021import org.apache.juneau.config.store.*;
022import org.apache.juneau.config.vars.*;
023import org.apache.juneau.microservice.console.*;
024import org.apache.juneau.svl.*;
025import org.apache.juneau.utils.*;
026
027/**
028 * Builder for {@link Microservice} class.
029 *
030 * <p>
031 * Instances of this class are created using {@link Microservice#create()}.
032 */
033public class MicroserviceBuilder {
034
035   Args args;
036   ManifestFile manifest;
037   Logger logger;
038   LogConfig logConfig;
039   Config config;
040   String configName;
041   ConfigStore configStore;
042   ConfigBuilder configBuilder = Config.create();
043   Boolean consoleEnabled;
044   List<ConsoleCommand> consoleCommands = new ArrayList<>();
045   VarResolverBuilder varResolverBuilder = VarResolver.create().defaultVars().vars(ConfigVar.class);
046   Scanner consoleReader;
047   PrintWriter consoleWriter;
048   MicroserviceListener listener;
049
050   /**
051    * Constructor.
052    */
053   protected MicroserviceBuilder() {}
054
055   /**
056    * Copy constructor.
057    *
058    * @param copyFrom The builder to copy settings from.
059    */
060   protected MicroserviceBuilder(MicroserviceBuilder copyFrom) {
061      this.args = copyFrom.args;
062      this.manifest = copyFrom.manifest;
063      this.logger = copyFrom.logger;
064      this.configName = copyFrom.configName;
065      this.logConfig = copyFrom.logConfig == null ? null : copyFrom.logConfig.copy();
066      this.consoleEnabled = copyFrom.consoleEnabled;
067      this.configBuilder = copyFrom.configBuilder;
068      this.varResolverBuilder = copyFrom.varResolverBuilder;
069      this.consoleReader = copyFrom.consoleReader;
070      this.consoleWriter = copyFrom.consoleWriter;
071   }
072
073   /**
074    * Creates a copy of this builder.
075    *
076    * @return A new copy of this builder.
077    */
078   public MicroserviceBuilder copy() {
079      return new MicroserviceBuilder(this);
080   }
081
082   /**
083    * Instantiate a new microservice using the settings defined on this builder.
084    *
085    * @return A new microservice.
086    * @throws Exception
087    */
088   public Microservice build() throws Exception {
089      return new Microservice(this);
090   }
091
092   /**
093    * Specifies the command-line arguments passed into the Java command.
094    *
095    * <p>
096    * This is required if you use {@link Microservice#getArgs()} or <code>$A</code> string variables.
097    *
098    * @param args
099    *    The command-line arguments passed into the Java command as a pre-parsed {@link Args} object.
100    * @return This object (for method chaining).
101    */
102   public MicroserviceBuilder args(Args args) {
103      this.args = args;
104      return this;
105   }
106
107   /**
108    * Specifies the command-line arguments passed into the Java command.
109    *
110    * <p>
111    * This is required if you use {@link Microservice#getArgs()} or <code>$A</code> string variables.
112    *
113    * @param args
114    *    The command-line arguments passed into the Java command as the raw command-line arguments.
115    * @return This object (for method chaining).
116    */
117   public MicroserviceBuilder args(String...args) {
118      this.args = new Args(args);
119      return this;
120   }
121
122   /**
123    * Specifies the manifest file of the jar file this microservice is contained within.
124    *
125    * <p>
126    * This is required if you use {@link Microservice#getManifest()}.
127    * It's also used to locate initialization values such as <code>Main-Config</code>.
128    *
129    * <p>
130    * If you do not specify the manifest file, we attempt to resolve it through the following methods:
131    * <ol class='spaced-list'>
132    *    <li>
133    *       Looking on the file system for a file at <js>"META-INF/MANIFEST.MF"</js>.
134    *       This is primarily to allow for running microservices from within eclipse workspaces where the manifest file
135    *       is located in the project root.
136    *    <li>
137    *       Using the class loader for this class to find the file at the URL <js>"META-INF/MANIFEST.MF"</js>.
138    * </ol>
139    *
140    * @param value
141    *    The manifest file of this microservice.
142    *    <br>Can be any of the following types:
143    *    <ul>
144    *       <li>{@link ManifestFile}
145    *       <li>{@link Manifest}
146    *       <li>{@link Reader} - Containing the raw contents of the manifest.  Note that the input must end with a newline.
147    *       <li>{@link InputStream} - Containing the raw contents of the manifest.  Note that the input must end with a newline.
148    *       <li>{@link File} - File containing the raw contents of the manifest.
149    *       <li>{@link String} - Path to file containing the raw contents of the manifest.
150    *       <li>{@link Class} - Finds and loads the manifest file of the jar file that the specified class is contained within.
151    *    </ul>
152    * @return This object (for method chaining).
153    * @throws IOException
154    */
155   public MicroserviceBuilder manifest(Object value) throws IOException {
156      if (value == null)
157         this.manifest = null;
158      else if (value instanceof ManifestFile)
159         this.manifest = (ManifestFile)value;
160      else if (value instanceof Manifest)
161         this.manifest = new ManifestFile((Manifest)value);
162      else if (value instanceof Reader)
163         this.manifest = new ManifestFile((Reader)value);
164      else if (value instanceof InputStream)
165         this.manifest = new ManifestFile((InputStream)value);
166      else if (value instanceof File)
167         this.manifest = new ManifestFile((File)value);
168      else if (value instanceof String)
169         this.manifest = new ManifestFile(new File((String)value));
170      else if (value instanceof Class)
171         this.manifest = new ManifestFile((Class<?>)value);
172      else
173         throw new RuntimeException("Invalid type passed to MicroserviceBuilder.manifest(Object).  Type=["+value.getClass().getName()+"]");
174
175      return this;
176   }
177
178   /**
179    * Specifies the logger used by the microservice and returned by the {@link Microservice#getLogger()} method.
180    *
181    * <p>
182    * Calling this method overrides the default logging mechanism controlled by the {@link #logConfig(LogConfig)} method.
183    *
184    * @param logger The logger to use for logging microservice messages.
185    * @return This object (for method chaining).
186    */
187   public MicroserviceBuilder logger(Logger logger) {
188      this.logger = logger;
189      return this;
190   }
191
192   /**
193    * Specifies logging instructions for the microservice.
194    *
195    * <p>
196    * If not specified, the values are taken from the <js>"Logging"</js> section of the configuration.
197    *
198    * <p>
199    * This method is ignored if {@link #logger(Logger)} is used to set the microservice logger.
200    *
201    * @param logConfig The log configuration.
202    * @return This object (for method chaining).
203    */
204   public MicroserviceBuilder logConfig(LogConfig logConfig) {
205      this.logConfig = logConfig;
206      return this;
207   }
208
209   /**
210    * Specifies the config for initializing this microservice.
211    *
212    * <p>
213    * Calling this method overrides the default configuration controlled by the {@link #configName(String)} and {@link #configStore(ConfigStore)} methods.
214    *
215    * @param config The configuration.
216    * @return This object (for method chaining).
217    */
218   public MicroserviceBuilder config(Config config) {
219      this.config = config;
220      return this;
221   }
222
223   /**
224    * Specifies the config name for initializing this microservice.
225    *
226    * <p>
227    * If you do not specify the config file location, we attempt to resolve it through the following methods:
228    * <ol class='spaced-list'>
229    *    <li>
230    *       Resolve file first in working directory, then in classpath, using the following names:
231    *       <ul>
232    *          <li>
233    *             The <js>"configFile"</js> argument in the command line arguments passed in through the constructor.
234    *          <li>
235    *             The value of the <code>Main-Config</code> entry in the manifest file.
236    *          <li>
237    *             A config file in the same location and with the same name as the executable jar file.
238    *             (e.g. <js>"java -jar myjar.jar"</js> will look for <js>"myjar.cfg"</js>).
239    *       </ul>
240    *    <li>
241    *       Resolve any <js>"*.cfg"</js> file that can be found in the working directory.
242    *    <li>
243    *       Resolve any of the following files in the classpath:  <js>"juneau.cfg"</js>, <js>"system.cfg"</js>
244    * </ol>
245    *
246    * <p>
247    * If no configuration file is found, and empty in-memory configuration is used.
248    *
249    * @param configName The configuration name.
250    * @return This object (for method chaining).
251    */
252   public MicroserviceBuilder configName(String configName) {
253      this.configName = configName;
254      return this;
255   }
256
257   /**
258    * Specifies the config store to use for storing and retrieving configurations.
259    *
260    * <p>
261    * By default, we use a {@link ConfigFileStore} store for configuration files.
262    *
263    * @param configStore The configuration name.
264    * @return This object (for method chaining).
265    */
266   public MicroserviceBuilder configStore(ConfigStore configStore) {
267      this.configStore = configStore;
268      return this;
269   }
270
271   /**
272    * Specifies that the Java console is enabled for this microservice.
273    *
274    * <p>
275    * If not specified, this value is taken from the <js>"Console/enabled"</js> configuration setting.
276    * If not specified in the configuration, defaults to <jk>false</jk>.
277    *
278    * @param consoleEnabled <jk>true</jk> if the Java console is enabled for this microservice.
279    * @return This object (for method chaining).
280    */
281   public MicroserviceBuilder consoleEnabled(boolean consoleEnabled) {
282      this.consoleEnabled = consoleEnabled;
283      return this;
284   }
285
286   /**
287    * Specifies console commands to make available on the Java console.
288    *
289    * <p>
290    * Note that these are ignored if the console is not enabled via {@link #consoleEnabled(boolean)}.
291    *
292    * <p>
293    * This list augments the commands defined via the <js>"Console/commands"</js> configuration setting.
294    *
295    * <p>
296    * This method can only be used on console commands with no-arg constructors.
297    *
298    * @param consoleCommands The list of console commands to append to the list of available commands.
299    * @return This object (for method chaining).
300    * @throws IllegalAccessException
301    * @throws InstantiationException
302    */
303   @SuppressWarnings("unchecked")
304   public MicroserviceBuilder consoleCommands(Class<? extends ConsoleCommand>...consoleCommands) throws InstantiationException, IllegalAccessException {
305      for (Class<? extends ConsoleCommand> cc : consoleCommands)
306         this.consoleCommands.add(cc.newInstance());
307      return this;
308   }
309
310   /**
311    * Specifies console commands to make available on the Java console.
312    *
313    * <p>
314    * Note that these are ignored if the console is not enabled via {@link #consoleEnabled(boolean)}.
315    *
316    * <p>
317    * This list augments the commands defined via the <js>"Console/commands"</js> configuration setting.
318    *
319    * @param consoleCommands The list of console commands to append to the list of available commands.
320    * @return This object (for method chaining).
321    */
322   public MicroserviceBuilder consoleCommands(ConsoleCommand...consoleCommands) {
323      this.consoleCommands.addAll(Arrays.asList(consoleCommands));
324      return this;
325   }
326
327   /**
328    * Specifies the console input and output.
329    *
330    * <p>
331    * If not specified, uses the console returned by {@link System#console()}.
332    * If that is not available, uses {@link System#in} and {@link System#out}.
333    *
334    * <p>
335    * Note that these are ignored if the console is not enabled via {@link #consoleEnabled(boolean)}.
336    *
337    * @param consoleReader The console input.
338    * @param consoleWriter The console output.
339    * @return This object (for method chaining).
340    */
341   public MicroserviceBuilder console(Scanner consoleReader, PrintWriter consoleWriter) {
342      this.consoleReader = consoleReader;
343      this.consoleWriter = consoleWriter;
344      return this;
345   }
346
347   /**
348    * Augments the set of variables defined in the configuration and var resolver.
349    *
350    * <p>
351    * This calls {@link VarResolverBuilder#vars(Class[])} on the var resolver used to construct the configuration
352    * object returned by {@link Microservice#getConfig()} and the var resolver returned by {@link Microservice#getVarResolver()}.
353    *
354    * @param vars The set of variables to append to the var resolver builder.
355    * @return This object (for method chaining).
356    */
357   @SuppressWarnings("unchecked")
358   public MicroserviceBuilder vars(Class<? extends Var>...vars) {
359      varResolverBuilder.vars(vars);
360      return this;
361   }
362
363   /**
364    * Adds a var resolver context object for vars defined in the configuration and var resolver.
365    *
366    * <p>
367    * This calls {@link VarResolverBuilder#contextObject(String,Object)} on the var resolver used to construct the configuration
368    * object returned by {@link Microservice#getConfig()} and the var resolver returned by {@link Microservice#getVarResolver()}.
369    *
370    * @param name The context object name.
371    * @param object The context object.
372    * @return This object (for method chaining).
373    */
374   public MicroserviceBuilder varContext(String name, Object object) {
375      varResolverBuilder.contextObject(name, object);
376      return this;
377   }
378
379   /**
380    * Registers an event listener for this microservice.
381    *
382    * @param listener An event listener for this microservice.
383    * @return This object (for method chaining).
384    */
385   public MicroserviceBuilder listener(MicroserviceListener listener) {
386      this.listener = listener;
387      return this;
388   }
389}