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