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