diff --git a/src/main/java/ortus/boxlang/runtime/BoxRunner.java b/src/main/java/ortus/boxlang/runtime/BoxRunner.java index 524023ab0..0d3e88972 100644 --- a/src/main/java/ortus/boxlang/runtime/BoxRunner.java +++ b/src/main/java/ortus/boxlang/runtime/BoxRunner.java @@ -79,7 +79,7 @@ public static void main( String[] args ) { } // Get a runtime going - BoxRuntime boxRuntime = BoxRuntime.getInstance( options.debug() ); + BoxRuntime boxRuntime = BoxRuntime.getInstance( options.debug(), options.configFile() ); if ( options.templatePath() != null ) { // Execute a file diff --git a/src/main/java/ortus/boxlang/runtime/BoxRuntime.java b/src/main/java/ortus/boxlang/runtime/BoxRuntime.java index 6b353b999..3760e16a3 100644 --- a/src/main/java/ortus/boxlang/runtime/BoxRuntime.java +++ b/src/main/java/ortus/boxlang/runtime/BoxRuntime.java @@ -73,7 +73,8 @@ public class BoxRuntime { "preFunctionInvoke", "postFunctionInvoke", "onScopeCreation", - "onConfigurationLoad" + "onConfigurationLoad", + "onConfigurationOverrideLoad" ); /** @@ -136,9 +137,10 @@ public class BoxRuntime { /** * Static constructor * - * @param debugMode true if the runtime should be started in debug mode + * @param debugMode true if the runtime should be started in debug mode + * @param configPath The path to the configuration file to load as overrides */ - private BoxRuntime( Boolean debugMode ) { + private BoxRuntime( Boolean debugMode, String configPath ) { // Internal timer timerUtil.start( "startup" ); @@ -161,6 +163,12 @@ private BoxRuntime( Boolean debugMode ) { this.configuration = ConfigLoader.getInstance().loadCore(); interceptorService.announce( "onConfigurationLoad", Struct.of( "config", this.configuration ) ); + // Config Override? + if ( configPath != null ) { + this.configuration.process( ConfigLoader.getInstance().deserializeConfig( configPath ) ); + interceptorService.announce( "onConfigurationOverrideLoad", Struct.of( "config", this.configuration ) ); + } + // Create our runtime context that will be the granddaddy of all contexts that execute inside this runtime this.runtimeContext = new RuntimeBoxContext(); @@ -181,12 +189,27 @@ private BoxRuntime( Boolean debugMode ) { /** * Get the singleton instance. This can be null if the runtime has not been started yet. * + * @param debugMode true if the runtime should be started in debug mode + * * @return BoxRuntime * */ public static synchronized BoxRuntime getInstance( Boolean debugMode ) { + return getInstance( debugMode, null ); + } + + /** + * Get the singleton instance. This can be null if the runtime has not been started yet. + * + * @param debugMode true if the runtime should be started in debug mode + * @param configPath The path to the configuration file to load as overrides + * + * @return BoxRuntime + * + */ + public static synchronized BoxRuntime getInstance( Boolean debugMode, String configPath ) { if ( instance == null ) { - instance = new BoxRuntime( debugMode ); + instance = new BoxRuntime( debugMode, configPath ); } return instance; } diff --git a/src/main/java/ortus/boxlang/runtime/config/ConfigLoader.java b/src/main/java/ortus/boxlang/runtime/config/ConfigLoader.java index c9e8604de..badac66fc 100644 --- a/src/main/java/ortus/boxlang/runtime/config/ConfigLoader.java +++ b/src/main/java/ortus/boxlang/runtime/config/ConfigLoader.java @@ -122,15 +122,26 @@ public Configuration loadFromResources( String configFile ) { } + /** + * Load the config from a Struct of settings + * + * @param configMap The configuration structure to load as a Configuration object + * + * @return The parsed configuration + */ + public Configuration loadFromMap( Struct configMap ) { + return new Configuration().process( configMap ); + } + /** * Load the config from a Map of settings * - * @param configMap The configuration map to load as a Configuration object + * @param configMap The configuration Map to load as a Configuration object * * @return The parsed configuration */ - public Configuration loadFromMap( Map configMap ) { - return new Configuration().process( new Struct( configMap ) ); + public Configuration loadFromMap( Map configMap ) { + return loadFromMap( new Struct( configMap ) ); } /** @@ -140,17 +151,10 @@ public Configuration loadFromMap( Map configMap ) { * * @return The parsed configuration */ - @SuppressWarnings( "unchecked" ) public Configuration loadFromFile( File source ) { - // Parse it natively to Java objects - Object rawConfig = JsonUtil.fromJson( source ); - // Verify it loaded the configuration map - if ( rawConfig instanceof Map ) { - logger.info( "Loaded custom BoxLang configuration file [{}]", source ); - return loadFromMap( ( Map ) rawConfig ); - } else { - throw new ConfigurationException( "The config map is not a JSON object. Can't work with it." ); - } + Struct rawConfig = deserializeConfig( source ); + logger.info( "Loaded custom BoxLang configuration file [{}]", source ); + return loadFromMap( rawConfig ); } /** @@ -186,4 +190,57 @@ public Configuration loadFromFile( String source ) { return loadFromFile( new File( source ) ); } + /** + * Load the config from a file source and return the raw config map + * + * @param source The source to load the configuration from + * + * @return The raw config map as a Struct + */ + @SuppressWarnings( "unchecked" ) + public Struct deserializeConfig( File source ) { + // Parse it natively to Java objects + Object rawConfig = JsonUtil.fromJson( source ); + + // Verify it loaded the configuration map + if ( rawConfig instanceof Map ) { + return new Struct( ( Map ) rawConfig ); + } + + throw new ConfigurationException( "The config map is not a JSON object. Can't work with it." ); + } + + /** + * Load the config from a String path source and return the raw config map + * + * @param source The source to load the configuration from + * + * @return The raw config map as a Struct + */ + public Struct deserializeConfig( String source ) { + return deserializeConfig( new File( source ) ); + } + + /** + * Load the config from a URL path source and return the raw config map + * + * @param source The source to load the configuration from + * + * @return The raw config map as a Struct + */ + public Struct deserializeConfig( URL source ) { + return deserializeConfig( new File( source.getFile() ) ); + } + + /** + * Load the config from a path source and return the raw config map + * + * @param source The source to load the configuration from + * + * @return The raw config map as a Struct + */ + public Struct deserializeConfig( Path source ) { + return deserializeConfig( source.toFile() ); + } + } diff --git a/src/main/java/ortus/boxlang/runtime/config/segments/RuntimeConfig.java b/src/main/java/ortus/boxlang/runtime/config/segments/RuntimeConfig.java index a8f8d793e..8db7a65c1 100644 --- a/src/main/java/ortus/boxlang/runtime/config/segments/RuntimeConfig.java +++ b/src/main/java/ortus/boxlang/runtime/config/segments/RuntimeConfig.java @@ -72,7 +72,7 @@ public RuntimeConfig process( Struct config ) { if ( config.containsKey( "mappings" ) ) { tempTester = config.get( "mappings" ); if ( tempTester instanceof Map ) { - this.mappings = new Struct( ( Map ) tempTester ); + this.mappings.addAll( new Struct( ( Map ) tempTester ) ); } else { logger.warn( "The [runtime.mappings] configuration is not a JSON Object, ignoring it." ); }