diff --git a/build/build.properties b/build/build.properties
index bad12bd71..c7b837428 100644
--- a/build/build.properties
+++ b/build/build.properties
@@ -11,12 +11,12 @@ java.debug=true
#dependencies
dependencies.dir=${basedir}/lib
cfml.version=5.2.9.31
-cfml.loader.version=2.2.9
+cfml.loader.version=2.2.11
cfml.cli.version=${cfml.loader.version}.${cfml.version}
lucee.version=${cfml.version}
lucee.config.version=5.2.4.37
jre.adoptVesionr=openjdk8
-jre.version=jdk8u192-b12
+jre.version=jdk8u202-b08
launch4j.version=3.12
runwar.version=3.8.1-SNAPSHOT
jline.version=3.8.2
diff --git a/build/build.xml b/build/build.xml
index 256e0d7c5..69dfcd086 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -16,8 +16,8 @@ External Dependencies:
-
-
+
+
@@ -199,6 +199,14 @@ External Dependencies:
+
+
+
diff --git a/src/cfml/system/BaseCommand.cfc b/src/cfml/system/BaseCommand.cfc
index 5eab2b4cb..8195a520a 100644
--- a/src/cfml/system/BaseCommand.cfc
+++ b/src/cfml/system/BaseCommand.cfc
@@ -37,6 +37,7 @@ component accessors="true" singleton {
variables.configService = wirebox.getInstance( "ConfigService" );
variables.SystemSettings = wirebox.getInstance( "SystemSettings" );
variables.job = wirebox.getInstance( "interactiveJob" );
+ variables.thisThread = createObject( 'java', 'java.lang.Thread' ).currentThread();
variables.exitCode = 0;
return this;
@@ -294,8 +295,8 @@ component accessors="true" singleton {
* if the user has hit Ctrl-C. This method will throw an UserInterruptException
* which you should not catch. It will unroll the stack all the way back to the shell
*/
- function checkInterrupted() {
- shell.checkInterrupted();
+ function checkInterrupted( thisThread=variables.thisThread ) {
+ shell.checkInterrupted( argumentCollection=arguments );
}
diff --git a/src/cfml/system/Shell.cfc b/src/cfml/system/Shell.cfc
index d705ccd0a..6aa7eb168 100644
--- a/src/cfml/system/Shell.cfc
+++ b/src/cfml/system/Shell.cfc
@@ -602,8 +602,10 @@ component accessors="true" singleton {
* if the user has hit Ctrl-C. This method will throw an UserInterruptException
* which you should not catch. It will unroll the stack all the way back to the shell
*/
- function checkInterrupted() {
- var thisThread = createObject( 'java', 'java.lang.Thread' ).currentThread();
+ function checkInterrupted( thisThread ) {
+ if( isNull( arguments.thisThread ) ) {
+ thisThread = createObject( 'java', 'java.lang.Thread' ).currentThread();
+ }
// Has the user tried to interrupt this thread?
if( thisThread.isInterrupted() ) {
diff --git a/src/cfml/system/endpoints/ForgeBox.cfc b/src/cfml/system/endpoints/ForgeBox.cfc
index b6f2a043a..f89f1ce27 100644
--- a/src/cfml/system/endpoints/ForgeBox.cfc
+++ b/src/cfml/system/endpoints/ForgeBox.cfc
@@ -225,7 +225,7 @@ component accessors="true" implements="IEndpointInteractive" {
try {
forgebox.getStorageLocation( props.slug, props.version, props.APIToken );
if ( ! arguments.force ) {
- consoleLogger.warn( "A zip for this version has already been uploaded. If you want to override the uploaded zip, run this command with the `force` flag. We will continue to update your package metadata." );
+ consoleLogger.error( "A zip for this version has already been uploaded. If you want to override the uploaded zip, run this command with the `force` flag. We will continue to update your package metadata." );
upload = false;
}
}
diff --git a/src/cfml/system/modules/propertyFile/ModuleConfig.cfc b/src/cfml/system/modules/propertyFile/ModuleConfig.cfc
index 6f40fe6f6..f95e7055a 100644
--- a/src/cfml/system/modules/propertyFile/ModuleConfig.cfc
+++ b/src/cfml/system/modules/propertyFile/ModuleConfig.cfc
@@ -5,4 +5,4 @@ component {
function configure(){
}
-}
+}
\ No newline at end of file
diff --git a/src/cfml/system/modules/propertyFile/box.json b/src/cfml/system/modules/propertyFile/box.json
index f75167acf..7c2184fa7 100644
--- a/src/cfml/system/modules/propertyFile/box.json
+++ b/src/cfml/system/modules/propertyFile/box.json
@@ -1,8 +1,8 @@
{
"name":"PropertyFile Util",
- "version":"1.1.0",
+ "version":"1.2.0",
"author":"Brad Wood",
- "location":"bdw429s/PropertyFile#v1.1.0",
+ "location":"bdw429s/PropertyFile#v1.2.0",
"homepage":"https://github.com/bdw429s/PropertyFile/",
"documentation":"https://github.com/bdw429s/PropertyFile/blob/master/readme.md",
"repository":{
diff --git a/src/cfml/system/modules/propertyFile/models/PropertyFile.cfc b/src/cfml/system/modules/propertyFile/models/PropertyFile.cfc
index ab0d44c5a..9e7f17e5d 100644
--- a/src/cfml/system/modules/propertyFile/models/PropertyFile.cfc
+++ b/src/cfml/system/modules/propertyFile/models/PropertyFile.cfc
@@ -2,12 +2,13 @@
* I am a new Model Object
*/
component accessors="true"{
-
+
// Properties
property name='javaPropertyFile';
+ // A fully qualified path to a property file
property name='path';
property name='syncedNames';
-
+
/**
* Constructor
@@ -17,46 +18,46 @@ component accessors="true"{
setJavaPropertyFile( createObject( 'java', 'java.util.Properties' ).init() );
return this;
}
-
+
/**
- * load
+ * @load A fully qualified path to a property file
*/
function load( required string path){
setPath( arguments.path );
- var fis = CreateObject( 'java', 'java.io.FileInputStream' ).init( expandPath( path ) );
+ var fis = CreateObject( 'java', 'java.io.FileInputStream' ).init( path );
var BOMfis = CreateObject( 'java', 'org.apache.commons.io.input.BOMInputStream' ).init( fis );
var propertyFile = getJavaPropertyFile();
propertyFile.load( BOMfis );
BOMfis.close();
-
-
+
+
var props = propertyFile.propertyNames();
var syncedNames = getSyncedNames();
while( props.hasMoreElements() ) {
var prop = props.nextElement();
this[ prop ] = get( prop );
- syncedNames.append( prop );
+ syncedNames.append( prop );
}
setSyncedNames( syncedNames );
-
+
return this;
}
/**
- * store
+ * @load A fully qualified path to a property file. File will be created if it doesn't exist.
*/
function store( string path=variables.path ){
syncProperties();
-
+
if( !fileExists( arguments.path ) ) {
directoryCreate( getDirectoryFromPath( arguments.path ), true, true );
fileWrite( arguments.path, '' );
}
-
- var fos = CreateObject( 'java', 'java.io.FileOutputStream' ).init( expandPath( arguments.path ) );
+
+ var fos = CreateObject( 'java', 'java.io.FileOutputStream' ).init( arguments.path );
getJavaPropertyFile().store( fos, '' );
fos.close();
-
+
return this;
}
@@ -65,7 +66,7 @@ component accessors="true"{
*/
function get( required string name, string defaultValue ){
if( structKeyExists( arguments, 'defaultValue' ) ) {
- return getJavaPropertyFile().getProperty( name, defaultValue );
+ return getJavaPropertyFile().getProperty( name, defaultValue );
} else if( exists( name ) ) {
return getJavaPropertyFile().getProperty( name );
} else {
@@ -78,14 +79,14 @@ component accessors="true"{
*/
function set( required string name, required string value ){
getJavaPropertyFile().setProperty( name, value );
-
+
var syncedNames = getSyncedNames();
this[ name ] = value;
if( !arrayContains( syncedNames, name ) ){
syncedNames.append( name );
}
setSyncedNames( syncedNames );
-
+
return this;
}
@@ -95,7 +96,7 @@ component accessors="true"{
function remove( required string name ){
if( exists( name ) ) {
getJavaPropertyFile().remove( name );
-
+
var syncedNames = getSyncedNames();
if( arrayFind( syncedNames, name ) ){
syncedNames.deleteAt( arrayFind( syncedNames, name ) );
@@ -122,7 +123,7 @@ component accessors="true"{
structAppend( result, getJavaPropertyFile() );
return result;
}
-
+
/**
* Keeps public properties in sync with Java object
*/
@@ -130,7 +131,7 @@ component accessors="true"{
var syncedNames = getSyncedNames();
var ignore = listToArray( 'init,load,store,get,set,exists,remove,exists,getAsStruct,$mixed' );
var propertyFile = getJavaPropertyFile();
-
+
// This CFC's public properties
for( var prop in this ) {
// Set any new/updated properties in, excluding actual methods and non-simple values
@@ -138,7 +139,7 @@ component accessors="true"{
set( prop, this[ prop ] );
}
}
-
+
// All the properties in the Java object
var props = propertyFile.propertyNames();
while( props.hasMoreElements() ) {
@@ -148,7 +149,7 @@ component accessors="true"{
remove( prop );
}
}
-
+
}
-}
+}
\ No newline at end of file
diff --git a/src/cfml/system/modules_app/coldbox-commands/commands/coldbox/create/handler.cfc b/src/cfml/system/modules_app/coldbox-commands/commands/coldbox/create/handler.cfc
index 4fe59651c..2a95978a8 100644
--- a/src/cfml/system/modules_app/coldbox-commands/commands/coldbox/create/handler.cfc
+++ b/src/cfml/system/modules_app/coldbox-commands/commands/coldbox/create/handler.cfc
@@ -83,12 +83,18 @@ component aliases='coldbox create controller' {
// Are we creating views?
if( arguments.views ) {
- var viewPath = arguments.viewsDirectory & '/' & arguments.name & '/' & thisAction & '.cfm';
+
+ var camelCaseHandlerName = arguments.name.left( 1 ).lCase();
+ if( arguments.name.len() > 1 ) {
+ camelCaseHandlerName &= arguments.name.right( -1 );
+ }
+
+ var viewPath = resolvePath( arguments.viewsDirectory & '/' & camelCaseHandlerName & '/' & thisAction & '.cfm' );
// Create dir if it doesn't exist
directorycreate( getDirectoryFromPath( viewPath ), true, true );
// Create View Stub
fileWrite( viewPath, '#cr##arguments.name#.#thisAction#
#cr#' );
- print.greenLine( 'Created ' & arguments.viewsDirectory & '/' & arguments.name & '/' & thisAction & '.cfm' );
+ print.greenLine( 'Created ' & viewPath );
}
// Are we creating tests cases on actions
@@ -109,7 +115,7 @@ component aliases='coldbox create controller' {
handlerTestContent = replaceNoCase( handlerTestContent, '|TestCases|', '', 'all' );
}
- var handlerPath = '#arguments.directory#/#arguments.name#.cfc';
+ var handlerPath = resolvePath( '#arguments.directory#/#arguments.name#.cfc' );
// Create dir if it doesn't exist
directorycreate( getDirectoryFromPath( handlerPath ), true, true );
@@ -124,7 +130,7 @@ component aliases='coldbox create controller' {
print.greenLine( 'Created #handlerPath#' );
if( arguments.integrationTests ) {
- var testPath = '#arguments.testsDirectory#/#arguments.name#Test.cfc';
+ var testPath = resolvePath( '#arguments.testsDirectory#/#arguments.name#Test.cfc' );
// Create dir if it doesn't exist
directorycreate( getDirectoryFromPath( testPath ), true, true );
// Create the tests
diff --git a/src/cfml/system/modules_app/package-commands/commands/package/init.cfc b/src/cfml/system/modules_app/package-commands/commands/package/init.cfc
index 8ae96b2fe..cc4a95167 100644
--- a/src/cfml/system/modules_app/package-commands/commands/package/init.cfc
+++ b/src/cfml/system/modules_app/package-commands/commands/package/init.cfc
@@ -110,7 +110,7 @@ component aliases="init" {
// Ignore List
if( arguments.ignoreList ){
- arguments[ "ignore" ] = serializeJSON( [ '**/.*', 'test', 'tests' ] );
+ arguments[ "ignore" ] = serializeJSON( [ '**/.*', '/test/', '/tests/' ] );
}
// Cleanup the argument so it does not get written.
structDelete( arguments, "ignoreList" );
diff --git a/src/cfml/system/modules_app/system-commands/commands/repl.cfc b/src/cfml/system/modules_app/system-commands/commands/repl.cfc
index 46834a4e1..7416a8bc9 100644
--- a/src/cfml/system/modules_app/system-commands/commands/repl.cfc
+++ b/src/cfml/system/modules_app/system-commands/commands/repl.cfc
@@ -80,6 +80,9 @@ component {
break;
}
}
+
+ // Evaluate any ${} placeholders
+ command = systemSettings.expandSystemSettings( command )
// add command to our parser
REPLParser.addCommandLine( command );
diff --git a/src/cfml/system/modules_app/system-commands/commands/run.cfc b/src/cfml/system/modules_app/system-commands/commands/run.cfc
index b600eee55..b3406b712 100644
--- a/src/cfml/system/modules_app/system-commands/commands/run.cfc
+++ b/src/cfml/system/modules_app/system-commands/commands/run.cfc
@@ -62,6 +62,10 @@ component{
commandArray = [ nativeShell, '-i', '-c', arguments.command & ' 2>&1 && ( exit $? > /dev/null )' ];
}
+ if( configService.getSetting( 'debugNativeExecution', false ) ) {
+ print.line( commandArray.tolist( ' ' ) ).toConsole();
+ }
+
var exitCode = 1;
// grab the current working directory
var CWDFile = createObject( 'java', 'java.io.File' ).init( resolvePath( '' ) );
@@ -111,9 +115,13 @@ component{
// I convert the byte array in the piped input stream to a character array
var inputStreamReader = createObject( 'java', 'java.io.InputStreamReader' ).init( inputStream );
+ var interruptCount = 0;
// This will block/loop until the input stream closes, which means this loops until the process ends.
while( ( var char = inputStreamReader.read() ) != -1 ) {
- checkInterrupted();
+ if( ++interruptCount > 1000 ) {
+ checkInterrupted();
+ interruptCount=0;
+ }
// if running non-interactive, gather the output of the command
processOutputStringBuilder.append( javaCast( 'char', char ) );
}
diff --git a/src/cfml/system/modules_app/task-commands/models/TaskService.cfc b/src/cfml/system/modules_app/task-commands/models/TaskService.cfc
index 936355545..a2c29c379 100644
--- a/src/cfml/system/modules_app/task-commands/models/TaskService.cfc
+++ b/src/cfml/system/modules_app/task-commands/models/TaskService.cfc
@@ -77,7 +77,7 @@ component singleton accessors=true {
try {
// Run the task
- taskCFC[ target ]( argumentCollection = taskArgs );
+ local.returnedExitCode = taskCFC[ target ]( argumentCollection = taskArgs );
} catch( any e ) {
// If this task didn't already set a failing exit code...
@@ -100,7 +100,11 @@ component singleton accessors=true {
} finally {
// Set task exit code into the shell
- shell.setExitCode( taskCFC.getExitCode() );
+ if( !isNull( local.returnedExitCode ) && isSimpleValue( local.returnedExitCode ) ) {
+ shell.setExitCode( val( local.returnedExitCode ) );
+ } else {
+ shell.setExitCode( taskCFC.getExitCode() );
+ }
}
// If the previous Task failed
diff --git a/src/cfml/system/services/ArtifactService.cfc b/src/cfml/system/services/ArtifactService.cfc
index 7c7733f05..4ce5c2f9c 100644
--- a/src/cfml/system/services/ArtifactService.cfc
+++ b/src/cfml/system/services/ArtifactService.cfc
@@ -21,7 +21,6 @@ component accessors="true" singleton {
property name='packageService' inject='PackageService';
property name='logger' inject='logbox:logger:{this}';
property name="semanticVersion" inject="provider:semanticVersion@semver";
- // COMMANDBOX-479
property name="configService" inject="ConfigService";
@@ -31,7 +30,6 @@ component accessors="true" singleton {
function onDIComplete() {
// Create the artifacts directory if it doesn't exist
- // COMMANDBOX-479
if( !directoryExists( getArtifactsDirectory() ) ) {
directoryCreate( getArtifactsDirectory() );
}
@@ -44,8 +42,9 @@ component accessors="true" singleton {
* @returns A struct of arrays where the struct key is the package package and the array contains the versions of that package in the cache.
*/
struct function listArtifacts( packageName='' ) {
- var result = {};
- // COMMANDBOX-479
+ // Ordered struct
+ var result = [:];
+
var dirList = directoryList( path=getArtifactsDirectory(), recurse=false, listInfo='query', sort='name asc' );
for( var dir in dirList ) {
@@ -71,7 +70,7 @@ component accessors="true" singleton {
* Removes all artifacts from the cache and returns the number of wiped out directories
*/
numeric function cleanArtifacts() {
- // COMMANDBOX-479
+
var qryDir = directoryList( path=getArtifactsDirectory(), recurse=false, listInfo='query' );
var numRemoved = 0;
@@ -115,7 +114,7 @@ component accessors="true" singleton {
*/
function getPackagePath( required packageName, version="" ){
// This will likely change, so I'm only going to put the code here.
- // COMMANDBOX-479
+
var path = getArtifactsDirectory() & '/' & arguments.packageName;
// do we have a version?
if( arguments.version.len() ){
@@ -267,7 +266,7 @@ component accessors="true" singleton {
}
}
- // COMMANDBOX-479
+
string function getArtifactsDirectory() {
return configService.getSetting( 'artifactsDirectory', variables.artifactDir );
}
diff --git a/src/cfml/system/services/CommandService.cfc b/src/cfml/system/services/CommandService.cfc
index 09c5eeb6f..9b170b80c 100644
--- a/src/cfml/system/services/CommandService.cfc
+++ b/src/cfml/system/services/CommandService.cfc
@@ -914,6 +914,12 @@ component accessors="true" singleton {
excludeFromHelp = commandMD.excludeFromHelp ?: false,
commandMD = commandMD
};
+
+ // Fix for CFCs with no hint, they inherit this from the Lucee base compnent.
+ if( commandData.hint == 'This is the Base Component' ) {
+ commandData.hint = '';
+ }
+
// check functions
if( structKeyExists( commandMD, 'functions' ) ){
// Capture the command's parameters
diff --git a/src/cfml/system/services/ConfigService.cfc b/src/cfml/system/services/ConfigService.cfc
index 2525ae456..92a1e3d4e 100644
--- a/src/cfml/system/services/ConfigService.cfc
+++ b/src/cfml/system/services/ConfigService.cfc
@@ -71,7 +71,8 @@ component accessors="true" singleton {
'JSON.ANSIColors.number',
'JSON.ANSIColors.string',
// General
- 'verboseErrors'
+ 'verboseErrors',
+ 'debugNativeExecution'
]);
setConfigFilePath( '/commandbox-home/CommandBox.json' );
diff --git a/src/cfml/system/services/EndpointService.cfc b/src/cfml/system/services/EndpointService.cfc
index ea8902df6..c4b0a07c1 100644
--- a/src/cfml/system/services/EndpointService.cfc
+++ b/src/cfml/system/services/EndpointService.cfc
@@ -52,7 +52,14 @@ component accessors="true" singleton {
var endpointPath = listChangeDelims( arguments.rootDirectory, '/\', '.' ) & '.' & endpointName;
var oEndPoint = wirebox.getInstance( endpointPath );
- registerEndpoint( oEndPoint );
+ if( endPointName == 'forgebox' ) {
+ var customForgeBoxAPIURL = configService.getSetting( 'endpoints.forgebox.apiURL', '' );
+ if( customForgeBoxAPIURL.len() ) {
+ oEndPoint.getForgeBox().setEndpointURL( customForgeBoxAPIURL.reReplaceNoCase( '/api/.*', '' ) );
+ oEndPoint.getForgeBox().setAPIURL( customForgeBoxAPIURL );
+ }
+ }
+ registerEndpoint( oEndPoint );
}
}
@@ -81,7 +88,7 @@ component accessors="true" singleton {
oEndPoint.setNamePrefixes( endpointName.replaceNoCase( 'forgebox-', '' ) );
// Set the API URL for this endpoint's forgebox Util
- oEndPoint.getForgeBox().setEndpointURL( endpointData.APIURL );
+ oEndPoint.getForgeBox().setEndpointURL( endpointData.APIURL.reReplaceNoCase( '/api/.*', '' ) );
oEndPoint.getForgeBox().setAPIURL( endpointData.APIURL );
// Register it, baby!
diff --git a/src/cfml/system/services/ModuleService.cfc b/src/cfml/system/services/ModuleService.cfc
index a8b18db66..90ca1971e 100644
--- a/src/cfml/system/services/ModuleService.cfc
+++ b/src/cfml/system/services/ModuleService.cfc
@@ -165,7 +165,7 @@
if( len( arguments.invocationPath ) ){
// Check if passed module name is already registered
if( structKeyExists( instance.moduleRegistry, arguments.moduleName ) AND !arguments.force ){
- instance.logger.warn( "The module #arguments.moduleName# has already been registered, so skipping registration" );
+ instance.logger.debug( "The module #arguments.moduleName# has already been registered, so skipping registration" );
return false;
}
// register new incoming location
@@ -468,8 +468,13 @@
interceptorProperties=mConfig.interceptors[ y ].properties,
interceptorName=mConfig.interceptors[ y ].name);
// Loop over module interceptors to autowire them
- wirebox.autowire( target=interceptorService.getInterceptor( mConfig.interceptors[ y ].name, true ),
- targetID=mConfig.interceptors[ y ].class );
+ try {
+ wirebox.autowire( target=interceptorService.getInterceptor( mConfig.interceptors[ y ].name, true ),
+ targetID=mConfig.interceptors[ y ].class );
+ } catch( EventPoolManager.ObjectNotFound var e ){
+ // This error simply means our interceptor had no states
+ // And an interceptor with no states basically ceases to exist as it has no purpose in life
+ }
}
// Register module routing entry point pre-pended to routes
diff --git a/src/cfml/system/services/ServerService.cfc b/src/cfml/system/services/ServerService.cfc
index ea99abd0f..7f9c61f19 100644
--- a/src/cfml/system/services/ServerService.cfc
+++ b/src/cfml/system/services/ServerService.cfc
@@ -855,6 +855,18 @@ component accessors="true" singleton {
var displayEngineName = 'WAR';
}
+ // Doing this check here instead of the ServerEngineService so it can apply to existing installs
+ if( CFEngineName == 'adobe' ) {
+ // Work arounnd sketchy resoution of non-existant paths in Undertow
+ // https://issues.jboss.org/browse/UNDERTOW-1413
+ var flexLogFile = serverInfo.serverHomeDirectory & "/WEB-INF/cfform/logs/flex.log";
+ if ( !fileExists( flexLogFile ) ) {
+ // if this doesn't already exist, it ends up getting created in a WEB-INF folder in the web root. Eww....
+ directoryCreate( getDirectoryFromPath( flexLogFile ), true, true );
+ fileWrite( flexLogFile, '' );
+ }
+ }
+
// logdir is set above and is different for WARs and CF engines
serverInfo.consolelogPath = serverInfo.logdir & '/server.out.txt';
serverInfo.accessLogPath = serverInfo.logDir & '/access.txt';
@@ -1326,13 +1338,19 @@ component accessors="true" singleton {
variables.waitingOnConsoleStart = true;
while( true ) {
- // Detect user pressing Ctrl-C
- // Any other characters captured will be ignored
- var line = shell.getReader().readLine();
- if( line == 'q' ) {
- break;
+ // For dumb terminals, just sit and wait to be interrupted
+ // Trying to read from a dumb terminal will throw "The handle is invalid" errors
+ if( shell.getReader().getTerminal().getClass().getName() contains 'dumb' ) {
+ sleep( 500 );
} else {
- consoleLogger.error( 'To exit press Ctrl-C or "q" followed the enter key.' );
+ // Detect user pressing Ctrl-C
+ // Any other characters captured will be ignored
+ var line = shell.getReader().readLine();
+ if( line == 'q' ) {
+ break;
+ } else {
+ consoleLogger.error( 'To exit press Ctrl-C or "q" followed the enter key.' );
+ }
}
}
@@ -1642,7 +1660,7 @@ component accessors="true" singleton {
throw( "The host name [#arguments.host#] can't be found. Do you need to add a host file entry?", 'serverException', e.message & ' ' & e.detail );
} catch( java.net.BindException var e ) {
// Same as above-- the IP address/host isn't bound to any local adapters. Probably a host file entry went missing.
- throw( "The IP address that [#arguments.host#] resovles to can't be bound. If you ping it, does it point to a local network adapter?", 'serverException', e.message & ' ' & e.detail );
+ throw( "The IP address that [#arguments.host#] resolves to can't be bound. If you ping it, does it point to a local network adapter?", 'serverException', e.message & ' ' & e.detail );
}
return portNumber;
@@ -1871,7 +1889,7 @@ component accessors="true" singleton {
arguments.webroot = fileSystemUtil.resolvePath( arguments.webroot );
var servers = getServers();
for( var thisServer in servers ){
- if( fileSystemUtil.resolvePath( servers[ thisServer ].webroot ) == arguments.webroot ){
+ if( fileSystemUtil.resolvePath( path=servers[ thisServer ].webroot, forceDirectory=true ) == arguments.webroot ){
return servers[ thisServer ];
}
}
diff --git a/src/cfml/system/util/FileSystem.cfc b/src/cfml/system/util/FileSystem.cfc
index 61c4ec33a..521d8ab3c 100644
--- a/src/cfml/system/util/FileSystem.cfc
+++ b/src/cfml/system/util/FileSystem.cfc
@@ -45,8 +45,9 @@ component accessors="true" singleton {
* Resolve the incoming path from the file system
* @path.hint The directory to resolve
* @basePath.hint An expanded base path to resolve the path against. Defaults to CWD.
+ * @forceDirectory is for optimization. If you know the path is a directory for sure, pass true and we'll skip the directoryExists() check for performance
*/
- function resolvePath( required string path, basePath=shell.pwd() ) {
+ function resolvePath( required string path, basePath=shell.pwd(), boolean forceDirectory=false ) {
// The Java class will strip trailing slashses, but these are meaningful in globbing patterns
var trailingSlash = ( path.len() > 1 && ( path.endsWith( '/' ) || path.endsWith( '\' ) ) );
@@ -83,10 +84,14 @@ component accessors="true" singleton {
}
// Add back trailing slash if we had it
- var finalPath = oPath.toString() & ( trailingSlash ? server.separator.file : '' );
+ var finalPath = oPath.toString() & ( ( trailingSlash || forceDirectory ) ? server.separator.file : '' );
// This will standardize the name and calculate stuff like ../../
- finalPath = getCanonicalPath( finalPath )
+ if( forceDirectory ) {
+ finalPath = calculateCanonicalPath( finalPath );
+ } else {
+ finalPath = getCanonicalPath( finalPath );
+ }
// have to add back the period after canonicalizing since Java removes it!
return finalPath & ( trailingPeriod && !finalPath.endsWith( '.' ) ? '.' : '' );
@@ -212,15 +217,21 @@ component accessors="true" singleton {
var runtime = createObject( "java", "java.lang.Runtime" ).getRuntime();
if( isWindows() and target.isFile() ){
runtime.exec( [ "rundll32", "url.dll,FileProtocolHandler", target.getCanonicalPath() ] );
- }
- else if( isWindows() and target.isDirectory() ){
+ } else if( isWindows() and target.isDirectory() ){
var processBuilder = createObject( "java", "java.lang.ProcessBuilder" )
.init( [ "explorer.exe", target.getCanonicalPath() ] )
.start();
- }
- // Linux based or mac
- else {
+ } else if( isMac() ) {
+ // Mac
runtime.exec( [ "/usr/bin/open", target.getCanonicalPath() ] );
+ } else {
+ // Default to Linux
+ // If there is xdg-open around, we'll try to use it - otherwise we'll use see.
+ if( runtime.exec( "which xdg-open" ).waitFor() == 0 ) {
+ runtime.exec( [ "/usr/bin/xdg-open", target.getCanonicalPath() ] );
+ } else {
+ runtime.exec( [ "/usr/bin/see", target.getCanonicalPath() ] );
+ }
}
return true;
}
@@ -235,12 +246,18 @@ component accessors="true" singleton {
if( !findNoCase( "http", arguments.URI ) ){
arguments.URI = "http://#arguments.uri#";
}
-
- // open using awt class, if it fails, we are in headless mode.
- if( desktop.isDesktopSupported() ){
- desktop.getDesktop().browse( createObject( "java", "java.net.URI" ).init( arguments.URI ) );
- return true;
- }
+
+ // Some openJDK distros error out above if installed headlessly.
+ try {
+ // open using awt class, if it fails, we are in headless mode.
+ if( desktop.isDesktopSupported() ){
+ desktop.getDesktop().browse( createObject( "java", "java.net.URI" ).init( arguments.URI ) );
+ return true;
+ }
+ } catch( any var e ) {
+ // Bird strike! Log it.
+ logger.error( '#e.message# #e.detail#' );
+ }
// if we get here, then we don't support desktop awt class, most likely in headless mode.
var runtime = createObject( "java", "java.lang.Runtime" ).getRuntime();
diff --git a/src/cfml/system/util/ForgeBox.cfc b/src/cfml/system/util/ForgeBox.cfc
index d53247871..bc35e3447 100644
--- a/src/cfml/system/util/ForgeBox.cfc
+++ b/src/cfml/system/util/ForgeBox.cfc
@@ -410,7 +410,7 @@ or just add DEBUG to the root logger
// If there's only one suggestion and it doesn't have an @ in it, add another suggestion with the @ at the end.
// This is to prevent the tab completion from adding a space after the suggestion since it thinks it's the only possible option
// Hitting tab will still populate the line, but won't add the space which makes it easier if the user intends to continue for a specific version.
- if( opts.len() == 1 && !( opts[1] contains '@' ) ) {
+ if( opts.len() == 1 && ( !( opts[1] contains '@' ) || opts[1].listRest( '@' ).reFindNoCase( '^[a-z]' ) ) ) {
opts.append( opts[1] & '@' );
}
@@ -419,7 +419,7 @@ or just add DEBUG to the root logger
function getStorageLocation( required string slug, required string version, required string APIToken ) {
var results = makeRequest(
- resource = "storage/#slug#/#version#",
+ resource = "storage/#urlEncode( slug )#/#urlEncode( version )#",
method = "get",
headers = {
'x-api-token' : arguments.APIToken
@@ -551,7 +551,7 @@ or just add DEBUG to the root logger
if( errorDetail != statusMessage ) {
errorDetail &= chr( 10 ) & statusMessage;
}
- errorDetail = ucase( arguments.method ) & ' ' &thisURL & chr( 10 ) & errorDetail;
+ errorDetail = ucase( arguments.method ) & ' ' & thisURL & ' ' & errorDetail;
CommandBoxlogger.error( 'Something other than JSON returned. #errorDetail#', 'Actual HTTP Response: ' & results.rawResponse );
throw( 'Uh-oh, ForgeBox returned something other than JSON. Run "system-log | open" to see the full response.', 'forgebox', errorDetail );
}
diff --git a/src/cfml/system/util/ProgressableDownloader.cfc b/src/cfml/system/util/ProgressableDownloader.cfc
index 627d3a5b0..8fbee801a 100644
--- a/src/cfml/system/util/ProgressableDownloader.cfc
+++ b/src/cfml/system/util/ProgressableDownloader.cfc
@@ -36,6 +36,14 @@ component singleton {
var info = resolveConnection( arguments.downloadURL, arguments.redirectUDF );
var connection = info.connection;
var netURL = info.netURL;
+
+ // Initialize status
+ var status = {
+ percent = 0,
+ speedKBps = 0,
+ totalSizeKB = -1,
+ completeSizeKB = 0
+ };
try {
@@ -84,7 +92,7 @@ component singleton {
}
// Build status data to pass to closure
- var status = {
+ status = {
percent = currentPercentage,
speedKBps = kiloBytesPerSecond,
totalSizeKB = ( lenghtOfFile == -1 ? -1 : lenghtOfFile/1000 ),
diff --git a/src/cfml/system/util/TaskDSL.cfc b/src/cfml/system/util/TaskDSL.cfc
index ea7b7c6b3..897e9273e 100644
--- a/src/cfml/system/util/TaskDSL.cfc
+++ b/src/cfml/system/util/TaskDSL.cfc
@@ -20,6 +20,7 @@ component accessors=true {
property name='flags';
property name='workingDirectory';
property name='rawParams';
+ property name='exitCode';
// DI
@@ -45,6 +46,7 @@ component accessors=true {
setFlags( [] );
setWorkingDirectory( '' );
setRawParams( false );
+ setExitCode( 0 );
return this;
}
@@ -161,6 +163,8 @@ component accessors=true {
var result = shell.callCommand( getTokens(), true );
} finally {
+ setExitCode( shell.getExitCode() );
+
var postCommandCWD = shell.getPWD();
// Only change back if the executed command didn't change the CWD
diff --git a/src/cfml/system/util/jline/CommandHighlighter.cfc b/src/cfml/system/util/jline/CommandHighlighter.cfc
index 07f1c05d5..cd8a5cd2d 100644
--- a/src/cfml/system/util/jline/CommandHighlighter.cfc
+++ b/src/cfml/system/util/jline/CommandHighlighter.cfc
@@ -26,8 +26,12 @@ component {
function highlight( reader, buffer ) {
- // Call CommandBox parser to parse the line.
- var commandChain = CommandService.resolveCommand( buffer );
+ try {
+ // Call CommandBox parser to parse the line.
+ var commandChain = CommandService.resolveCommand( buffer );
+ } catch( any var e ) {
+ return createObject("java","org.jline.utils.AttributedString").fromAnsi( buffer );
+ }
// For each command in the chain
for( var command in commandChain ) {
diff --git a/src/cfml/system/util/jline/REPLHighlighter.cfc b/src/cfml/system/util/jline/REPLHighlighter.cfc
index 2323a8d67..d4334e956 100644
--- a/src/cfml/system/util/jline/REPLHighlighter.cfc
+++ b/src/cfml/system/util/jline/REPLHighlighter.cfc
@@ -13,22 +13,6 @@ component {
property name='shell' inject='provider:shell';
function init() {
- variables.functionList = getFunctionList()
- .keyArray()
- // Add in member function versions of functions
- .reduce( function( orig, i ) {
- orig.append( i );
- if( reFind( 'array|struct|query|image|spreadsheet|XML', i ) ) {
- orig.append( i.reReplaceNoCase( '(array|struct|query|image|spreadsheet|XML)(.+)', '\2' ) );
- }
- return orig;
- }, [] )
- // Sort function names longest to shortest
- .sort( function(a,b){
- if( a.len() > b.len() ) return -1;
- if( a.len() < b.len() ) return 1;
- return 0;
- } );
variables.reservedWords = [
'if',
@@ -49,8 +33,10 @@ component {
'false',
'return',
'in',
- 'function'
- ];
+ 'function',
+ 'any'
+ ].toList( '|' );
+
variables.sets = {
')' : '(',
'}' : '{',
@@ -64,17 +50,12 @@ component {
function highlight( reader, buffer ) {
// Highlight CF function names
- for( var func in functionList ) {
- // Find function names that are at the line start or prepended with a space, curly, or period and ending with an opening paren
- buffer = reReplaceNoCase( buffer, '(^|[ \.\{\}])(#func#)(\()', '\1' & print.boldCyan( '\2' ) & '\3', 'all' );
- }
+ // Find text that is at the line start or prepended with a space, curly, or period and ending with an opening paren
+ buffer = reReplaceNoCase( buffer, '(^|[ \.\{\}\(\)])([^ \.\{\}\(\)]*)(\()', '\1' & print.boldCyan( '\2' ) & '\3', 'all' );
// highight reserved words
- for( var reservedWord in reservedWords ) {
- // Find keywords, bookended by a space, curly, paren, or semicolon. Or, of course, the start/end of the line
- buffer = reReplaceNoCase( buffer, '(^|[ \{\}\(])(#reservedWord#)($|[ ;\(\)\{\}])', '\1' & print.boldCyan( '\2' ) & '\3', 'all' );
- }
-
+ buffer = reReplaceNoCase( buffer, '(^|[ \{\}\(])(#reservedWords#)($|[ ;\(\)\{\}])', '\1' & print.boldCyan( '\2' ) & '\3', 'all' );
+
// If the last character was an ending } or ) or ] or " or ' then highlight it and the matching start character
// This logic is pretty basic and doesn't account for escaped stuff. If you want, please send a pull to improve it :)
if( sets.keyExists( buffer.right( 1 ) ) ) {
diff --git a/src/cfml/system/wirebox/system/logging/appenders/FileAppender.cfc b/src/cfml/system/wirebox/system/logging/appenders/FileAppender.cfc
index 234570e64..88a144477 100644
--- a/src/cfml/system/wirebox/system/logging/appenders/FileAppender.cfc
+++ b/src/cfml/system/wirebox/system/logging/appenders/FileAppender.cfc
@@ -119,8 +119,6 @@ component accessors="true" extends="wirebox.system.logging.AbstractAppender"{
var message = loge.getMessage();
var entry = "";
- // Ensure Log File
- initLogLocation();
// Message Layout
if( hasCustomLayout() ){
@@ -238,6 +236,10 @@ component accessors="true" extends="wirebox.system.logging.AbstractAppender"{
var flushInterval = 1000; // 1 second
var sleepInterval = 50;
var count = 0;
+
+ // Ensure Log File
+ initLogLocation();
+
var oFile = fileOpen( variables.logFullPath, "append", this.getProperty( "fileEncoding" ) );
var hasMessages = false;