From 50bac310e7a3b4ac3a28128560768293ad51a090 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 21 Oct 2020 15:35:35 -0500 Subject: [PATCH 01/78] version bump --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 93333c8d8..93cec4fdf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ notifications: env: global: # TARGET RELEASE VERSION: BUMP AS NEEDED - - COLDBOX_VERSION=6.1.0 + - COLDBOX_VERSION=6.2.0 - COLDBOX_PRERELEASE=false matrix: - ENGINE=lucee@5 # Build Entire Frameworks From edef88ffb4c9851bf2a5924805130efd430e6ae5 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 23 Oct 2020 14:08:33 -0500 Subject: [PATCH 02/78] COLDBOX-932 #resolve response setErrorMessage() if the statusText is not sent, default it to the error message --- system/web/context/Response.cfc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/system/web/context/Response.cfc b/system/web/context/Response.cfc index 25ae0d758..b1128efe6 100644 --- a/system/web/context/Response.cfc +++ b/system/web/context/Response.cfc @@ -321,12 +321,17 @@ component accessors="true" { Response function setErrorMessage( required errorMessage, statusCode, - statusText = "" + statusText ){ setError( true ); addMessage( arguments.errorMessage ); if ( !isNull( arguments.statusCode ) ) { + + if( isNull( arguments.statusText ) ){ + arguments.statusText = arguments.errorMessage; + } + setStatus( arguments.statusCode, arguments.statusText From d96a23ba0733cab2b67e49ee61d8ca1dc72d66b9 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 29 Oct 2020 16:00:47 -0500 Subject: [PATCH 03/78] removal of old annotations --- system/cache/IColdboxApplicationCache.cfc | 10 +++++----- system/cache/util/ICacheStats.cfc | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/system/cache/IColdboxApplicationCache.cfc b/system/cache/IColdboxApplicationCache.cfc index 3e53c52f6..6313a6ca8 100644 --- a/system/cache/IColdboxApplicationCache.cfc +++ b/system/cache/IColdboxApplicationCache.cfc @@ -20,21 +20,21 @@ Description : - + - + - + - + @@ -61,7 +61,7 @@ Description : - + \ No newline at end of file diff --git a/system/cache/util/ICacheStats.cfc b/system/cache/util/ICacheStats.cfc index aceb34b71..04c64f451 100644 --- a/system/cache/util/ICacheStats.cfc +++ b/system/cache/util/ICacheStats.cfc @@ -84,7 +84,6 @@ DEPRECATED USE IStats.cfc instead returntype="any" output ="false" hint ="Get the total cache's misses" - colddoc > From b6f3ed515f4131e82a97561b3596cef7d7b18efc Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 29 Oct 2020 17:13:09 -0500 Subject: [PATCH 04/78] simple refactoring --- tests/specs/async/AsyncManagerSpec.cfc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/specs/async/AsyncManagerSpec.cfc b/tests/specs/async/AsyncManagerSpec.cfc index fbb928e54..f023e23d5 100644 --- a/tests/specs/async/AsyncManagerSpec.cfc +++ b/tests/specs/async/AsyncManagerSpec.cfc @@ -6,6 +6,8 @@ component extends="BaseAsyncSpec" { /*********************************** BDD SUITES ***********************************/ function run( testResults, testBox ){ + variables.out = createObject( "java", "java.lang.System" ).out; + // all your suites go here. describe( "ColdBox Async Programming", function(){ beforeEach( function( currentSpec ){ @@ -19,7 +21,7 @@ component extends="BaseAsyncSpec" { .runAsync( function(){ debug( "runAsync: " & getThreadName() ); var message = "hello from in closure land"; - createObject( "java", "java.lang.System" ).out.println( message ); + variables.out.println( message ); debug( "Hello debugger" ); sleep( randRange( 1, 1000 ) ); From 3211d0b55d9f5e495b5177e45caefb7ea2a51eb1 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 29 Oct 2020 17:34:58 -0500 Subject: [PATCH 05/78] COLDBOX-932 #reverting, makes no sense anymore since the request context gives the defaults. --- system/web/context/Response.cfc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/system/web/context/Response.cfc b/system/web/context/Response.cfc index b1128efe6..32cc9535f 100644 --- a/system/web/context/Response.cfc +++ b/system/web/context/Response.cfc @@ -328,10 +328,6 @@ component accessors="true" { if ( !isNull( arguments.statusCode ) ) { - if( isNull( arguments.statusText ) ){ - arguments.statusText = arguments.errorMessage; - } - setStatus( arguments.statusCode, arguments.statusText From f472874cfc8343ba075493b4e9b065ff16892cd7 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 30 Oct 2020 08:36:19 -0500 Subject: [PATCH 06/78] Missing default value --- system/web/context/Response.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/web/context/Response.cfc b/system/web/context/Response.cfc index 32cc9535f..daa277f54 100644 --- a/system/web/context/Response.cfc +++ b/system/web/context/Response.cfc @@ -321,7 +321,7 @@ component accessors="true" { Response function setErrorMessage( required errorMessage, statusCode, - statusText + statusText="" ){ setError( true ); addMessage( arguments.errorMessage ); From 84d739ff66eb06ab30fdaee1eb4349d1bfcdfa6d Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 30 Oct 2020 15:22:23 -0500 Subject: [PATCH 07/78] lots of updates and refactorings to improve testing. --- system/testing/BaseTestCase.cfc | 24 ++++++++++- system/web/Renderer.cfc | 1 + test-harness/interceptors/Test1.cfc | 5 +++ tests/Application.cfc | 7 +++- tests/resources/BaseIntegrationTest.cfc | 23 +++++++++++ tests/specs/FrameworkSuperTypeTest.cfc | 12 ------ tests/specs/integration/EventCaching.cfc | 25 ----------- tests/specs/integration/EventExecutions.cfc | 12 ------ .../integration/ExecutorRegistrations.cfc | 4 ++ tests/specs/integration/MainTests.cfc | 12 ------ tests/specs/integration/ModuleTests.cfc | 12 ------ tests/specs/integration/Renderings.cfc | 41 +------------------ .../integration/RestfulHandlersTests.cfc | 4 -- tests/specs/integration/WireBoxDSLTests.cfc | 12 ------ 14 files changed, 63 insertions(+), 131 deletions(-) diff --git a/system/testing/BaseTestCase.cfc b/system/testing/BaseTestCase.cfc index 79a991cda..3ae8a8159 100755 --- a/system/testing/BaseTestCase.cfc +++ b/system/testing/BaseTestCase.cfc @@ -169,11 +169,33 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { /** * Reset the persistence of the unit test coldbox app, basically removes the controller from application scope + * + * @orm Reload ORM or not + * * @return BaseTestCase */ - function reset( boolean clearMethods = false, decorator ){ + function reset( boolean orm = false ){ + + // Graceful shutdown + if( structKeyExists( application, getColdboxAppKey() ) ){ + application[ getColdboxAppKey() ].getLoaderService().processShutdown(); + } + + // Wipe app scopes structDelete( application, getColdboxAppKey() ); + structDelete( application, "wirebox" ); + + // Lucee Cleanups + if( server.keyExists( "lucee" ) ){ + pagePoolClear(); + } + + // ORM + if( arguments.orm ){ + ORMReload(); + } + // Wipe out request scope. if ( !structIsEmpty( request ) ) { lock type="exclusive" scope="request" timeout=10 { if ( !structIsEmpty( request ) ) { diff --git a/system/web/Renderer.cfc b/system/web/Renderer.cfc index cf92f3d76..7e01d13f3 100755 --- a/system/web/Renderer.cfc +++ b/system/web/Renderer.cfc @@ -46,6 +46,7 @@ component /** * Constructor + * * @controller The ColdBox main controller * @controller.inject coldbox */ diff --git a/test-harness/interceptors/Test1.cfc b/test-harness/interceptors/Test1.cfc index 4ca16eb6d..66b4163af 100755 --- a/test-harness/interceptors/Test1.cfc +++ b/test-harness/interceptors/Test1.cfc @@ -15,6 +15,11 @@ component extends="coldbox.system.Interceptor" { log.info( "Executing request capture" ); } + function afterRendererInit( event, data, rc, prc ){ + log.info( "Executing render init" ); + arguments.data.this.bdd = true; + } + void function onCustomState( event, struct data, rc ){ var threadName = createObject( "java", "java.lang.Thread" ) .currentThread() diff --git a/tests/Application.cfc b/tests/Application.cfc index 1964c0270..bd9b1ea47 100755 --- a/tests/Application.cfc +++ b/tests/Application.cfc @@ -34,7 +34,12 @@ component{ function onRequestStart( required targetPage ){ - //ORMReload(); + // Cleanup + if( !isNull( application.cbController ) ){ + application.cbController.getLoaderService().processShutdown(); + } + structDelete( application, "cbController" ); + structDelete( application, "wirebox" ); return true; } diff --git a/tests/resources/BaseIntegrationTest.cfc b/tests/resources/BaseIntegrationTest.cfc index 231263ed6..36bee6179 100644 --- a/tests/resources/BaseIntegrationTest.cfc +++ b/tests/resources/BaseIntegrationTest.cfc @@ -19,11 +19,34 @@ component appMapping="/cbTestHarness" { + // Load on first test + this.loadColdBox = true; + // Never unload until the request dies + this.unloadColdBox = false; + /*********************************** LIFE CYCLE Methods ***********************************/ function beforeAll(){ super.beforeAll(); // do your own stuff here + structDelete( request, "_lastInvalidEvent" ); + // Wire up the test object with dependencies + if( this.loadColdBox && structKeyExists( application, "wirebox" ) ){ + application.wirebox.autowire( this ); + } + + // add custom matchers + addMatchers( { + toHavePartialKey : function( expectation, args = {} ){ + // iterate over actual to find key + for ( var thisKey in arguments.expectation.actual ) { + if ( findNoCase( arguments.args[ 1 ], thisKey ) ) { + return true; + } + } + return false; + } + } ); } function afterAll(){ diff --git a/tests/specs/FrameworkSuperTypeTest.cfc b/tests/specs/FrameworkSuperTypeTest.cfc index 9005dc683..9999b95c0 100644 --- a/tests/specs/FrameworkSuperTypeTest.cfc +++ b/tests/specs/FrameworkSuperTypeTest.cfc @@ -1,17 +1,5 @@ component extends="tests.resources.BaseIntegrationTest"{ - /*********************************** LIFE CYCLE Methods ***********************************/ - - // executes before all suites+specs in the run() method - function beforeAll(){ - super.beforeAll(); - } - - // executes after all suites+specs in the run() method - function afterAll(){ - super.afterAll(); - } - /*********************************** BDD SUITES ***********************************/ function run(){ diff --git a/tests/specs/integration/EventCaching.cfc b/tests/specs/integration/EventCaching.cfc index faf65ec87..c9d071595 100755 --- a/tests/specs/integration/EventCaching.cfc +++ b/tests/specs/integration/EventCaching.cfc @@ -2,31 +2,6 @@ extends="tests.resources.BaseIntegrationTest" { - /*********************************** LIFE CYCLE Methods ***********************************/ - - function beforeAll(){ - super.beforeAll(); - // do your own stuff here - - // add custom matchers - addMatchers( { - toHavePartialKey : function( expectation, args = {} ){ - // iterate over actual to find key - for ( var thisKey in arguments.expectation.actual ) { - if ( findNoCase( arguments.args[ 1 ], thisKey ) ) { - return true; - } - } - return false; - } - } ); - } - - function afterAll(){ - // do your own stuff here - super.afterAll(); - } - /*********************************** BDD SUITES ***********************************/ function run(){ diff --git a/tests/specs/integration/EventExecutions.cfc b/tests/specs/integration/EventExecutions.cfc index b292e923a..df1468d15 100644 --- a/tests/specs/integration/EventExecutions.cfc +++ b/tests/specs/integration/EventExecutions.cfc @@ -1,17 +1,5 @@ component extends="tests.resources.BaseIntegrationTest"{ - /*********************************** LIFE CYCLE Methods ***********************************/ - - function beforeAll(){ - super.beforeAll(); - // do your own stuff here - } - - function afterAll(){ - // do your own stuff here - super.afterAll(); - } - /*********************************** BDD SUITES ***********************************/ function run(){ diff --git a/tests/specs/integration/ExecutorRegistrations.cfc b/tests/specs/integration/ExecutorRegistrations.cfc index 74421ee91..039e4be0a 100644 --- a/tests/specs/integration/ExecutorRegistrations.cfc +++ b/tests/specs/integration/ExecutorRegistrations.cfc @@ -28,6 +28,10 @@ component expect( getController().getAsyncManager().hasExecutor( "resourcesPool" ) ).toBeTrue(); getController().getModuleService().unload( "resourcesTest" ); expect( getController().getAsyncManager().hasExecutor( "resourcesPool" ) ).toBeFalse(); + + // Load it back up, we need it :) + getController().getModuleService().registerAndActivateModule( "resourcesTest" ); + expect( getController().getAsyncManager().hasExecutor( "resourcesPool" ) ).toBeTrue(); } ); } ); } ); diff --git a/tests/specs/integration/MainTests.cfc b/tests/specs/integration/MainTests.cfc index 91f1accc5..5e4291cb0 100755 --- a/tests/specs/integration/MainTests.cfc +++ b/tests/specs/integration/MainTests.cfc @@ -2,18 +2,6 @@ extends="tests.resources.BaseIntegrationTest" { - /*********************************** LIFE CYCLE Methods ***********************************/ - - function beforeAll(){ - super.beforeAll(); - // do your own stuff here - } - - function afterAll(){ - // do your own stuff here - super.afterAll(); - } - /*********************************** BDD SUITES ***********************************/ function run(){ diff --git a/tests/specs/integration/ModuleTests.cfc b/tests/specs/integration/ModuleTests.cfc index 506651928..385bedd3b 100644 --- a/tests/specs/integration/ModuleTests.cfc +++ b/tests/specs/integration/ModuleTests.cfc @@ -1,17 +1,5 @@ component extends="tests.resources.BaseIntegrationTest" { - /*********************************** LIFE CYCLE Methods ***********************************/ - - function beforeAll(){ - super.beforeAll(); - // do your own stuff here - } - - function afterAll(){ - // do your own stuff here - super.afterAll(); - } - /*********************************** BDD SUITES ***********************************/ function run(){ diff --git a/tests/specs/integration/Renderings.cfc b/tests/specs/integration/Renderings.cfc index 6defa96d7..48a2b9537 100644 --- a/tests/specs/integration/Renderings.cfc +++ b/tests/specs/integration/Renderings.cfc @@ -2,13 +2,6 @@ component extends="tests.resources.BaseIntegrationTest" { - function beforeAll(){ - // forced cleanup - structDelete( application, "cbController" ); - super.beforeAll(); - // do your own stuff here - } - /*********************************** BDD SUITES ***********************************/ function run(){ @@ -62,31 +55,6 @@ component } ); } ); - story( "I want to listen to when the renderer is created", function(){ - given( "A new renderer", function(){ - then( "I can listen to renderer creations", function(){ - // Mock the listener - var mockListener = createStub(); - mockListener[ "afterRendererInit" ] = variables.afterRendererInit; - - // register it - getController() - .getInterceptorService() - .registerInterceptor( interceptorObject = mockListener, interceptorName = "mockListener" ); - - try { - // Run it - var renderer = getController().getRenderer(); - expect( renderer ).toHaveKey( "bdd" ); - } finally { - getController() - .getInterceptorService() - .unregister( interceptorName = "mockListener", state = "afterRenderReinit" ); - } - } ); - } ); - } ); - story( "I want to be able to render multiple rendering regions", function(){ given( "a rendering region to setView()", function(){ then( "it should render using only its name", function(){ @@ -114,7 +82,6 @@ component } ); } ); - story( "I want to pass through rendering arguments to both layouts and views", function(){ given( "a renderLayout() call with custom arguments", function(){ then( "the view AND layout should receive them", function(){ @@ -126,10 +93,4 @@ component } ); } - function afterRendererInit( event, data ){ - if ( !isNull( arguments.data.this ) ) { - arguments.data.this.bdd = true; - } - } - -} +} \ No newline at end of file diff --git a/tests/specs/integration/RestfulHandlersTests.cfc b/tests/specs/integration/RestfulHandlersTests.cfc index 485f1f193..a72d2f9df 100644 --- a/tests/specs/integration/RestfulHandlersTests.cfc +++ b/tests/specs/integration/RestfulHandlersTests.cfc @@ -1,9 +1,5 @@ component extends="tests.resources.BaseIntegrationTest" { - function beforeAll(){ - super.beforeAll(); - } - function run(){ describe( "ColdBox Restful Handlers", function(){ beforeEach( function( currentSpec ){ diff --git a/tests/specs/integration/WireBoxDSLTests.cfc b/tests/specs/integration/WireBoxDSLTests.cfc index 3b31fe2e3..169d866dc 100644 --- a/tests/specs/integration/WireBoxDSLTests.cfc +++ b/tests/specs/integration/WireBoxDSLTests.cfc @@ -3,18 +3,6 @@ *******************************************************************************/ component extends="tests.resources.BaseIntegrationTest" { - /*********************************** LIFE CYCLE Methods ***********************************/ - - function beforeAll(){ - super.beforeAll(); - // do your own stuff here - } - - function afterAll(){ - // do your own stuff here - super.afterAll(); - } - /*********************************** BDD SUITES ***********************************/ function run(){ From 0ca5e8c4c22f55ad2049ad473636ee0a87b4e13e Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 30 Oct 2020 17:11:52 -0500 Subject: [PATCH 08/78] more more more refactoring on tests so we don't have tons of overlaps. Better control of integration tests. --- tests/Application.cfc | 4 + .../specs/web/routing/RoutingServiceTest.cfc | 12 + .../specs/web/services/requestserviceTest.cfc | 227 +++++++----------- 3 files changed, 109 insertions(+), 134 deletions(-) diff --git a/tests/Application.cfc b/tests/Application.cfc index bd9b1ea47..158094a94 100755 --- a/tests/Application.cfc +++ b/tests/Application.cfc @@ -41,6 +41,10 @@ component{ structDelete( application, "cbController" ); structDelete( application, "wirebox" ); + if( server.keyExists( "lucee" ) ){ + pagePoolClear(); + } + return true; } diff --git a/tests/specs/web/routing/RoutingServiceTest.cfc b/tests/specs/web/routing/RoutingServiceTest.cfc index 793842fa2..10705d6c9 100755 --- a/tests/specs/web/routing/RoutingServiceTest.cfc +++ b/tests/specs/web/routing/RoutingServiceTest.cfc @@ -5,6 +5,18 @@ routingService = prepareMock( getController().getRoutingService() ); } + function afterAll(){ + // Cleanup due to mods! + + // Graceful shutdown + if( structKeyExists( application, getColdboxAppKey() ) ){ + application[ getColdboxAppKey() ].getLoaderService().processShutdown(); + } + // Wipe app scopes + structDelete( application, getColdboxAppKey() ); + structDelete( application, "wirebox" ); + } + function run(){ describe( "Routing Services", function(){ it( "can clean incoming pathing", function(){ diff --git a/tests/specs/web/services/requestserviceTest.cfc b/tests/specs/web/services/requestserviceTest.cfc index 3b72892e5..f6fe1cad4 100755 --- a/tests/specs/web/services/requestserviceTest.cfc +++ b/tests/specs/web/services/requestserviceTest.cfc @@ -1,134 +1,93 @@ - - - - - - // Call the super setup method to setup the app. - super.setup(); - - - - - - var originalSetting = getcontroller().getSetting( "RequestContextDecorator" ); - - getcontroller().setSetting( "RequestContextDecorator", "" ); - testRequestCaptures(); - getcontroller().setSetting( "RequestContextDecorator", originalSetting ); - - - - - - var service = getController().getRequestService(); - var context = ""; - var persistStruct = structNew(); - var today = now(); - - /* Setup test variables */ - form.name = "luis majano"; - form.event = "ehGeneral.dspHome,movies.list"; - - url.name = "pio majano"; - url.today = today; - - /* Catpure the request */ - context = service.requestCapture(); - - // debug(context.getCollection()); - - /* Tests */ - assertTrue( isObject( context ), "Context Creation" ); - assertTrue( url.today eq context.getValue( "today" ), "URL Append" ); - assertTrue( context.valueExists( "event" ), "Multi-Event Test" ); - - - - - - getController().setSetting( "jsonPayloadToRC", true ); - var mockContext = prepareMock( getController().getRequestService().getContext() ) - .$( "getHTTPContent" ) - .$callback( function( boolean json = false ){ - var payload = { - "name" : "Jon Clausen", - "type" : "JSON" - }; - - if ( json ) { - return payload; - } else { - return serializeJSON( payload ); - } - } ); - var service = prepareMock( getController().getRequestService() ) - .$( "getContext" ) - .$callback( function(){ - return mockContext; - } ); - - /* Catpure the request */ - context = service.requestCapture(); - - debug( context.getCollection() ); - - /* Tests */ - assertTrue( isObject( context ), "Context Creation" ); - assertTrue( context.valueExists( "name" ), "JSON Append" ); - assertTrue( context.valueExists( "type" ), "JSON Append" ); - assertEquals( context.getValue( "type" ), "JSON" ); - - - - - - var service = getController().getRequestService(); - var context = ""; - - /* Setup test variables */ - url.event = "default"; - - /* Catpure the request */ - context = service.requestCapture(); - - /* Tests */ - assertTrue( isObject( context ), "Context Creation" ); - assertTrue( url.event eq context.getCurrentEvent(), "Event mismatch: #context.getCurrentEvent()#" ); - - - - - - var service = getController().getRequestService(); - var context = ""; - - context = service.getContext(); - assertTrue( isObject( context ), "Context Create" ); - - structDelete( request, "cb_requestContext" ); - assertFalse( service.contextExists(), "Context exists" ); - - service.setContext( context ); - assertTrue( structKeyExists( request, "cb_requestContext" ), "setter in request" ); - - - - - - // This errors sometimes on Adobe CF 11 - try { - structClear( cookie ); - } catch ( any e ) { - } - - - +component extends="tests.resources.BaseIntegrationTest"{ + + function run( testResults, testBox ){ + // all your suites go here. + describe( "Request Services", function(){ + + beforeEach(function( currentSpec ){ + setup(); + requestService = getController().getRequestService(); + }); + + it( "can capture requests", function(){ + var today = now(); + + /* Setup test variables */ + form.name = "luis majano"; + form.event = "ehGeneral.dspHome,movies.list"; + + url.name = "pio majano"; + url.today = today; + + /* Catpure the request */ + var context = requestService.requestCapture(); + + // debug(context.getCollection()); + + /* Tests */ + expect( context ).toBeComponent(); + expect( url.today ).toBe( context.getValue( "today" ) ); + expect( url.name ).toBe( context.getValue( "name" ) ); + expect( context.valueExists( "event" ) ).toBeTrue(); + }); + + it( "can capture a json body", function(){ + var mockContext = prepareMock( requestService.getContext() ) + .$( "getHTTPContent" ) + .$callback( function( boolean json = false ){ + var payload = { + "fullName" : "Jon Clausen", + "type" : "JSON" + }; + + if ( json ) { + return payload; + } else { + return serializeJSON( payload ); + } + } ); + // Mock it + request[ "cb_requestContext" ] = mockContext; + + /* Catpure the request */ + var context = requestService.requestCapture(); + + /* Tests */ + expect( context ).toBeComponent(); + expect( context.valueExists( "fullName" ) ).toBeTrue(); + expect( context.valueExists( "type" ) ).toBeTrue(); + expect( context.getValue( "type" ) ).toBe( "JSON" ); + }); + + + it( "can test the default event setup", function(){ + /* Setup test variables */ + form.event = url.event = "photos.index"; + + /* Catpure the request */ + structDelete( request, "cb_requestContext" ); + var context = requestService.requestCapture(); + + /* Tests */ + expect( context ).toBeComponent(); + expect( url.event ).toBe( context.getCurrentEvent() ); + }); + + + it( "can create and check for context in the request scope", function(){ + var context = requestService.getContext(); + expect( context ).toBeComponent(); + expect( requestService.contextExists() ).toBeTrue(); + + structDelete( request, "cb_requestContext" ); + expect( requestService.contextExists() ).toBeFalse(); + + requestService.setContext( context ); + expect( requestService.contextExists() ).toBeTrue(); + expect( request ).toHaveKey( "cb_requestContext" ); + }); + + } ); + } + +} \ No newline at end of file From b46ac079d6f07d4048b595aa9f210b25ce3ab3e3 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 2 Nov 2020 10:04:33 -0600 Subject: [PATCH 09/78] LOGBOX-55 #resolve Improved exception handling when dealing with shutdowns for appenders and scheduled executors --- system/logging/AbstractAppender.cfc | 29 +++++++++++++++++++---------- tests/Application.cfc | 4 ---- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/system/logging/AbstractAppender.cfc b/system/logging/AbstractAppender.cfc index 04ef2a2dd..50cf96d81 100644 --- a/system/logging/AbstractAppender.cfc +++ b/system/logging/AbstractAppender.cfc @@ -268,18 +268,27 @@ component accessors="true"{ if ( !isActive ) { variables.lock( function(){ if ( !variables.logListener.active ) { - // Mark listener as activated - //out( "(#getName()#) ScheduleTask needs to be started..." ); - variables.logListener.active = true; // Create the runnable Log Listener, Start it up baby! - variables.logBox - .getTaskScheduler() - .schedule( - task = this, - method = "runLogListener", - loadAppContext = false - ); + try{ + variables.logBox + .getTaskScheduler() + .schedule( + task = this, + method = "runLogListener", + loadAppContext = false + ); + + // Mark listener as activated + //out( "(#getName()#) ScheduleTask needs to be started..." ); + variables.logListener.active = true; + } catch( any e ){ + // Just in case it doesn't start, just skip it for now and let another thread + // kick start it. We will just log the exception just in case + // Usually these exceptions can be on shutdowns or when the scheduler cannot take + // any more tasks. + out( "Error scheduling log listener: #e.message# #e.detail#" ); + } //out( "(#getName()#) ScheduleTask started" ); } diff --git a/tests/Application.cfc b/tests/Application.cfc index 158094a94..bd9b1ea47 100755 --- a/tests/Application.cfc +++ b/tests/Application.cfc @@ -41,10 +41,6 @@ component{ structDelete( application, "cbController" ); structDelete( application, "wirebox" ); - if( server.keyExists( "lucee" ) ){ - pagePoolClear(); - } - return true; } From 4d451e9cf0e9dec1e9dc868b403c4ec6a5c77b21 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 2 Nov 2020 10:16:10 -0600 Subject: [PATCH 10/78] more update on tests so they don't overalp with each other. --- .../specs/web/services/HandlerServiceTest.cfc | 14 -------- .../specs/web/services/loaderserviceTest.cfc | 34 +++++++++++-------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/tests/specs/web/services/HandlerServiceTest.cfc b/tests/specs/web/services/HandlerServiceTest.cfc index e51b02b5f..3f207d2b3 100755 --- a/tests/specs/web/services/HandlerServiceTest.cfc +++ b/tests/specs/web/services/HandlerServiceTest.cfc @@ -3,20 +3,6 @@ */ component extends="tests.resources.BaseIntegrationTest" { - /*********************************** LIFE CYCLE Methods ***********************************/ - - // executes before all suites+specs in the run() method - function beforeAll(){ - super.beforeAll(); - } - - // executes after all suites+specs in the run() method - function afterAll(){ - super.afterAll(); - } - - /*********************************** BDD SUITES ***********************************/ - function run( testResults, testBox ){ describe( "Handler Service", function(){ beforeEach( function(){ diff --git a/tests/specs/web/services/loaderserviceTest.cfc b/tests/specs/web/services/loaderserviceTest.cfc index c38612fbf..643f5605e 100755 --- a/tests/specs/web/services/loaderserviceTest.cfc +++ b/tests/specs/web/services/loaderserviceTest.cfc @@ -1,25 +1,29 @@ component extends="tests.resources.BaseIntegrationTest"{ - function setup(){ - super.setup(); + function run( testResults, testBox ){ - ls = getController().getLoaderService(); - } + describe( "Loader services", function(){ + beforeEach(function( currentSpec ){ + setup(); + ls = getController().getLoaderService(); + }); - function testRegisterHandlers(){ - var context = ""; - var fs = "/"; - var dummyFile = getController().getSetting( "HandlersPath" ) & fs & "dummy.cfc"; - createFile( dummyFile ); - getController().getHandlerService().registerHandlers(); - assertTrue( listFindNoCase( getController().getSetting( "RegisteredHandlers" ), "dummy" ) ); - removeFile( dummyFile ); - } + it( "can register handlers", function(){ + var context = ""; + var dummyFile = getController().getSetting( "HandlersPath" ) & "/dummy.cfc";y + + createFile( dummyFile ); + getController().getHandlerService().registerHandlers(); + try{ + assertTrue( listFindNoCase( getController().getSetting( "RegisteredHandlers" ), "dummy" ) ); + } finally{ + removeFile( dummyFile ); + } + }); - function testProcessShutdown(){ - ls.processShutdown(); + }); } private function createFile( required filename ){ From 556b123e77222059e5bd34e12054714bff788873 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 2 Nov 2020 15:32:15 -0600 Subject: [PATCH 11/78] acf incompat --- tests/specs/web/services/loaderserviceTest.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/specs/web/services/loaderserviceTest.cfc b/tests/specs/web/services/loaderserviceTest.cfc index 643f5605e..5130e7c38 100755 --- a/tests/specs/web/services/loaderserviceTest.cfc +++ b/tests/specs/web/services/loaderserviceTest.cfc @@ -11,7 +11,7 @@ it( "can register handlers", function(){ var context = ""; - var dummyFile = getController().getSetting( "HandlersPath" ) & "/dummy.cfc";y + var dummyFile = getController().getSetting( "HandlersPath" ) & "/dummy.cfc"; createFile( dummyFile ); getController().getHandlerService().registerHandlers(); From 8f4f32e2c5503f5cc91ad418de21a705a43de313 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 2 Nov 2020 16:48:56 -0600 Subject: [PATCH 12/78] some exceptions for mock testing. --- system/testing/mock/web/MockController.cfc | 3 +++ system/web/services/HandlerService.cfc | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/system/testing/mock/web/MockController.cfc b/system/testing/mock/web/MockController.cfc index 107ea52d6..e3ddbcd2f 100755 --- a/system/testing/mock/web/MockController.cfc +++ b/system/testing/mock/web/MockController.cfc @@ -6,6 +6,9 @@ */ component extends="coldbox.system.web.Controller" accessors="true" { + // Easy public marker for easy recognition of the mock controller. + this.mockController = true; + /** * Constructor */ diff --git a/system/web/services/HandlerService.cfc b/system/web/services/HandlerService.cfc index 91431e991..1bb6d6238 100644 --- a/system/web/services/HandlerService.cfc +++ b/system/web/services/HandlerService.cfc @@ -450,7 +450,10 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { // If invalidEventHandler is registered, use it if ( len( variables.invalidEventHandler ) ) { // Test for invalid Event Error as well so we don't go in an endless error loop - if ( compareNoCase( arguments.event, request._lastInvalidEvent ) eq 0 ) { + if ( + compareNoCase( arguments.event, request._lastInvalidEvent ) eq 0 && + !structKeyExists( controller, "mockController" ) // Verify this is a real and not a mock controller. + ) { throw( message : "The invalidEventHandler event is also invalid: #variables.invalidEventHandler#", type : "HandlerService.InvalidEventHandlerException" From c5ebb934020c651d614a1439d0105f59a850acab Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Tue, 10 Nov 2020 09:54:58 -0600 Subject: [PATCH 13/78] COLDBOX-936 #resolve ExceptionBean throws exception on weird ORM illegal access collection on scope dump --- system/web/context/ExceptionBean.cfc | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/system/web/context/ExceptionBean.cfc b/system/web/context/ExceptionBean.cfc index ad64e6703..5984fb994 100644 --- a/system/web/context/ExceptionBean.cfc +++ b/system/web/context/ExceptionBean.cfc @@ -476,12 +476,21 @@ component accessors="true" { list.append( " [" & getMetaData( arguments.scope[ i ] ).name & "] Instance" ); } else { savecontent variable="local.myContent" { - writeDump( - var = arguments.scope[ i ], - format = "html", - top = 2, - expand = false - ) + try{ + writeDump( + var = arguments.scope[ i ], + format = "html", + top = 2, + expand = false + ) + } catch( any e ){ + writeDump( + var = arguments.scope[ i ].toString(), + format = "html", + top = 2, + expand = false + ) + } } list.append( "" & i & "" ); list.append( "" & local.myContent & "" ); From 4aa09ee58d41307888d871cc0dd5bffe8136a8a4 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 12 Nov 2020 12:29:53 -0600 Subject: [PATCH 14/78] I think finally the invalid event handler event luring exception is captured. --- system/web/services/HandlerService.cfc | 54 ++++++++++---------------- tests/specs/integration/MainTests.cfc | 6 --- 2 files changed, 21 insertions(+), 39 deletions(-) diff --git a/system/web/services/HandlerService.cfc b/system/web/services/HandlerService.cfc index 1bb6d6238..a6f588b16 100644 --- a/system/web/services/HandlerService.cfc +++ b/system/web/services/HandlerService.cfc @@ -324,11 +324,11 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { } // Run invalid event procedures, handler not found as a module or in all lists - invalidEvent( arguments.event, oHandlerBean ); + arguments.event = invalidEvent( arguments.event, oHandlerBean ); // If we get here, then invalid event handler is active and we need to // return an event handler bean that matches it - return getHandlerBean( oHandlerBean.getFullEvent() ); + return getHandlerBean( arguments.event ); } /** @@ -415,16 +415,19 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { } /** - * Invalid Event procedures + * Invalid Event procedures. An invalid event is detected, so this method + * will verify if the application has an invalidEventHandler or an interceptor + * listening to `onInvalidEvent` modifies the handler bean. Then this method will + * either return the invalid event handler event, or set an exception to be captured. * * @event The event that was found to be invalid - * @ehBean The event handler bean + * @ehBean The event handler bean representing the invalid event * * @throws EventHandlerNotRegisteredException,InvalidEventHandlerException * - * @return HandlerService + * @return The string event that should be executed as the invalid event handler or throws an EventHandlerNotRegisteredException */ - function invalidEvent( required string event, required ehBean ){ + string function invalidEvent( required string event, required ehBean ){ // Announce it var iData = { "invalidEvent" : arguments.event, @@ -435,27 +438,22 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { // If the override was changed by the interceptors then they updated the ehBean of execution if ( iData.override ) { - return this; + return ehBean.getFullEvent(); } // Param our last invalid event just incase param request._lastInvalidEvent = ""; - // If we got here, we have an invalid event and no override, throw a 404 header - controller - .getRequestService() - .getContext() - .setHTTPHeader( statusCode = 404, statusText = "Not Found" ); - // If invalidEventHandler is registered, use it if ( len( variables.invalidEventHandler ) ) { // Test for invalid Event Error as well so we don't go in an endless error loop if ( - compareNoCase( arguments.event, request._lastInvalidEvent ) eq 0 && + compareNoCase( arguments.event, request._lastInvalidEvent ) eq 0 + && !structKeyExists( controller, "mockController" ) // Verify this is a real and not a mock controller. ) { throw( - message : "The invalidEventHandler event is also invalid: #variables.invalidEventHandler#", + message : "The invalidEventHandler event (#variables.invalidEventHandler#) is also invalid: #arguments.event#", type : "HandlerService.InvalidEventHandlerException" ); } @@ -470,26 +468,16 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { .getContext() .setPrivateValue( "invalidevent", arguments.event ); - // Override Event With On Invalid Event - arguments.ehBean - .setHandler( - reReplace( - variables.invalidEventHandler, - "\.[^.]*$", - "" - ) - ) - .setMethod( listLast( variables.invalidEventHandler, "." ) ) - .setModule( "" ); - - // If module found in invalid event, set it for discovery - if ( find( ":", variables.invalidEventHandler ) ) { - arguments.ehBean.setModule( getToken( variables.invalidEventHandler, 1 ) ); - } - - return this; + // Override Event With On invalid handler event + return variables.invalidEventHandler; } + // If we got here, we have an invalid event and no override, throw a 404 header + controller + .getRequestService() + .getContext() + .setHTTPHeader( statusCode = 404, statusText = "Not Found" ); + // Invalid Event Detected, log it in the Application log, not a coldbox log but an app log variables.log.error( "Invalid Event detected: #arguments.event#. Path info: #CGI.PATH_INFO#, query string: #CGI.QUERY_STRING#" diff --git a/tests/specs/integration/MainTests.cfc b/tests/specs/integration/MainTests.cfc index 5e4291cb0..dd7f0731d 100755 --- a/tests/specs/integration/MainTests.cfc +++ b/tests/specs/integration/MainTests.cfc @@ -13,12 +13,6 @@ structDelete( request, "_lastInvalidEvent" ); } ); - xit( "can render the cache panel", function(){ - // Why can't I just call GET() ACF, why do you make things hard! - var event = this.request( route = "main/cachePanel" ); - expect( event.getRenderedContent() ).toInclude( "cachebox_cache" ); - } ); - it( "can handle invalid events", function(){ var event = execute( event = "invalid:bogus.index", renderResults = true ); expect( event.getValue( "cbox_rendered_content" ) ).toInclude( "Invalid Page" ); From 1a29f430e75ee240a848a7b63d1242de2a5e5b96 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 12 Nov 2020 14:55:38 -0600 Subject: [PATCH 15/78] fix for invalid event handler tests --- tests/specs/integration/MainTests.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/specs/integration/MainTests.cfc b/tests/specs/integration/MainTests.cfc index dd7f0731d..7a2360237 100755 --- a/tests/specs/integration/MainTests.cfc +++ b/tests/specs/integration/MainTests.cfc @@ -26,7 +26,7 @@ execute( event = "invalid:bogus.index", renderResults = true ); fail( "The event handler was invalid and should have thrown an exception" ); } catch ( HandlerService.InvalidEventHandlerException e ) { - expect( e.message ).toInclude( "The invalidEventHandler event is also invalid" ); + expect( e.message ).toInclude( "is also invalid" ); } finally { getController().setSetting( "invalidEventHandler", originalInvalidEventHandler ); getController().getHandlerService().onConfigurationLoad(); From 4d5e86b0a6fe5b000e4362988270cf14d108eeaf Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 13 Nov 2020 15:43:17 -0600 Subject: [PATCH 16/78] COLDBOX-937 Migration to cgi.server_name and server_port did not account for the incoming browser port but the cf service port --- system/exceptions/BugReport.cfm | 2 +- system/exceptions/Whoops.cfm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/system/exceptions/BugReport.cfm b/system/exceptions/BugReport.cfm index 193d95fb2..6a34dab70 100644 --- a/system/exceptions/BugReport.cfm +++ b/system/exceptions/BugReport.cfm @@ -190,7 +190,7 @@ A reporting template about exceptions in your ColdBox Apps Host & Server: - #encodeForHTML( CGI.SERVER_NAME )# #encodeForHTML( local.thisInetHost )# + #encodeForHTML( CGI.HTTP_HOST )# #encodeForHTML( local.thisInetHost )# Query String: diff --git a/system/exceptions/Whoops.cfm b/system/exceptions/Whoops.cfm index e9719b3df..6563a6bd1 100644 --- a/system/exceptions/Whoops.cfm +++ b/system/exceptions/Whoops.cfm @@ -51,7 +51,7 @@ "Coldfusion ID" : "Session Scope Not Enabled", "Template Path" : CGI.CF_TEMPLATE_PATH, "Path Info" : CGI.PATH_INFO, - "Host" : CGI.SERVER_NAME, + "Host" : CGI.HTTP_HOST, "Server" : local.thisInetHost, "Query String" : CGI.QUERY_STRING, "Referrer" : CGI.HTTP_REFERER, From a80b03b1c463d1290b0985b2331c3c0674fd3527 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 13 Nov 2020 15:43:30 -0600 Subject: [PATCH 17/78] COLDBOX-937 Migration to cgi.server_name and server_port did not account for the incoming browser port but the cf service port --- system/web/config/ApplicationLoader.cfc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/system/web/config/ApplicationLoader.cfc b/system/web/config/ApplicationLoader.cfc index 3e1a6ad2d..a90821c3a 100644 --- a/system/web/config/ApplicationLoader.cfc +++ b/system/web/config/ApplicationLoader.cfc @@ -793,9 +793,10 @@ component accessors="true" { for ( var key in environments ) { // loop over patterns for ( var i = 1; i lte listLen( environments[ key ] ); i = i + 1 ) { - if ( reFindNoCase( listGetAt( environments[ key ], i ), CGI.SERVER_NAME ) ) { + if ( reFindNoCase( listGetAt( environments[ key ], i ), CGI.HTTP_HOST ) ) { // set new environment configStruct.environment = key; + break; } } } From 826ba8adb23bb5209ca5e042327222af6fff222c Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 13 Nov 2020 15:44:39 -0600 Subject: [PATCH 18/78] COLDBOX-942 #resolve Add timeout and timeUnit arguments to the allApply method directly COLDBOX-941 #resolve timeUnits had type mismatches when used in allApply COLDBOX-940 #resolve Performance optimizations for entire async package --- system/async/Future.cfc | 90 +++++++++++++----- tests/tmp/Ref1.cfc | 10 ++ tests/tmp/Ref2.cfc | 10 ++ tests/tmp/User.cfc | 55 +++++++++++ tests/tmp/test.cfm | 199 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 342 insertions(+), 22 deletions(-) create mode 100644 tests/tmp/Ref1.cfc create mode 100644 tests/tmp/Ref2.cfc create mode 100755 tests/tmp/User.cfc diff --git a/system/async/Future.cfc b/system/async/Future.cfc index 31bca953c..0cba0e66c 100644 --- a/system/async/Future.cfc +++ b/system/async/Future.cfc @@ -795,7 +795,7 @@ component accessors="true" { } /** - * This function can accept an array of items or a struct and apply a function + * This function can accept an array of items or a struct of items and apply a function * to each of the item's in parallel. The `fn` argument receives the appropriate item * and must return a result. Consider this a parallel map() operation * @@ -806,52 +806,98 @@ component accessors="true" { * allApply( data, ( item ) => item.key & item.value.toString() ) * * - * @items An array to process - * @fn The function that will be applied to each of the array's items + * @items An array or struct to process in parallel + * @fn The function that will be applied to each of the collection's items * @executor The custom executor to use if passed, else the forkJoin Pool + * @timeout The timeout to use when waiting for each item to be processed + * @timeUnit The time unit to use, available units are: days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds * - * @return An array with the items processed + * @throws UnsuportedCollectionException - When something other than an array or struct is passed as items + * @return An array or struct with the items processed in parallel */ - any function allApply( any items, required fn, executor ){ - var incomingExecutor = arguments.executor ?: ""; + any function allApply( items, fn, executor, timeout, timeUnit ){ + var incomingExecutor = arguments.executor ?: ""; + // Boolean indicator to avoid `isObject()` calls on iterations + var usingExecutor = isObject( incomingExecutor ); + // Cast it here, so it only happens once + var currentTimeout = javacast( "long", arguments.timeout ?: variables.futureTimeout.timeout ); + var currentTimeunit = arguments.timeUnit ?: variables.futureTimeout.timeUnit; + + // Create the function proxy once instead of many times during iterations + var jApply = createDynamicProxy( + new proxies.Function( + arguments.fn, + variables.debug, + variables.loadAppContext + ), + [ "java.util.function.Function" ] + ); // Array Processing if( isArray( arguments.items ) ){ + // Return the array as a collection of processed values return arguments.items + // Startup the tasks .map( function( thisItem ){ - if ( isObject( incomingExecutor ) ) { - return new Future( thisItem ).thenAsync( fn, incomingExecutor ); + // Create a new completed future + var f = new Future( arguments.thisItem ); + + // Execute it on a custom executor or core + if( usingExecutor ){ + return f.setNative( + f.getNative().thenApplyAsync( jApply, incomingExecutor ) + ); } - return new Future( thisItem ).thenAsync( fn ); + + // Core Executor + return f.setNative( + f.getNative().thenApplyAsync( jApply ) + ); } ) + // Collect the tasks .map( function( thisFuture ){ return arguments.thisFuture.get( - javacast( "long", variables.futureTimeout.timeout ), - arguments.thisFuture.$timeUnit.get( variables.futureTimeout.timeUnit ) + currentTimeout, + currentTimeunit ); } ); } // Struct Processing else if( isStruct( arguments.items ) ){ return arguments.items + // Startup the tasks .map( function( key, value ){ - if ( isObject( incomingExecutor ) ) { - return new Future( { - "key" : arguments.key, - "value" : arguments.value - } ).thenAsync( fn, incomingExecutor ); - } - return new Future( { - "key" : arguments.key, + // Create a new completed future + var f = new Future( { + "key" : arguments.key, "value" : arguments.value - } ).thenAsync( fn ); + } ); + + // Execute it on a custom executor or core + if( usingExecutor ){ + return f.setNative( + f.getNative().thenApplyAsync( jApply, incomingExecutor ) + ); + } + + // Core Executor + return f.setNative( + f.getNative().thenApplyAsync( jApply ) + ); } ) + // Collect the tasks .map( function( key, thisFuture ){ return arguments.thisFuture.get( - javacast( "long", variables.futureTimeout.timeout ), - arguments.thisFuture.$timeUnit.get( variables.futureTimeout.timeUnit ) + currentTimeout, + currentTimeunit ); } ); + } else{ + throw( + message : "The collection type passed is not yet supported!", + type : "UnsuportedCollectionException", + detail : getMetadata( arguments.items ) + ); } } diff --git a/tests/tmp/Ref1.cfc b/tests/tmp/Ref1.cfc new file mode 100644 index 000000000..739b3a245 --- /dev/null +++ b/tests/tmp/Ref1.cfc @@ -0,0 +1,10 @@ +component{ + + property name="name"; + + function init(){ + variables.pool = {}; + return this; + } + +} \ No newline at end of file diff --git a/tests/tmp/Ref2.cfc b/tests/tmp/Ref2.cfc new file mode 100644 index 000000000..739b3a245 --- /dev/null +++ b/tests/tmp/Ref2.cfc @@ -0,0 +1,10 @@ +component{ + + property name="name"; + + function init(){ + variables.pool = {}; + return this; + } + +} \ No newline at end of file diff --git a/tests/tmp/User.cfc b/tests/tmp/User.cfc new file mode 100755 index 000000000..a33c90c6c --- /dev/null +++ b/tests/tmp/User.cfc @@ -0,0 +1,55 @@ +component persistent = "true" table = "users"{ + + property name="ref1" inject="tests.tmp.Ref1"; + property name="ref2" inject="tests.tmp.Ref2"; + property name="ref3" inject="tests.tmp.Ref2"; + property name="ref4" inject="tests.tmp.Ref2"; + property name="ref5" inject="tests.tmp.Ref2"; + property name="ref6" inject="tests.tmp.Ref2"; + + property name="ref7" inject="tests.tmp.Ref1"; + property name="ref8" inject="tests.tmp.Ref1"; + property name="ref9" inject="tests.tmp.Ref1"; + property name="ref10" inject="tests.tmp.Ref1"; + + property + name ="id" + column ="user_id" + fieldType="id" + generator="uuid"; + /** + * @display First Name + * @message Please provide firstname + * @NotEmpty + */ + property name="firstName"; + /** + * @display Last Name + * @message Please provide lastname + * @NotEmpty + */ + property name="lastName"; + property name="userName"; + property name="password"; + property name="lastLogin" ormtype="date"; + + // M20 -> Role + property + name ="role" + cfc ="Role" + fieldtype="many-to-one" + fkcolumn ="FKRoleID" + lazy ="true" + notnull ="false"; + + // DI Test + property + name ="testDI" + inject ="model:testService" + persistent="false" + required ="false"; + // property name="controller" inject="coldbox" persistent="false" required="false"; + + sleep( randRange( 1, 5 ) ); + +} diff --git a/tests/tmp/test.cfm b/tests/tmp/test.cfm index e69de29bb..460f5b98e 100755 --- a/tests/tmp/test.cfm +++ b/tests/tmp/test.cfm @@ -0,0 +1,199 @@ + + function injectState( state ){ + structAppend( variables, arguments.state, true ); + return this; + }; + function getObjectState(){ + return variables.filter( ( k, v ) => !isCustomFunction( v ) || !isClosure( v ) ); + } + + wirebox = new coldbox.system.ioc.Injector(); + mockdata = new testbox.system.modules.mockdatacfc.models.MockData(); + count = 1000; + + // Traditional Approach + sTime = getTickCount(); + data = mockData.mock( + $num : count, + fname : "name", + lname : "lname", + dob : "date", + id : "uuid", + password : "lorem" + ); + + results = []; + for( x=1 ; x lte count; x++ ){ + thisItem = wirebox.getInstance( "tests.tmp.User" ); + thisItem.injectState = variables.injectState; + + thisItem.injectState( data[ x ] ); + + results.append( thisItem ); + } + + writeDump( var={ + label: "getInstance() for loop", + value : "#getTickCount() - sTime#ms" + } ); + + /*****************************************************/ + /*****************************************************/ + /*****************************************************/ + // Using State Pattern Injection + + sTime = getTickCount(); + data = mockData.mock( + $num : count, + fname : "name", + lname : "lname", + dob : "date", + id : "uuid", + password : "lorem" + ); + + thisPrototype = wirebox.getInstance( "tests.tmp.User" ); + thisPrototype.injectState = variables.injectState; + thisPrototype.getObjectState = variables.getObjectState; + protoTypeState = thisProtoType.getObjectState(); + + results = []; + for( x=1 ; x lte count; x++ ){ + thisItem = new tests.tmp.User(); + thisItem.injectState = variables.injectState; + + // Inject Both States + thisItem.injectState( protoTypeState ); + thisItem.injectState( data[ x ] ); + + results.append( thisItem ); + } + + writeDump( var={ + label: "State Injection for loop", + value : "#getTickCount() - sTime#ms" + } ); + + + /*****************************************************/ + /*****************************************************/ + /*****************************************************/ + // Using State Pattern Injection + Futures with default 20 thread bound executor + + asyncManager = new coldbox.system.async.AsyncManager(); + + sTime = getTickCount(); + data = mockData.mock( + $num : count, + fname : "name", + lname : "lname", + dob : "date", + id : "uuid", + password : "lorem" + ); + + thisPrototype = wirebox.getInstance( "tests.tmp.User" ); + thisPrototype.injectState = variables.injectState; + thisPrototype.getObjectState = variables.getObjectState; + protoTypeState = thisProtoType.getObjectState(); + + results = asyncManager.newFuture().allApply( + data, + ( record ) => { + var thisItem = new tests.tmp.User(); + thisItem.injectState = variables.injectState; + + // Inject Both States + thisItem.injectState( protoTypeState ); + thisItem.injectState( record ); + + return thisItem; + } + ); + + writeDump( var={ + label: "State injection futures apply()", + value : "#getTickCount() - sTime#ms" + } ); + + + /*****************************************************/ + /*****************************************************/ + /*****************************************************/ + // Using State Pattern Injection + Futures with default 20 thread bound executor + + asyncManager = new coldbox.system.async.AsyncManager(); + executor = asyncManager.$executors.newCachedThreadPool(); + + sTime = getTickCount(); + data = mockData.mock( + $num : count, + fname : "name", + lname : "lname", + dob : "date", + id : "uuid", + password : "lorem" + ); + + thisPrototype = wirebox.getInstance( "tests.tmp.User" ); + thisPrototype.injectState = variables.injectState; + thisPrototype.getObjectState = variables.getObjectState; + protoTypeState = thisProtoType.getObjectState(); + + results = asyncManager.newFuture().allApply( + data, + ( record ) => { + var thisItem = new tests.tmp.User(); + thisItem.injectState = variables.injectState; + + // Inject Both States + thisItem.injectState( protoTypeState ); + thisItem.injectState( record ); + + return thisItem; + }, + executor + ); + + writeDump( var={ + label: "State injections futures apply() with custom executor", + value : "#getTickCount() - sTime#ms" + } ); + executor.shutdown(); + + /*****************************************************/ + /*****************************************************/ + /*****************************************************/ + // Using lucee map in parallel + + sTime = getTickCount(); + data = mockData.mock( + $num : count, + fname : "name", + lname : "lname", + dob : "date", + id : "uuid", + password : "lorem" + ); + + thisPrototype = wirebox.getInstance( "tests.tmp.User" ); + thisPrototype.injectState = variables.injectState; + thisPrototype.getObjectState = variables.getObjectState; + protoTypeState = thisProtoType.getObjectState(); + + results = data.map( ( record ) => { + var thisItem = new tests.tmp.User(); + thisItem.injectState = variables.injectState; + + // Inject Both States + thisItem.injectState( protoTypeState ); + thisItem.injectState( record ); + + return thisItem; + }, true ); + + writeDump( var={ + label: "State injection with Parallel Map", + value : "#getTickCount() - sTime#ms" + } ); + \ No newline at end of file From 97152d0e5b70d0d9460768ee043b7de20522d12f Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 13 Nov 2020 17:10:08 -0600 Subject: [PATCH 19/78] COLDBOX-938 #resolve `getFullURL()` is not accounting for app mappings --- system/web/context/RequestContext.cfc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/system/web/context/RequestContext.cfc b/system/web/context/RequestContext.cfc index 5fa75dcd4..b76bbc440 100644 --- a/system/web/context/RequestContext.cfc +++ b/system/web/context/RequestContext.cfc @@ -1161,11 +1161,12 @@ component serializable="false" accessors="true" { * Handles SES urls gracefully. */ string function getFullURL(){ + var appMapping = variables.controller.getSetting( "AppMapping" ); return arrayToList( [ isSSL() ? "https://" : "http://", - CGI.SERVER_NAME, - listFind( "80,443", CGI.SERVER_PORT ) ? "" : ":" & CGI.SERVER_PORT, + CGI.HTTP_HOST, + len( appMapping ) ? "/#appMapping#" : "", isSES() ? "" : "/index.cfm", CGI.PATH_INFO, CGI.QUERY_STRING != "" && CGI.PATH_INFO == "" ? "/" : "", From 5af8cad54a96abb3d167c6287f61e2fbeaac016d Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 13 Nov 2020 17:10:26 -0600 Subject: [PATCH 20/78] COLDBOX-938 `getFullURL()` is not accounting for app mappings --- .../specs/web/context/RequestContextTest.cfc | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/specs/web/context/RequestContextTest.cfc b/tests/specs/web/context/RequestContextTest.cfc index 7e5f04e75..3248a598c 100755 --- a/tests/specs/web/context/RequestContextTest.cfc +++ b/tests/specs/web/context/RequestContextTest.cfc @@ -19,8 +19,11 @@ component extends="coldbox.system.testing.BaseModelTest" { /* Init it */ mockController = getMockController() .$( "getSetting" ) - .$args( "modules" ) - .$results( props.modules ); + .$args( "modules" ) + .$results( props.modules ) + .$( "getSetting" ) + .$args( "AppMapping" ) + .$results( "" ); prepareMock( mockController.getInterceptorService() ); prepareMock( mockController.getWireBox() ); @@ -93,7 +96,6 @@ component extends="coldbox.system.testing.BaseModelTest" { expect( r ).toBe( "http://jfetmac/applications/coldbox/test-harness/index.cfm/contactus/3" ); } - function testGetModuleEntryPoint(){ var event = getRequestContext() .setSESEnabled( true ) @@ -903,6 +905,22 @@ component extends="coldbox.system.testing.BaseModelTest" { ); } + function testGetFullUrlWithAppMapping(){ + mockController.$( "getSetting" ) + .$args( "AppMapping" ) + .$results( "test-harness" ); + + var event = getRequestContext(); + + debug( event.getFullUrl() ); + expect( event.getFullUrl() ).toBeTypeOf( "url" ); + + var javaUrl = createObject( "java", "java.net.URL" ).init( event.getFullUrl() ); + expect( javaUrl.getPort() ).toBe( + listFind( "80,443", CGI.SERVER_PORT ) > 0 ? -1 : CGI.SERVER_PORT + ); + } + function testUrlMatches(){ var event = getRequestContext(); event.setPrivateValue( "currentRoutedURL", "/foo/bar/baz" ); From eab9db38bd5e16380940d06f20ac510057798575 Mon Sep 17 00:00:00 2001 From: Giancarlo Gomez Date: Wed, 18 Nov 2020 14:56:29 -0500 Subject: [PATCH 21/78] Update Binder.cfc When setting coldbox.autoMap to false and choosing to map a directory as follows: mapDirectory(packagePath="models",exclude="orm/*",process=true); The following error would occur: The parameter [binder] to function [process] is required but was not passed in. --- system/ioc/config/Binder.cfc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/ioc/config/Binder.cfc b/system/ioc/config/Binder.cfc index 1063606b1..d0371bb4d 100644 --- a/system/ioc/config/Binder.cfc +++ b/system/ioc/config/Binder.cfc @@ -318,7 +318,7 @@ Description : - + @@ -335,7 +335,7 @@ Description : for( var mapping in getCurrentMapping() ) { - mapping.process(); + mapping.process( binder=this, injector=instance.injector ); } return this; From 3a9bef43dbaa3e48d86a6f2ac4934a75f171ec42 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 18 Nov 2020 17:39:09 -0600 Subject: [PATCH 22/78] WIREBOX-98 #resolve Pass the current injector to the binder's life-cycle methods: onShutdown(), onLoad() --- system/ioc/Injector.cfc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system/ioc/Injector.cfc b/system/ioc/Injector.cfc index bd89b77d0..716b75cc4 100644 --- a/system/ioc/Injector.cfc +++ b/system/ioc/Injector.cfc @@ -258,7 +258,7 @@ component serializable="false" accessors="true" implements="coldbox.system.ioc.I // Check if binder has onLoad convention if( structKeyExists( variables.binder, "onLoad" ) ){ - variables.binder.onLoad(); + variables.binder.onLoad( this ); } // process mappings for metadata and initialization. @@ -289,17 +289,17 @@ component serializable="false" accessors="true" implements="coldbox.system.ioc.I // Check if binder has onShutdown convention if( structKeyExists( variables.binder, "onShutdown" ) ){ - variables.binder.onShutdown(); + variables.binder.onShutdown( this ); } // Is parent linked if( isObject( variables.parent ) ){ - variables.parent.shutdown(); + variables.parent.shutdown( this ); } // standalone cachebox? Yes, then shut it down baby! if( isCacheBoxLinked() ){ - variables.cacheBox.shutdown(); + variables.cacheBox.shutdown( this ); } // Remove from scope From 127003834e4cf8c0acbfdad7195129f66329c9e8 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 19 Nov 2020 14:35:14 -0600 Subject: [PATCH 23/78] More debuggin on invalid events for CI/Testing issues --- system/web/services/HandlerService.cfc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/system/web/services/HandlerService.cfc b/system/web/services/HandlerService.cfc index a6f588b16..60a5c91fd 100644 --- a/system/web/services/HandlerService.cfc +++ b/system/web/services/HandlerService.cfc @@ -452,8 +452,16 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { && !structKeyExists( controller, "mockController" ) // Verify this is a real and not a mock controller. ) { + var exceptionMessage = "The invalidEventHandler event (#variables.invalidEventHandler#) is also invalid: #arguments.event#"; + // Extra Debugging for illusive CI/Tests exceptions: Remove at one point if discovered. + variables.log.error( exceptionMessage, { + event : arguments.event, + registeredHandlers : variables.registeredHandlers, + fullEvent : ehBean.getFullEvent() + } ); + // Now throw the exception throw( - message : "The invalidEventHandler event (#variables.invalidEventHandler#) is also invalid: #arguments.event#", + message : exceptionMessage, type : "HandlerService.InvalidEventHandlerException" ); } From 54203fe6736f854f565b8be491a517f300a69ca8 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 19 Nov 2020 14:35:27 -0600 Subject: [PATCH 24/78] Fine tuning when failures happen in travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 93cec4fdf..2965fd51f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,10 +69,10 @@ after_failure: - cd $TRAVIS_BUILD_DIR # Get response from test server to see what went wrong - curl http://localhost:8599/tests/runner.cfm?reporter=text - - curl http://localhost:8599/tests/tools/IDEDictionaries/builderDictionary.cfm?text=true + #- curl http://localhost:8599/tests/tools/IDEDictionaries/builderDictionary.cfm?text=true # Spit out our Commandbox log in case we need to debug - box server log name=$ENGINE - - cat `box system-log` + #- cat `box system-log` - ls -lr $TRAVIS_BUILD_DIR deploy: From 93bffe7a51497e737180a2263ad49810e0939d30 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 19 Nov 2020 14:35:43 -0600 Subject: [PATCH 25/78] cleanups --- tests/suites/eventCachingCollisions/config/Coldbox.cfc | 2 +- tests/suites/eventCachingCollisions/config/routes.cfm | 4 ++-- tests/suites/loadtests/5load/config/Coldbox.cfc | 2 +- tests/suites/loadtests/6load/config/Coldbox.cfc | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/suites/eventCachingCollisions/config/Coldbox.cfc b/tests/suites/eventCachingCollisions/config/Coldbox.cfc index 8c34e00c1..ec0820bf6 100755 --- a/tests/suites/eventCachingCollisions/config/Coldbox.cfc +++ b/tests/suites/eventCachingCollisions/config/Coldbox.cfc @@ -48,7 +48,7 @@ // environment settings, create a detectEnvironment() method to detect it yourself. // create a function with the name of the environment so it can be executed if that environment is detected - // the value of the environment is a list of regex patterns to match the CGI.SERVER_NAME. + // the value of the environment is a list of regex patterns to match the http host. environments = { }; diff --git a/tests/suites/eventCachingCollisions/config/routes.cfm b/tests/suites/eventCachingCollisions/config/routes.cfm index d950ef535..a8d447a95 100755 --- a/tests/suites/eventCachingCollisions/config/routes.cfm +++ b/tests/suites/eventCachingCollisions/config/routes.cfm @@ -58,9 +58,9 @@ NOTE: The interceptor will create a new setting called: sesBaseURL with this val Else, htmlBaseURL and sesBaseURL should be the same. ---> - + - + + cm = createEmptyMock( "coldbox.system.cache.providers.MockProvider" ); cm.$( "getEventCacheKeyPrefix", "mock" ); - facade = new coldbox.system.cache.util.EventURLFacade( cm ); + facade = prepareMock( new coldbox.system.cache.util.EventURLFacade( cm ) ) + .$( "buildAppLink", "http://localhost/test-harness" ); @@ -23,7 +25,9 @@ Request service Test var routedStruct = { name : "luis" }; /* Mocks */ - var context = createMock( "coldbox.system.web.context.RequestContext" ).setRoutedStruct( routedStruct ).setContext( { event : "main.index", id : "123" } ); + var context = createMock( "coldbox.system.web.context.RequestContext" ) + .setRoutedStruct( routedStruct ) + .setContext( { event : "main.index", id : "123" } ); var testHash = facade.getUniqueHash( context ); @@ -38,7 +42,7 @@ Request service Test var testargs = { "id" : 1, "name" : "luis" }; var target = { "incomingHash" : hash( testargs.toString() ), - "cgihost" : CGI.SERVER_NAME + "cgihost" : "http://localhost/test-harness" }; expect( testHash ).toBe( hash( target.toString() ) ); From 5503ab5bd3b0a683aaf240b3394d24604cd2a963 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 20 Nov 2020 10:23:10 -0600 Subject: [PATCH 29/78] COLDBOX-947 #resolve encapsulate processEception() from the bootstrap to within the exception objects COLDBOX-931 #resolve ProcessStackTrace() Creates Many Nested Spans --- system/Bootstrap.cfc | 93 +++++++------------ system/exceptions/BugReport.cfm | 2 +- system/exceptions/Whoops.cfm | 2 +- system/web/context/ExceptionBean.cfc | 133 ++++++++++++--------------- 4 files changed, 93 insertions(+), 137 deletions(-) diff --git a/system/Bootstrap.cfc b/system/Bootstrap.cfc index ebf70c279..853617e8b 100644 --- a/system/Bootstrap.cfc +++ b/system/Bootstrap.cfc @@ -25,11 +25,11 @@ component serializable="false" accessors="true"{ property name="COLDBOX_FAIL_FAST"; // param the properties with defaults - param name="COLDBOX_CONFIG_FILE" default=""; + param name="COLDBOX_CONFIG_FILE" default=""; param name="COLDBOX_APP_ROOT_PATH" default="#getDirectoryFromPath( getbaseTemplatePath() )#"; - param name="COLDBOX_APP_KEY" default="cbController"; - param name="COLDBOX_APP_MAPPING" default=""; - param name="appHash" default="#hash( getBaseTemplatePath() )#"; + param name="COLDBOX_APP_KEY" default="cbController"; + param name="COLDBOX_APP_MAPPING" default=""; + param name="appHash" default="#hash( getBaseTemplatePath() )#"; param name="lockTimeout" default="30" type="numeric"; param name="COLDBOX_FAIL_FAST" default="true"; @@ -46,7 +46,7 @@ component serializable="false" accessors="true"{ required string COLDBOX_APP_ROOT_PATH, string COLDBOX_APP_KEY, string COLDBOX_APP_MAPPING="", - any COLDBOX_FAIL_FAST=true + any COLDBOX_FAIL_FAST =true ){ // Set vars for two main locations setCOLDBOX_CONFIG_FILE( arguments.COLDBOX_CONFIG_FILE ); @@ -84,7 +84,7 @@ component serializable="false" accessors="true"{ ); throw( message = "Cannot find the '/'coldbox' mapping", - detail = "It seems that you do not have a '/coldbox' mapping in your application and we cannot continue to process the request. + detail = "It seems that you do not have a '/coldbox' mapping in your application and we cannot continue to process the request. The good news is that you can easily resolve this by either creating a mapping in your Admnistrator or in this application's Application.cfc that points to this directory: '#coldboxDirectory#'. You can also copy the code snippet below to add to your Application.cfc's pseudo constructor: this.mappings[ '/coldbox' ] = '#coldboxDirectory#'", @@ -123,9 +123,9 @@ component serializable="false" accessors="true"{ * Request Reload procedures */ function reloadChecks(){ - var appKey = locateAppKey(); - var cbController = ""; - var needReinit = isfwReinit(); + var appKey = locateAppKey(); + var cbController = ""; + var needReinit = isfwReinit(); // Initialize the Controller If Needed, double locked if( NOT structkeyExists( application, appkey ) OR NOT application[ appKey ].getColdboxInitiated() OR needReinit ){ @@ -194,8 +194,8 @@ component serializable="false" accessors="true"{ var cbController = application[ locateAppKey() ]; } // Local references - var interceptorService = cbController.getInterceptorService(); - var cacheBox = cbController.getCacheBox(); + var interceptorService = cbController.getInterceptorService(); + var cacheBox = cbController.getCacheBox(); try{ // set request time, for info purposes @@ -209,14 +209,14 @@ component serializable="false" accessors="true"{ interceptorService.announce( "preProcess" ); if( len( cbController.getSetting( "RequestStartHandler" ) ) ){ cbController.runEvent( - event : cbController.getSetting( "RequestStartHandler" ), - prePostExempt : true + event : cbController.getSetting( "RequestStartHandler" ), + prePostExempt : true ); } //****** EVENT CACHING CONTENT DELIVERY *******/ - var refResults = {}; - var eCacheEntry = event.getEventCacheableEntry(); + var refResults = {}; + var eCacheEntry= event.getEventCacheableEntry(); // Verify if event caching item is in selected cache if( eCacheEntry.keyExists( "cachekey" ) ){ @@ -328,14 +328,14 @@ component serializable="false" accessors="true"{ // prepare storage entry var cacheEntry = { - renderedContent = renderedContent, - renderData = false, - contentType = defaultContentType, - encoding = "", - statusCode = "", - statusText = "", - isBinary = false, - responseHeaders = event.getResponseHeaders() + renderedContent= renderedContent, + renderData = false, + contentType = defaultContentType, + encoding = "", + statusCode = "", + statusText = "", + isBinary = false, + responseHeaders= event.getResponseHeaders() }; // is this a render data entry? If So, append data @@ -400,7 +400,6 @@ component serializable="false" accessors="true"{ } - /** * Verify if a reinit is sent */ @@ -409,7 +408,7 @@ component serializable="false" accessors="true"{ // CF Parm Structures just in case param name="FORM" default="#structNew()#"; - param name="URL" default="#structNew()#"; + param name="URL" default="#structNew()#"; // Check if app exists already in scope if( not structKeyExists( application, appKey ) ){ @@ -545,7 +544,7 @@ component serializable="false" accessors="true"{ // Execute interceptors var iData = { - sessionReference = arguments.sessionScope, + sessionReference = arguments.sessionScope, applicationReference = arguments.appScope }; cbController.getInterceptorService().announce( "sessionEnd", iData ); @@ -596,11 +595,11 @@ component serializable="false" accessors="true"{ */ private string function processException( required controller, required exception ){ // prepare exception facade object + app logger - var oException = new coldbox.system.web.context.ExceptionBean( arguments.exception ); - var appLogger = arguments.controller.getLogBox().getLogger( this ); - var event = arguments.controller.getRequestService().getContext(); - var rc = event.getCollection(); - var prc = event.getPrivateCollection(); + var oException= new coldbox.system.web.context.ExceptionBean( arguments.exception ); + var appLogger = arguments.controller.getLogBox().getLogger( this ); + var event = arguments.controller.getRequestService().getContext(); + var rc = event.getCollection(); + var prc = event.getPrivateCollection(); // Announce interception arguments.controller.getInterceptorService() @@ -637,8 +636,8 @@ component serializable="false" accessors="true"{ if( len( arguments.controller.getSetting( "AppMapping" ) ) ){ appLocation = appLocation & arguments.controller.getSetting( "AppMapping" ) & "/"; } - var bugReportRelativePath = appLocation & reReplace( customErrorTemplate, "^/", "" ); - var bugReportAbsolutePath = customErrorTemplate; + var bugReportRelativePath = appLocation & reReplace( customErrorTemplate, "^/", "" ); + var bugReportAbsolutePath = customErrorTemplate; // Show Bug Report savecontent variable="local.exceptionReport"{ @@ -660,33 +659,7 @@ component serializable="false" accessors="true"{ return local.exceptionReport; } - /** - * Process Stack trace for errors - */ - private function processStackTrace( str ){ - // Not using encodeForHTML() as it is too destructive and ruins whitespace chars and other stuff - arguments.str = HTMLEditFormat( arguments.str ); - - var aMatches = REMatchNoCase( "\(([^\)]+)\)", arguments.str ); - for( var aString in aMatches ){ - arguments.str = replacenocase( arguments.str, aString, "#aString#", "all" ); - } - var aMatches = REMatchNoCase( "\[([^\]]+)\]", arguments.str ); - for( var aString in aMatches ){ - arguments.str = replacenocase( arguments.str, aString, "#aString#", "all" ); - } - var aMatches = REMatchNoCase( "\$([^(\(|\:)]+)(\:|\()", arguments.str ); - for( var aString in aMatches ){ - arguments.str = replacenocase( arguments.str, aString, "#aString#", "all" ); - } - arguments.str = replace( arguments.str, chr( 13 ) & chr( 10 ), chr( 13 ) , 'all' ); - arguments.str = replace( arguments.str, chr( 10 ), chr( 13 ) , 'all' ); - arguments.str = replace( arguments.str, chr( 13 ), '
' , 'all' ); - arguments.str = replaceNoCase( arguments.str, chr(9), repeatString( " ", 4 ), "all" ); - return arguments.str; - } - - /** + /** * Process render data setup * @controller The ColdBox controller * @statusCode The status code to send diff --git a/system/exceptions/BugReport.cfm b/system/exceptions/BugReport.cfm index 6a34dab70..f86e7a118 100644 --- a/system/exceptions/BugReport.cfm +++ b/system/exceptions/BugReport.cfm @@ -148,7 +148,7 @@ A reporting template about exceptions in your ColdBox Apps

Stack Trace:

-
#processStackTrace( oException.getstackTrace() )#
+
#oException.processStackTrace( oException.getstackTrace() )#

FRAMEWORK SNAPSHOT:

diff --git a/system/exceptions/Whoops.cfm b/system/exceptions/Whoops.cfm index 6563a6bd1..05fb911a8 100644 --- a/system/exceptions/Whoops.cfm +++ b/system/exceptions/Whoops.cfm @@ -371,7 +371,7 @@ style="cursor: pointer"> -
#processStackTrace( oException.getstackTrace() )#
+
#oException.processStackTrace( oException.getstackTrace() )#
diff --git a/system/web/context/ExceptionBean.cfc b/system/web/context/ExceptionBean.cfc index 5984fb994..fa96d64c4 100644 --- a/system/web/context/ExceptionBean.cfc +++ b/system/web/context/ExceptionBean.cfc @@ -348,63 +348,22 @@ component accessors="true" { * @return The nicer trace */ function processStackTrace( required str ){ + // cfformat-ignore-start + // Not using encodeForHTML() as it is too destructive and ruins whitespace chars and other stuff - arguments.str = htmlEditFormat( arguments.str ); - - var aMatches = reMatchNoCase( "\(([^\)]+)\)", arguments.str ); - for ( var aString in aMatches ) { - arguments.str = replaceNoCase( - arguments.str, - aString, - "#aString#", - "all" - ); - } - var aMatches = reMatchNoCase( "\[([^\]]+)\]", arguments.str ); - for ( var aString in aMatches ) { - arguments.str = replaceNoCase( - arguments.str, - aString, - "#aString#", - "all" - ); - } - var aMatches = reMatchNoCase( - "\$([^(\(|\:)]+)(\:|\()", - arguments.str - ); - for ( var aString in aMatches ) { - arguments.str = replaceNoCase( - arguments.str, - aString, - "#aString#", - "all" - ); - } - arguments.str = replace( - arguments.str, - chr( 13 ) & chr( 10 ), - chr( 13 ), - "all" - ); - arguments.str = replace( - arguments.str, - chr( 10 ), - chr( 13 ), - "all" - ); - arguments.str = replace( - arguments.str, - chr( 13 ), - "
", - "all" - ); - arguments.str = replaceNoCase( - arguments.str, - chr( 9 ), - repeatString( " ", 4 ), - "all" - ); + arguments.str = HTMLEditFormat( arguments.str ); + // process functions e.g. $funcINDEX.runFunction( + arguments.str = reReplaceNoCase( arguments.str, "\$([^(\(|\:)]+)(\:|\()", "$\1(", "ALL" ); + // process characters within parentheses e.g. (ServletAuthenticationCallHandler.java:57) + arguments.str = reReplaceNoCase( arguments.str, "\(([^\)]+)\)", "(\1)", "ALL" ); + // process characters in brackets e.g. [hello world] + arguments.str = reReplaceNoCase( arguments.str, "\[([^\]]+)\]", "[\1]", "ALL" ); + arguments.str = replace( arguments.str, chr( 13 ) & chr( 10 ), chr( 13 ) , 'all' ); + arguments.str = replace( arguments.str, chr( 10 ), chr( 13 ) , 'all' ); + arguments.str = replace( arguments.str, chr( 13 ), '
' , 'all' ); + arguments.str = replaceNoCase( arguments.str, chr(9), repeatString( " ", 4 ), "all" ); + + // cfformat-ignore-end return arguments.str; } @@ -419,7 +378,8 @@ component accessors="true" { str = replace( str, arguments.item, - "#arguments.item#", + "#arguments.item# +", "all" ); } ); @@ -448,7 +408,9 @@ component accessors="true" { * @return The HTML of the scope data */ function displayScope( required scope ){ - var list = createObject( "java", "java.lang.StringBuilder" ).init( "" ); + var list = createObject( "java", "java.lang.StringBuilder" ).init( "
+ + " ); var orderedArr = arguments.scope; if ( structKeyExists( arguments.scope, "itemorder" ) ) { @@ -456,24 +418,37 @@ component accessors="true" { } for ( var i in orderedArr ) { - list.append( "" ); + list.append( " + " ); // Null Checks if ( !structKeyExists( arguments.scope , i ) || isNull( arguments.scope[ i ] ) ) { - arguments.scope[ i ] = "Java Null"; + arguments.scope[ i ] = " + Java Null + + "; } if ( isDate( arguments.scope[ i ] ) ) { - list.append( "" ); - list.append( " + " ); + list.append( "" ); + timeFormat( arguments.scope[ i ], "HH:mm:ss" ) & " + " ); } else if ( isSimpleValue( arguments.scope[ i ] ) ) { - list.append( "" ); - list.append( "" ); + list.append( " + " ); + list.append( " + " ); } else if( isObject( arguments.scope[ i ] ) ) { - list.append( "" ); - list.append( "" ); + list.append( " + " ); + list.append( " + " ); } else { savecontent variable="local.myContent" { try{ @@ -492,21 +467,29 @@ component accessors="true" { ) } } - list.append( "" ); - list.append( "" ); + list.append( " + " ); + list.append( " + " ); } - list.append( "" ); + list.append( " + + " ); } if ( !structCount( arguments.scope ) ) { list.append( " - - " ); + + + " ); } - list.append( "
" & i & "" & + list.append( "" & i & "" & dateFormat( arguments.scope[ i ], "mm/dd/yyyy" ) & " " & - timeFormat( arguments.scope[ i ], "HH:mm:ss" ) & "" & i & "" & ( len( arguments.scope[ i ] ) ? arguments.scope[ i ] : "---" ) & "" & i & " + " & ( len( arguments.scope[ i ] ) ? arguments.scope[ i ] : "--- + " ) & " + " & i & " [" & getMetaData( arguments.scope[ i ] ).name & "] Instance" & i & "[" & getMetaData( arguments.scope[ i ] ).name & "] Instance" & i & "" & local.myContent & "" & i & "" & local.myContent & "
- No details found! -
+ No details found! +
" ); + list.append( " + + +" ); return list.toString(); } From aa072d885601d3ebf71d1ab5e24bd7d236dd4a83 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 25 Nov 2020 09:44:22 -0600 Subject: [PATCH 30/78] COLDBOX-948 #resolve Whoops should validate a file exists before trying to present it to the code viewer --- system/exceptions/Whoops.cfm | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/system/exceptions/Whoops.cfm b/system/exceptions/Whoops.cfm index 05fb911a8..d4b76e03c 100644 --- a/system/exceptions/Whoops.cfm +++ b/system/exceptions/Whoops.cfm @@ -386,18 +386,21 @@ - - - + + + + + + From 1c10c45574e46b991c014d599047c7dcbd557fef Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 25 Nov 2020 14:53:15 -0600 Subject: [PATCH 31/78] fixing tests --- system/testing/BaseTestCase.cfc | 2 +- test-harness/handlers/main.cfc | 2 +- tests/specs/integration/EventExecutions.cfc | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/system/testing/BaseTestCase.cfc b/system/testing/BaseTestCase.cfc index c216ad362..84a157926 100755 --- a/system/testing/BaseTestCase.cfc +++ b/system/testing/BaseTestCase.cfc @@ -533,7 +533,7 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { // In either case, if the interceptor doesn't exists, just ignore it. } catch ( any e1 ) { // Are we doing exception handling? - if ( withExceptionHandling ) { + if ( arguments.withExceptionHandling ) { try { processException( cbController, e1 ); } catch ( any e2 ) { diff --git a/test-harness/handlers/main.cfc b/test-harness/handlers/main.cfc index 5783cece0..b4233f635 100644 --- a/test-harness/handlers/main.cfc +++ b/test-harness/handlers/main.cfc @@ -50,7 +50,7 @@ component { } function throwException( event, rc, prc ){ - throw( "Whoops!" ); + throw( message : "Whoops!", type : "CustomException" ); } /** diff --git a/tests/specs/integration/EventExecutions.cfc b/tests/specs/integration/EventExecutions.cfc index df1468d15..dbc40235c 100644 --- a/tests/specs/integration/EventExecutions.cfc +++ b/tests/specs/integration/EventExecutions.cfc @@ -64,13 +64,13 @@ component extends="tests.resources.BaseIntegrationTest"{ story( "I want the onException to have a default status code of 500", function(){ given( "an event that fires an exception", function(){ then( "it should default the status code to 500", function(){ - expect( function(){ - var e = execute( - event = "main.throwException", - renderResults = true, - withExceptionHandling = true - ); - } ).toThrow(); + var e = execute( + event = "main.throwException", + renderResults = true, + withExceptionHandling = true + ); + expecT( e.getPrivateValue( "exception" ).getType() ).toBe( "CustomException" ); + expecT( e.getPrivateValue( "exception" ).getMessage() ).toInclude( "Whoops" ); expect( getNativeStatusCode() ).toBe( 500 ); } ); } ); From 9752793b644566ed8a7fd2b2ad727f32d8b40e22 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 25 Nov 2020 15:13:50 -0600 Subject: [PATCH 32/78] LOGBOX-56 #resolve Missing line break on file appender control string --- system/logging/appenders/FileAppender.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/logging/appenders/FileAppender.cfc b/system/logging/appenders/FileAppender.cfc index 24387d801..b90b67eed 100644 --- a/system/logging/appenders/FileAppender.cfc +++ b/system/logging/appenders/FileAppender.cfc @@ -143,7 +143,7 @@ component accessors="true" extends="coldbox.system.logging.AbstractAppender"{ // Default Log Directory ensureDefaultLogDirectory(); // Create log file - fileWrite( variables.logFullPath, '"Severity","Appender","Date","Time","Category","Message"' ); + fileWrite( variables.logFullPath, '"Severity","Appender","Date","Time","Category","Message"#chr( 13 )##chr( 10 )#' ); } catch( Any e ) { $log( "ERROR", "Cannot create appender's: #getName()# log file. File #variables.logFullpath#. #e.message# #e.detail#" ); } From 3d72ae40b23af068867cb4f3c5b816a90e068687 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 25 Nov 2020 15:56:32 -0600 Subject: [PATCH 33/78] LOGBOX-58 #resolve New logbox config onShutdown() callback, which is called when LogBox has been shutdown LOGBOX-57 #resolve new shutdown() method to process graceful shutdown of LogBox LOGBOX-59 #resolve New shutdown() method can be now used in appenders that will be called when LogBox is shutdown --- system/ioc/Injector.cfc | 6 +++++ system/logging/AbstractAppender.cfc | 29 ++++++++++++++++------- system/logging/LogBox.cfc | 21 ++++++++++++++++ system/logging/appenders/FileAppender.cfc | 10 +++++++- system/web/services/LoaderService.cfc | 4 ++++ test-harness/Application.cfc | 2 +- 6 files changed, 61 insertions(+), 11 deletions(-) diff --git a/system/ioc/Injector.cfc b/system/ioc/Injector.cfc index 716b75cc4..07ee439e5 100644 --- a/system/ioc/Injector.cfc +++ b/system/ioc/Injector.cfc @@ -284,6 +284,7 @@ component serializable="false" accessors="true" implements="coldbox.system.ioc.I if( variables.log.canInfo() ){ variables.log.info( "Shutdown of Injector: #getInjectorID()# requested and started." ); } + // Notify Listeners variables.eventManager.announce( "beforeInjectorShutdown", iData ); @@ -313,6 +314,11 @@ component serializable="false" accessors="true" implements="coldbox.system.ioc.I variables.log.info( "Shutdown of injector: #getInjectorID()# completed." ); } + // Shutdown LogBox last if not in ColdBox Mode + if( !isColdBoxLinked() ){ + variables.logBox.shutdown(); + } + return this; } diff --git a/system/logging/AbstractAppender.cfc b/system/logging/AbstractAppender.cfc index 50cf96d81..f8d04b4f1 100644 --- a/system/logging/AbstractAppender.cfc +++ b/system/logging/AbstractAppender.cfc @@ -299,8 +299,10 @@ component accessors="true"{ /** * Executed by our schedule tasks to move the queue elements into the appender's implemented * destination + * + * @force This forces a flush with no waiting, usually called synchronously by a shutdown */ - function runLogListener(){ + function runLogListener( force = false ){ try { // Create a queue context for queue processing data var queueContext = { @@ -309,7 +311,8 @@ component accessors="true"{ "maxIdle" : 15000, "sleepInterval" : 25, "count" : 0, - "hasMessages" : false + "hasMessages" : false, + "force" : arguments.force }; // Init Message @@ -318,7 +321,7 @@ component accessors="true"{ // Start Advice onLogListenerStart( queueContext ); - while ( variables.logListener.queue.len() || queueContext.lastRun + queueContext.maxIdle > getTickCount() ) { + while ( arguments.force || variables.logListener.queue.len() || queueContext.lastRun + queueContext.maxIdle > getTickCount() ) { // out( "len: #variables.logListener.queue.len()# last run: #lastRun# idle: #queueContext.maxIdle#" ); if ( variables.logListener.queue.len() ) { @@ -339,8 +342,8 @@ component accessors="true"{ // Advice we are about to go to sleep onLogListenerSleep( queueContext ); - // Only take a nap if we've nothing to do - if( !variables.logListener.queue.len() ) { + // Only take a nap if we've nothing to do and we are not in force mode + if( !arguments.force && !variables.logListener.queue.len() ) { sleep( queueContext.sleepInterval ); // take a nap } } @@ -357,10 +360,12 @@ component accessors="true"{ // Advice //out( "Stopping Log listener task for (#getName()#), it ran for #getTickCount() - queueContext.start#ms!" ); - // Stop log listener - variables.lock( function(){ - variables.logListener.active = false; - } ); + // Stop log listener only if not in force mode + if( !arguments.force ){ + variables.lock( function(){ + variables.logListener.active = false; + } ); + } } } @@ -416,6 +421,12 @@ component accessors="true"{ return this; } + /** + * Each appender can shut itself down if needed. This callback is done by the LogBox engine during reinits or shutdowns + */ + function shutdown(){ + } + /****************************************** PRIVATE *********************************************/ /** diff --git a/system/logging/LogBox.cfc b/system/logging/LogBox.cfc index cc2b864e2..ac4c7ce69 100644 --- a/system/logging/LogBox.cfc +++ b/system/logging/LogBox.cfc @@ -161,6 +161,27 @@ component accessors="true"{ } } + /** + * Shutdown the injector gracefully by calling the shutdown events internally. + **/ + function shutdown(){ + + // Check if config has onShutdown convention + if( structKeyExists( variables.config, "onShutdown" ) ){ + variables.config.onShutdown( this ); + } + + // Shutdown Executors if not in ColdBox Mode + if( !isObject( variables.coldbox ) ){ + variables.asyncManager.shutdownAllExecutors( force = true ); + } + + // Shutdown appenders + variables.appenderRegistry.each( function( key, appender ){ + arguments.appender.shutdown(); + } ); + } + /** * Get the root logger object * diff --git a/system/logging/appenders/FileAppender.cfc b/system/logging/appenders/FileAppender.cfc index b90b67eed..85310a778 100644 --- a/system/logging/appenders/FileAppender.cfc +++ b/system/logging/appenders/FileAppender.cfc @@ -177,9 +177,10 @@ component accessors="true" extends="coldbox.system.logging.AbstractAppender"{ * @queueContext A struct of data attached to this processing queue thread */ function onLogListenerSleep( required struct queueContext ){ + var isFlushNeeded = ( arguments.queueContext.start + arguments.queueContext.flushInterval < getTickCount() ) || arguments.queueContext.force; // flush to disk every start + 1000ms if( - arguments.queueContext.start + arguments.queueContext.flushInterval < getTickCount() + isFlushNeeded && !isSimpleValue( arguments.queueContext.oFile ) ){ @@ -227,6 +228,13 @@ component accessors="true" extends="coldbox.system.logging.AbstractAppender"{ } } + /** + * Process a shutdown! + */ + function shutdown(){ + //runLogListener( force = true ); + } + /************************************ PRIVATE ************************************/ /** diff --git a/system/web/services/LoaderService.cfc b/system/web/services/LoaderService.cfc index fd00318cd..ec79a30c5 100644 --- a/system/web/services/LoaderService.cfc +++ b/system/web/services/LoaderService.cfc @@ -267,6 +267,10 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { variables.log.info( "† Shutting down ColdBox Task Scheduler..." ); asyncManager.shutdownAllExecutors( force = true ); + // Shutdown LogBox LAST + variables.log.info( "† Shutting down LogBox..." ); + variables.controller.getLogBox().shutdown(); + return this; } diff --git a/test-harness/Application.cfc b/test-harness/Application.cfc index 8bc484b66..10f8521f4 100644 --- a/test-harness/Application.cfc +++ b/test-harness/Application.cfc @@ -70,7 +70,7 @@ component { } // Bootstrap Reinit - if ( not structKeyExists( application, "cbBootstrap" ) or application.cbBootStrap.isfwReinit() ) { + if ( not structKeyExists( application, "cbBootstrap" ) or structKeyExists( url, "bsReinit" ) ) { lock name="coldbox.bootstrap_#this.name#" type="exclusive" timeout="5" throwonTimeout=true { structDelete( application, "cbBootStrap" ); onApplicationStart(); From 89c5d53a961a5cbc45abb0c19f600adbf091bae0 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 25 Nov 2020 16:53:58 -0600 Subject: [PATCH 34/78] no need for this redunant test that can mess up state. --- tests/specs/integration/MainTests.cfc | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/specs/integration/MainTests.cfc b/tests/specs/integration/MainTests.cfc index 7a2360237..a90cdefe4 100755 --- a/tests/specs/integration/MainTests.cfc +++ b/tests/specs/integration/MainTests.cfc @@ -18,20 +18,6 @@ expect( event.getValue( "cbox_rendered_content" ) ).toInclude( "Invalid Page" ); } ); - it( "can handle invalid onInvalidEvent handlers", function(){ - var originalInvalidEventHandler = getController().getSetting( "invalidEventHandler" ); - getController().setSetting( "invalidEventHandler", "notEvenAnAction" ); - try { - getController().getHandlerService().onConfigurationLoad(); - execute( event = "invalid:bogus.index", renderResults = true ); - fail( "The event handler was invalid and should have thrown an exception" ); - } catch ( HandlerService.InvalidEventHandlerException e ) { - expect( e.message ).toInclude( "is also invalid" ); - } finally { - getController().setSetting( "invalidEventHandler", originalInvalidEventHandler ); - getController().getHandlerService().onConfigurationLoad(); - } - } ); } ); } From 2979b19cac0ad76ab547ddb97ad374000923ad4b Mon Sep 17 00:00:00 2001 From: James Moberg Date: Wed, 2 Dec 2020 10:21:51 -0800 Subject: [PATCH 35/78] Update DateFormat Mask to use lowercase "d". DateFormat mask to use lowercase "d" due to ColdFusion 2021 breaking change. ("D" now outputs "day of year".) More info here: https://www.carehart.org/blog/client/index.cfm/2020/11/24/breaking_change_in_cf2021_dateformat_D_vs_d --- system/cache/report/skins/default/CacheReport.cfm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/cache/report/skins/default/CacheReport.cfm b/system/cache/report/skins/default/CacheReport.cfm index 511f9d2c2..37083e1b9 100644 --- a/system/cache/report/skins/default/CacheReport.cfm +++ b/system/cache/report/skins/default/CacheReport.cfm @@ -40,7 +40,7 @@ Last Reap
- #DateFormat(cacheStats.getlastReapDatetime(),"MMM-DD-YYYY")# + #DateFormat(cacheStats.getlastReapDatetime(),"mmm-dd-yyyy")# #TimeFormat(cacheStats.getlastReapDatetime(),"hh:mm:ss tt")#
@@ -125,4 +125,4 @@ #renderCacheContentReport(arguments.cacheName)# - \ No newline at end of file + From 1153c6f9709bd9fd36c97fb491d06cfdebc26986 Mon Sep 17 00:00:00 2001 From: James Moberg Date: Wed, 2 Dec 2020 10:26:10 -0800 Subject: [PATCH 36/78] Update DateFormat Mask to use lowercase "d". Fix DateFormat mask to use lowercase "d" due to ColdFusion 2021 breaking change. ("D" now outputs "day of year".) More info here: https://www.carehart.org/blog/client/index.cfm/2020/11/24/breaking_change_in_cf2021_dateformat_D_vs_d --- system/exceptions/BugReport.cfm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/exceptions/BugReport.cfm b/system/exceptions/BugReport.cfm index f86e7a118..09d762119 100644 --- a/system/exceptions/BugReport.cfm +++ b/system/exceptions/BugReport.cfm @@ -68,7 +68,7 @@ A reporting template about exceptions in your ColdBox Apps Timestamp: - #dateformat(now(), "MM/DD/YYYY")# #timeformat(now(),"hh:MM:SS TT")# + #dateformat(now(), "mm/dd/yyyy")# #timeformat(now(),"hh:mm:ss tt")# Event Details: @@ -155,7 +155,7 @@ A reporting template about exceptions in your ColdBox Apps - + From f020518d2da23f398b74c2ed49ed02a192078e9a Mon Sep 17 00:00:00 2001 From: James Moberg Date: Wed, 2 Dec 2020 10:29:15 -0800 Subject: [PATCH 37/78] Update DateFormat Mask to use lowercase "d". Fix DateFormat mask to use lowercase "d" due to ColdFusion 2021 breaking change. ("D" now outputs "day of year".) More info here: https://www.carehart.org/blog/client/index.cfm/2020/11/24/breaking_change_in_cf2021_dateformat_D_vs_d --- system/logging/appenders/FileAppender.cfc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/logging/appenders/FileAppender.cfc b/system/logging/appenders/FileAppender.cfc index 85310a778..8a3b44268 100644 --- a/system/logging/appenders/FileAppender.cfc +++ b/system/logging/appenders/FileAppender.cfc @@ -105,7 +105,7 @@ component accessors="true" extends="coldbox.system.logging.AbstractAppender"{ message = replace( message, chr(13), ' ', "all" ); // Entry string - entry = '"#severityToString( logEvent.getSeverity() )#","#getname()#","#dateformat( timestamp, "MM/DD/YYYY" )#","#timeformat( timestamp, "HH:MM:SS" )#","#loge.getCategory()#","#message#"'; + entry = '"#severityToString( logEvent.getSeverity() )#","#getname()#","#dateformat( timestamp, "mm/dd/yyyy" )#","#timeformat( timestamp, "HH:mm:ss" )#","#loge.getCategory()#","#message#"'; } // Queue it up @@ -250,4 +250,4 @@ component accessors="true" extends="coldbox.system.logging.AbstractAppender"{ return this; } -} \ No newline at end of file +} From 07d68996ada0db4d88fc029be2b17ad3d54d1890 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 4 Dec 2020 11:52:02 -0600 Subject: [PATCH 38/78] COLDBOX-949 #resolve better exception tracking for interceptor getProperty() calls that don't exist --- system/Interceptor.cfc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/system/Interceptor.cfc b/system/Interceptor.cfc index 63b699fd5..1b7064792 100755 --- a/system/Interceptor.cfc +++ b/system/Interceptor.cfc @@ -65,10 +65,22 @@ component extends="coldbox.system.FrameworkSupertype" serializable="false" acces * @property The property to retrieve * @defaultValue The default value to return if property does not exist * + * @throws InvalidPropertyException * @return The property value requested or the default value if not found */ any function getProperty( required property, defaultValue ){ - return ( structKeyExists( variables.properties, arguments.property ) ? variables.properties[ arguments.property ] : arguments.defaultValue ); + if( structKeyExists( variables.properties, arguments.property ) ){ + return variables.properties[ arguments.property ]; + } + + if( !isNull( arguments.defaultValue ) ){ + return arguments.defaultValue; + } + + throw( + message = "The requested property #arguments.property# does not exist.", + type = "InvalidPropertyException" + ); } /** From 184c8be7e2b0413a42d6d42ae228ea2c26203ac1 Mon Sep 17 00:00:00 2001 From: Dominic Watson Date: Mon, 7 Dec 2020 12:24:00 +0000 Subject: [PATCH 39/78] COLDBOX-951 delay calling getHttpRequestData() until necessary --- system/web/services/RoutingService.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/web/services/RoutingService.cfc b/system/web/services/RoutingService.cfc index 0be5f4963..50c0258a2 100644 --- a/system/web/services/RoutingService.cfc +++ b/system/web/services/RoutingService.cfc @@ -886,7 +886,6 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { var handler = ""; var action = ""; var newpath = ""; - var httpRequestData = getHTTPRequestData(); var rc = event.getCollection(); /** @@ -928,6 +927,7 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { } // Setup Relocation + var httpRequestData = getHTTPRequestData(); var relocationUrl = "#arguments.event.getSESbaseURL()##newpath##serializeURL( httpRequestData.content, arguments.event )#"; if ( httpRequestData.method eq "GET" ) { From 868ad06aed820a48139f96c6ff742801bc81cba9 Mon Sep 17 00:00:00 2001 From: Dominic Watson Date: Mon, 7 Dec 2020 12:29:21 +0000 Subject: [PATCH 40/78] COLDBOX-951 do not read BODY content from request when not necessary --- system/RestHandler.cfc | 6 +++--- system/exceptions/Whoops.cfm | 2 +- system/web/context/RequestContext.cfc | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/system/RestHandler.cfc b/system/RestHandler.cfc index b443b6258..ddbadc58d 100644 --- a/system/RestHandler.cfc +++ b/system/RestHandler.cfc @@ -87,7 +87,7 @@ component extends="EventHandler" { "Error calling #arguments.event.getCurrentEvent()#: #e.message# #e.detail#", { "_stacktrace" : e.stacktrace, - "httpData" : getHTTPRequestData() + "httpData" : getHTTPRequestData( false ) } ); @@ -220,7 +220,7 @@ component extends="EventHandler" { "Error in base handler (#arguments.faultAction#): #arguments.exception.message# #arguments.exception.detail#", { "_stacktrace" : arguments.exception.stacktrace, - "httpData" : getHTTPRequestData() + "httpData" : getHTTPRequestData( false ) } ); @@ -366,7 +366,7 @@ component extends="EventHandler" { // Log it log.warn( "InvalidHTTPMethod Execution of (#arguments.faultAction#): #arguments.event.getHTTPMethod()#", - getHTTPRequestData() + getHTTPRequestData( false ) ); // Setup Response diff --git a/system/exceptions/Whoops.cfm b/system/exceptions/Whoops.cfm index d4b76e03c..a08734157 100644 --- a/system/exceptions/Whoops.cfm +++ b/system/exceptions/Whoops.cfm @@ -337,7 +337,7 @@
- #oException.displayScope( getHTTPRequestData().headers )# + #oException.displayScope( getHTTPRequestData( false ).headers )#
diff --git a/system/web/context/RequestContext.cfc b/system/web/context/RequestContext.cfc index dd05ae32a..4666ff780 100644 --- a/system/web/context/RequestContext.cfc +++ b/system/web/context/RequestContext.cfc @@ -1758,7 +1758,7 @@ component serializable="false" accessors="true" { * @defaultValue The default value, if not found */ function getHTTPHeader( required header, defaultValue = "" ){ - var headers = getHTTPRequestData().headers; + var headers = getHTTPRequestData( false ).headers; // ADOBE FIX YOUR ISNULL BS if ( headers.keyExists( arguments.header ) ) { From dc3a7401f7db71aa56c084136e155fd85a8a54c5 Mon Sep 17 00:00:00 2001 From: Dominic Watson Date: Mon, 7 Dec 2020 12:33:09 +0000 Subject: [PATCH 41/78] COLDBOX-951 when reading the body content from the request, ensure we only read it once --- system/web/context/RequestContext.cfc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/system/web/context/RequestContext.cfc b/system/web/context/RequestContext.cfc index 4666ff780..d75884077 100644 --- a/system/web/context/RequestContext.cfc +++ b/system/web/context/RequestContext.cfc @@ -1741,14 +1741,17 @@ component serializable="false" accessors="true" { boolean json = false, boolean xml = false ){ - var content = getHTTPRequestData().content; - - // ToString() neccessary when body comes in as binary. - if ( arguments.json and isJSON( toString( content ) ) ) return deserializeJSON( toString( content ) ); - if ( arguments.xml and len( toString( content ) ) and isXML( toString( content ) ) ) - return xmlParse( toString( content ) ); + // Only read the content once + if ( !StructKeyExists( variables.privateContext, "_httpContent" ) ) { + variables.privateContext._httpContent = getHTTPRequestData().content; + if ( arguments.json and isJSON( toString( variables.privateContext._httpContent ) ) ) { + variables.privateContext._httpContent = deserializeJSON( toString( variables.privateContext._httpContent ) ); + } else if ( arguments.xml and len( toString( variables.privateContext._httpContent ) ) and isXML( toString( variables.privateContext._httpContent ) ) ) { + variables.privateContext._httpContent = xmlParse( toString( variables.privateContext._httpContent ) ); + } + } - return content; + return variables.privateContext._httpContent; } /** From 2e5840fdb75985395030c7892799fd7681999711 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 09:22:28 -0600 Subject: [PATCH 42/78] update to focal --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2965fd51f..78dde2fd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ branches: - development - master -dist: xenial +dist: focal services: - mysql #addons: From 545371082e2a9e24d2f2699629b3b05b67739fde Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 15:07:26 -0600 Subject: [PATCH 43/78] more work so it works on latest ubuntu. --- .cfconfig.json | 15 ++++++++------- .env.template | 9 ++++++--- .travis.yml | 13 +++++++++---- server-adobe@2021.json | 24 ++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 server-adobe@2021.json diff --git a/.cfconfig.json b/.cfconfig.json index ffd12ff52..ec35e5df8 100644 --- a/.cfconfig.json +++ b/.cfconfig.json @@ -16,15 +16,16 @@ "cacheDefaultObject": "default", "datasources": { "coolblog": { - "clientHostname": false, - "host": "localhost", - "dbdriver": "MySQL", - "password": "${DB_PASSWORD}", - "dsn": "${DB_CONNECTIONSTRING}", - "custom": "", + "dbdriver": "MySQL", + "dsn":"jdbc:mysql://{host}:{port}/{database}", + "custom":"useUnicode=true&characterEncoding=UTF8&serverTimezone=America%2FChicago&useLegacyDatetimeCode=true&autoReconnect=true&useSSL=false", + "host":"${DB_HOST:localhost}", "username": "${DB_USER:root}", + "password": "${DB_PASSWORD}", "database": "coolblog", - "port": "3306" + "port": "${DB_PORT:3306}", + "bundleName": "${DB_BUNDLENAME}", + "bundleVersion": "${DB_BUNDLEVERSION}" } }, "debuggingEnabled": false, diff --git a/.env.template b/.env.template index a02ee07eb..74293e34d 100644 --- a/.env.template +++ b/.env.template @@ -1,4 +1,7 @@ -DB_CLASS=com.mysql.jdbc.Driver -DB_CONNECTIONSTRING=jdbc:mysql://localhost:3306/coolblog?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true&autoConfigureForColdFusion=false&serverTimezone=UTC +DB_HOST=localhost +DB_PORT=3306 DB_USER=root -DB_PASSWORD= \ No newline at end of file +DB_PASSWORD=mysql +# Lucee Bundle Info: 5.1.40 IS THE ONLY ONE THAT WORKS FOR HIBERNATE to build databases +DB_BUNDLEVERSION=5.1.40 +DB_BUNDLENAME=com.mysql.cj \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 78dde2fd4..a8e63f2fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: java os: "linux" -group: deprecated-2017Q2 +dist: focal notifications: slack: @@ -21,7 +21,6 @@ branches: - development - master -dist: focal services: - mysql #addons: @@ -57,8 +56,14 @@ before_script: - echo "************************ Runtime Properties ********************" - cat build/build.properties - echo "************************ END Runtime Properties ********************" - # Starting server twice due to stupid LUCEE 5 bug. - - box server start name=$ENGINE debug=true + # Seed our .env + - echo "Seeding .env file" + - touch $TRAVIS_BUILD_DIR/.env + - printf "DB_HOST=localhost\n" >> $TRAVIS_BUILD_DIR/.env + - printf "DB_USER=root\n" >> $TRAVIS_BUILD_DIR/.env + - printf "DB_PASSWORD=\n" >> $TRAVIS_BUILD_DIR/.env + - printf "DB_BUNDLEVERSION=5.1.40\n" >> $TRAVIS_BUILD_DIR/.env + - printf "DB_BUNDLENAME=com.mysql.cj\n" >> $TRAVIS_BUILD_DIR/.env script: # Execute build via ANT diff --git a/server-adobe@2021.json b/server-adobe@2021.json new file mode 100644 index 000000000..b331f1d10 --- /dev/null +++ b/server-adobe@2021.json @@ -0,0 +1,24 @@ +{ + "app":{ + "cfengine":"adobe@2021", + "serverHomeDirectory":".engine/adobe2021" + }, + "name":"coldbox-adobe@2021", + "force":true, + "openBrowser":false, + "web":{ + "directoryBrowsing":true, + "http":{ + "port":"8599" + }, + "rewrites":{ + "enable":true + }, + "aliases":{ + "/coldbox":"./" + } + }, + "JVM":{ + "heapSize":"1024" + } +} \ No newline at end of file From 13b62a6ee6896e003cfdd96bd8039ac59c2d4641 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 15:39:09 -0600 Subject: [PATCH 44/78] more tests for travis and latest mysql --- .env.template | 3 +-- .travis.yml | 4 ++-- server-adobe@2021.json | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.env.template b/.env.template index 74293e34d..1de8d8c18 100644 --- a/.env.template +++ b/.env.template @@ -2,6 +2,5 @@ DB_HOST=localhost DB_PORT=3306 DB_USER=root DB_PASSWORD=mysql -# Lucee Bundle Info: 5.1.40 IS THE ONLY ONE THAT WORKS FOR HIBERNATE to build databases -DB_BUNDLEVERSION=5.1.40 +DB_BUNDLEVERSION=8.0.19 DB_BUNDLENAME=com.mysql.cj \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index a8e63f2fc..68bf1c99b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,10 +59,10 @@ before_script: # Seed our .env - echo "Seeding .env file" - touch $TRAVIS_BUILD_DIR/.env - - printf "DB_HOST=localhost\n" >> $TRAVIS_BUILD_DIR/.env + - printf "DB_HOST=127.0.0.1\n" >> $TRAVIS_BUILD_DIR/.env - printf "DB_USER=root\n" >> $TRAVIS_BUILD_DIR/.env - printf "DB_PASSWORD=\n" >> $TRAVIS_BUILD_DIR/.env - - printf "DB_BUNDLEVERSION=5.1.40\n" >> $TRAVIS_BUILD_DIR/.env + - printf "DB_BUNDLEVERSION=8.0.19\n" >> $TRAVIS_BUILD_DIR/.env - printf "DB_BUNDLENAME=com.mysql.cj\n" >> $TRAVIS_BUILD_DIR/.env script: diff --git a/server-adobe@2021.json b/server-adobe@2021.json index b331f1d10..b6ed15737 100644 --- a/server-adobe@2021.json +++ b/server-adobe@2021.json @@ -2,7 +2,7 @@ "app":{ "cfengine":"adobe@2021", "serverHomeDirectory":".engine/adobe2021" - }, + }, "name":"coldbox-adobe@2021", "force":true, "openBrowser":false, @@ -13,8 +13,8 @@ }, "rewrites":{ "enable":true - }, - "aliases":{ + }, + "aliases":{ "/coldbox":"./" } }, From 3951c15d5b77a194a663af86db83629b253f16aa Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 15:50:02 -0600 Subject: [PATCH 45/78] more work on automation --- .travis.yml | 6 ++++++ apidocs/box.json | 6 ++---- build/build.xml | 24 +----------------------- 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/.travis.yml b/.travis.yml index 68bf1c99b..57cad4813 100644 --- a/.travis.yml +++ b/.travis.yml @@ -66,6 +66,12 @@ before_script: - printf "DB_BUNDLENAME=com.mysql.cj\n" >> $TRAVIS_BUILD_DIR/.env script: + # install dependencies + - box install + - cd apidocs && box install + - cd $TRAVIS_BUILD_DIR + # Startup the server to test + - box server start serverConfigFile="server-${cfengine}.json" --debug # Execute build via ANT - ant -DisPreRelease=${COLDBOX_PRERELEASE} -Dcoldbox.version=$COLDBOX_VERSION -DisTravis=true -Dbuild.branch=$TRAVIS_BRANCH -Dbuild.number=$TRAVIS_BUILD_NUMBER -f build/build.xml $ANT_TARGET #- ls -l $TRAVIS_BUILD_DIR diff --git a/apidocs/box.json b/apidocs/box.json index 6417fbc43..df6906753 100644 --- a/apidocs/box.json +++ b/apidocs/box.json @@ -5,10 +5,8 @@ "dependencies":{ "docbox":"^2.0.7" }, - "devDependencies":{ - - }, + "devDependencies":{}, "installPaths":{ - "docbox":"docbox" + "docbox":"docbox/" } } \ No newline at end of file diff --git a/build/build.xml b/build/build.xml index 68310d88c..2a2617e30 100644 --- a/build/build.xml +++ b/build/build.xml @@ -75,7 +75,7 @@ - +s @@ -85,17 +85,6 @@ - - - - - - - - - - - @@ -612,15 +601,4 @@ encoding="UTF-8"/> - - - - - - - - - - - \ No newline at end of file From f1a701f01429522304b3356635049e7eab27ccdb Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 15:55:16 -0600 Subject: [PATCH 46/78] adding dependencies missing for dev --- box.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/box.json b/box.json index 9b6f9e698..246e2f7eb 100644 --- a/box.json +++ b/box.json @@ -6,7 +6,7 @@ "slug":"coldbox", "packageDirectory":"coldbox", "type":"mvc", - "keywords":"mvc,hmvc,conventions,coldbox", + "keywords":"mvc,hmvc,conventions,coldbox",d "homepage":"https://www.coldbox.org", "documentation":"https://coldbox.ortusbooks.com/", "repository":{ @@ -39,7 +39,10 @@ ] }, "devDependencies":{ - "testbox":"^4.1.0" + "testbox":"^4.1.0", + "commandbox-dotenv":"*", + "commandbox-cfconfig":"*", + "commandbox-cfformat":"*" }, "installPaths":{ "testbox":"testbox/" From d19c171b0de455f0e94de467fa5e7e0a420db802 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 16:43:08 -0600 Subject: [PATCH 47/78] typo --- box.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/box.json b/box.json index 246e2f7eb..67f202e13 100644 --- a/box.json +++ b/box.json @@ -6,7 +6,7 @@ "slug":"coldbox", "packageDirectory":"coldbox", "type":"mvc", - "keywords":"mvc,hmvc,conventions,coldbox",d + "keywords":"mvc,hmvc,conventions,coldbox", "homepage":"https://www.coldbox.org", "documentation":"https://coldbox.ortusbooks.com/", "repository":{ From 2bc6b539e8b65c3cd0683fb69c017cd4842a4a29 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 16:49:44 -0600 Subject: [PATCH 48/78] update typos --- build/build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.xml b/build/build.xml index 2a2617e30..50390f5d9 100644 --- a/build/build.xml +++ b/build/build.xml @@ -75,7 +75,7 @@ -s + From c4c994783116333c2f3666a751cecd1aa4fe0805 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 16:59:32 -0600 Subject: [PATCH 49/78] wrong engine --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 57cad4813..4b4c044d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -71,7 +71,7 @@ script: - cd apidocs && box install - cd $TRAVIS_BUILD_DIR # Startup the server to test - - box server start serverConfigFile="server-${cfengine}.json" --debug + - box server start serverConfigFile="server-${ENGINE}.json" --debug # Execute build via ANT - ant -DisPreRelease=${COLDBOX_PRERELEASE} -Dcoldbox.version=$COLDBOX_VERSION -DisTravis=true -Dbuild.branch=$TRAVIS_BRANCH -Dbuild.number=$TRAVIS_BUILD_NUMBER -f build/build.xml $ANT_TARGET #- ls -l $TRAVIS_BUILD_DIR From bdce87bb15d254c2b6d20d139f5319d56c290041 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 17:25:36 -0600 Subject: [PATCH 50/78] more tests --- .cfconfig.json | 4 ++-- .travis.yml | 19 ++++++++----------- box.json | 9 +++++---- build/build.xml | 3 --- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/.cfconfig.json b/.cfconfig.json index ec35e5df8..74fea4d25 100644 --- a/.cfconfig.json +++ b/.cfconfig.json @@ -19,9 +19,9 @@ "dbdriver": "MySQL", "dsn":"jdbc:mysql://{host}:{port}/{database}", "custom":"useUnicode=true&characterEncoding=UTF8&serverTimezone=America%2FChicago&useLegacyDatetimeCode=true&autoReconnect=true&useSSL=false", - "host":"${DB_HOST:localhost}", + "host":"${DB_HOST:127.0.0.1}", "username": "${DB_USER:root}", - "password": "${DB_PASSWORD}", + "password": "${DB_PASSWORD:}", "database": "coolblog", "port": "${DB_PORT:3306}", "bundleName": "${DB_BUNDLENAME}", diff --git a/.travis.yml b/.travis.yml index 4b4c044d8..5f597eae2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,19 +51,16 @@ before_script: - mysql -u root -e 'create database coolblog;' # import database - mysql -u root < tests/resources/coolblog.sql - # add our custom engine to our properties - - printf "\ncfengine=$ENGINE" >> build/build.properties - - echo "************************ Runtime Properties ********************" - - cat build/build.properties - - echo "************************ END Runtime Properties ********************" # Seed our .env - echo "Seeding .env file" - - touch $TRAVIS_BUILD_DIR/.env - - printf "DB_HOST=127.0.0.1\n" >> $TRAVIS_BUILD_DIR/.env - - printf "DB_USER=root\n" >> $TRAVIS_BUILD_DIR/.env - - printf "DB_PASSWORD=\n" >> $TRAVIS_BUILD_DIR/.env - - printf "DB_BUNDLEVERSION=8.0.19\n" >> $TRAVIS_BUILD_DIR/.env - - printf "DB_BUNDLENAME=com.mysql.cj\n" >> $TRAVIS_BUILD_DIR/.env + - touch .env + - printf "DB_HOST=127.0.0.1\n" >> .env + - printf "DB_PORT=3306\n" >> .env + - printf "DB_USER=root\n" >> .env + - printf "DB_PASSWORD=\n" >> .env + - printf "DB_BUNDLEVERSION=8.0.19\n" >> .env + - printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env + - cat .env script: # install dependencies diff --git a/box.json b/box.json index 67f202e13..ef06f1c07 100644 --- a/box.json +++ b/box.json @@ -39,10 +39,11 @@ ] }, "devDependencies":{ - "testbox":"^4.1.0", - "commandbox-dotenv":"*", - "commandbox-cfconfig":"*", - "commandbox-cfformat":"*" + "testbox":"^4.1.0", + "commandbox-dotenv":"*", + "commandbox-cfconfig":"*", + "commandbox-cfformat":"*", + "commandbox-docbox":"*" }, "installPaths":{ "testbox":"testbox/" diff --git a/build/build.xml b/build/build.xml index 50390f5d9..afaa3f3e2 100644 --- a/build/build.xml +++ b/build/build.xml @@ -47,9 +47,6 @@ - - - From 5c9d9efe88b1dda71890a0aef24ac996634a5b48 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 18:10:50 -0600 Subject: [PATCH 51/78] config for mysql 8 for lucee! I wish they would document their junk! --- .cfconfig.json | 38 ++++++++++++++++++++++++++++---------- .travis.yml | 11 ++--------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/.cfconfig.json b/.cfconfig.json index 74fea4d25..0ad4f1439 100644 --- a/.cfconfig.json +++ b/.cfconfig.json @@ -1,7 +1,14 @@ { + "ajaxDebugWindowEnabled": false, + "debuggingEnabled": false, + "debuggingReportExecutionTimes": false, + "inspectTemplate":"always", + "maxCFThreads":100, + "requestTimeout":"0,0,0,90", + "robustExceptionEnabled":true, "systemErr":"System", - "systemOut":"System", - "adminPassword" : "coldbox", + "systemOut":"System", + "whitespaceManagement":"white-space-pref", "caches": { "default": { "storage": "false", @@ -16,20 +23,31 @@ "cacheDefaultObject": "default", "datasources": { "coolblog": { + "allowAlter":true,lass + "allowCreate":true, + "allowDelete":true, + "allowDrop":true, + "allowGrant":true, + "allowInsert":true, + "allowRevoke":true, + "allowSelect":true, + "allowUpdate":true, + "blob":"false", + "clob":"false", + "connectionTimeout":"1", + "class":"{DB_CLASS:com.mysql.jdbc.Driver}", "dbdriver": "MySQL", "dsn":"jdbc:mysql://{host}:{port}/{database}", "custom":"useUnicode=true&characterEncoding=UTF8&serverTimezone=America%2FChicago&useLegacyDatetimeCode=true&autoReconnect=true&useSSL=false", "host":"${DB_HOST:127.0.0.1}", - "username": "${DB_USER:root}", - "password": "${DB_PASSWORD:}", + "username": "${DB_USER}", + "password": "${DB_PASSWORD}", "database": "coolblog", "port": "${DB_PORT:3306}", + "storage":"false", "bundleName": "${DB_BUNDLENAME}", - "bundleVersion": "${DB_BUNDLEVERSION}" + "bundleVersion": "${DB_BUNDLEVERSION}", + "validate":"false" } - }, - "debuggingEnabled": false, - "ajaxDebugWindowEnabled": false, - "debuggingReportExecutionTimes": false, - "maxCFThreads":100 + } } \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 5f597eae2..a182d9332 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,12 +23,6 @@ branches: services: - mysql -#addons: -# apt: -# packages: -# - mysql-server-5.6 -# - mysql-client-core-5.6 -# - mysql-client-5.6 before_install: # CommandBox Keys @@ -54,13 +48,12 @@ before_script: # Seed our .env - echo "Seeding .env file" - touch .env - - printf "DB_HOST=127.0.0.1\n" >> .env - - printf "DB_PORT=3306\n" >> .env - printf "DB_USER=root\n" >> .env - printf "DB_PASSWORD=\n" >> .env - printf "DB_BUNDLEVERSION=8.0.19\n" >> .env - printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env - - cat .env + # This is the key for MySQL 8 to work + - printf "DB_CLASS=com.mysql.cj.jdbc.Driver\n" >> .env script: # install dependencies From 3db0ee166d646cf9c7fe0aef0b00f8acee72e935 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 9 Dec 2020 18:14:46 -0600 Subject: [PATCH 52/78] typos --- .cfconfig.json | 2 +- build/Build.cfc | 272 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 build/Build.cfc diff --git a/.cfconfig.json b/.cfconfig.json index 0ad4f1439..4dfcb39aa 100644 --- a/.cfconfig.json +++ b/.cfconfig.json @@ -23,7 +23,7 @@ "cacheDefaultObject": "default", "datasources": { "coolblog": { - "allowAlter":true,lass + "allowAlter":true, "allowCreate":true, "allowDelete":true, "allowDrop":true, diff --git a/build/Build.cfc b/build/Build.cfc new file mode 100644 index 000000000..cf74dff98 --- /dev/null +++ b/build/Build.cfc @@ -0,0 +1,272 @@ +/** + * Build process for ColdBox Modules + * Adapt to your needs. + */ +component{ + + /** + * Constructor + */ + function init(){ + // Setup Pathing + variables.cwd = getCWD().reReplace( "\.$", "" ); + variables.artifactsDir = cwd & "/.artifacts"; + variables.buildDir = cwd & "/.tmp"; + variables.apiDocsURL = "http://localhost:60299/apidocs/"; + variables.testRunner = "http://localhost:60299/tests/runner.cfm"; + + // Source Excludes Not Added to final binary + variables.excludes = [ + ".gitignore", + ".travis.yml", + ".artifacts", + ".tmp", + "build", + "test-harness", + ".DS_Store", + ".git" + ]; + + // Cleanup + Init Build Directories + [ variables.buildDir, variables.artifactsDir ].each( function( item ){ + if( directoryExists( item ) ){ + directoryDelete( item, true ); + } + // Create directories + directoryCreate( item, true, true ); + } ); + + // Create Mappings + fileSystemUtil.createMapping( "coldbox", variables.cwd & "test-harness/coldbox" ); + + return this; + } + + /** + * Run the build process: test, build source, docs, checksums + * + * @projectName The project name used for resources and slugs + * @version The version you are building + * @buldID The build identifier + * @branch The branch you are building + */ + function run( + required projectName, + version="1.0.0", + buildID=createUUID(), + branch="development" + ){ + // Create project mapping + fileSystemUtil.createMapping( arguments.projectName, variables.cwd ); + + // Run the tests + runTests(); + + // Build the source + buildSource( argumentCollection=arguments ); + + // Build Docs + arguments.outputDir = variables.buildDir & "/apidocs"; + docs( argumentCollection=arguments ); + + // checksums + buildChecksums(); + + // Build latest changelog + latestChangelog(); + + // Finalize Message + print.line() + .boldMagentaLine( "Build Process is done! Enjoy your build!" ) + .toConsole(); + } + + /** + * Run the test suites + */ + function runTests(){ + // Tests First, if they fail then exit + print.blueLine( "Testing the package, please wait..." ).toConsole(); + + command( 'testbox run' ) + .params( + runner = variables.testRunner, + verbose = true, + outputFile = "build/results.json" + ) + .run(); + + // Check Exit Code? + if( shell.getExitCode() ){ + return error( "Cannot continue building, tests failed!" ); + } + } + + /** + * Build the source + * + * @projectName The project name used for resources and slugs + * @version The version you are building + * @buldID The build identifier + * @branch The branch you are building + */ + function buildSource( + required projectName, + version="1.0.0", + buildID=createUUID(), + branch="development" + ){ + // Build Notice ID + print.line() + .boldMagentaLine( "Building #arguments.projectName# v#arguments.version#+#arguments.buildID# from #cwd# using the #arguments.branch# branch." ) + .toConsole(); + + // Prepare exports directory + variables.exportsDir = variables.artifactsDir & "/#projectName#/#arguments.version#"; + directoryCreate( variables.exportsDir, true, true ); + + // Project Build Dir + variables.projectBuildDir = variables.buildDir & "/#projectName#"; + directoryCreate( variables.projectBuildDir, true, true ); + + // Copy source + print.blueLine( "Copying source to build folder..." ).toConsole(); + copy( variables.cwd, variables.projectBuildDir ); + + // Create build ID + fileWrite( "#variables.projectBuildDir#/#projectName#-#version#+#buildID#", "Built with love on #dateTimeFormat( now(), "full")#" ); + + // Updating Placeholders + print.greenLine( "Updating version identifier to #arguments.version#" ).toConsole(); + command( 'tokenReplace' ) + .params( + path = "/#variables.projectBuildDir#/**", + token = "@build.version@", + replacement = arguments.version + ) + .run(); + + print.greenLine( "Updating build identifier to #arguments.buildID#" ).toConsole(); + command( 'tokenReplace' ) + .params( + path = "/#variables.projectBuildDir#/**", + token = ( arguments.branch == "master" ? "@build.number@" : "+@build.number@" ), + replacement = ( arguments.branch == "master" ? arguments.buildID : "-snapshot" ) + ) + .run(); + + // zip up source + var destination = "#variables.exportsDir#/#projectName#-#version#.zip"; + print.greenLine( "Zipping code to #destination#" ).toConsole(); + cfzip( + action="zip", + file="#destination#", + source="#variables.projectBuildDir#", + overwrite=true, + recurse=true + ); + + // Copy box.json for convenience + fileCopy( "#variables.projectBuildDir#/box.json", variables.exportsDir ); + } + + /** + * Produce the API Docs + */ + function docs( required projectName, version="1.0.0", outputDir=".tmp/apidocs" ){ + // Generate Docs + print.greenLine( "Generating API Docs, please wait..." ).toConsole(); + directoryCreate( arguments.outputDir, true, true ); + + command( 'docbox generate' ) + .params( + "source" = "models", + "mapping" = "models", + "strategy-projectTitle" = "#arguments.projectName# v#arguments.version#", + "strategy-outputDir" = arguments.outputDir + ) + .run(); + + print.greenLine( "API Docs produced at #arguments.outputDir#" ).toConsole(); + + var destination = "#variables.exportsDir#/#projectName#-docs-#version#.zip"; + print.greenLine( "Zipping apidocs to #destination#" ).toConsole(); + cfzip( + action="zip", + file="#destination#", + source="#arguments.outputDir#", + overwrite=true, + recurse=true + ); + } + + /** + * Build the latest changelog file: changelog-latest.md + */ + function latestChangelog(){ + print.blueLine( "Building latest changelog..." ).toConsole(); + + if( !fileExists( variables.cwd & "changelog.md" ) ){ + return error( "Cannot continue building, changelog.md file doesn't exist!" ); + } + + fileWrite( + variables.cwd & "changelog-latest.md", + fileRead( variables.cwd & 'changelog.md' ).split( '----' )[2].trim() & chr( 13 ) & chr( 10 ) + ); + + print + .greenLine( "Latest changelog file created at `changelog-latest.md`" ) + .line() + .line( fileRead( variables.cwd & "changelog-latest.md" ) ); + } + + /********************************************* PRIVATE HELPERS *********************************************/ + + /** + * Build Checksums + */ + private function buildChecksums(){ + print.greenLine( "Building checksums" ).toConsole(); + command( 'checksum' ) + .params( path = '#variables.exportsDir#/*.zip', algorithm = 'SHA-512', extension="sha512", write=true ) + .run(); + command( 'checksum' ) + .params( path = '#variables.exportsDir#/*.zip', algorithm = 'md5', extension="md5", write=true ) + .run(); + } + + /** + * DirectoryCopy is broken in lucee + */ + private function copy( src, target, recurse=true ){ + // process paths with excludes + directoryList( src, false, "path", function( path ){ + var isExcluded = false; + variables.excludes.each( function( item ){ + if( path.replaceNoCase( variables.cwd, "", "all" ).findNoCase( item ) ){ + isExcluded = true; + } + } ); + return !isExcluded; + }).each( function( item ){ + // Copy to target + if( fileExists( item ) ){ + print.blueLine( "Copying #item#" ).toConsole(); + fileCopy( item, target ); + } else { + print.greenLine( "Copying directory #item#" ).toConsole(); + directoryCopy( item, target & "/" & item.replace( src, "" ), true ); + } + } ); + } + + /** + * Gets the last Exit code to be used + **/ + private function getExitCode() { + return (createObject( 'java', 'java.lang.System' ).getProperty( 'cfml.cli.exitCode' ) ?: 0); + + } + +} \ No newline at end of file From d88abb7faf90771b2a965024e72420cbac322040 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 10 Dec 2020 08:57:14 -0600 Subject: [PATCH 53/78] updates --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a182d9332..3ee72d1fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -149,4 +149,4 @@ after_deploy: # publish WireBox - cd $TRAVIS_BUILD_DIR/artifacts/wirebox/$COLDBOX_VERSION && box forgebox publish # publish LogBox - - cd $TRAVIS_BUILD_DIR/artifacts/logbox/$COLDBOX_VERSION && box forgebox publish + - cd $TRAVIS_BUILD_DIR/artifacts/logbox/$COLDBOX_VERSION && box forgebox publish \ No newline at end of file From 14be97d2ecd4697a2d08b24e652a9621d6cac0d8 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 10 Dec 2020 09:04:50 -0600 Subject: [PATCH 54/78] trying with host now --- .cfconfig.json | 2 +- .env.template | 3 ++- .travis.yml | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.cfconfig.json b/.cfconfig.json index 4dfcb39aa..d124226bd 100644 --- a/.cfconfig.json +++ b/.cfconfig.json @@ -35,7 +35,7 @@ "blob":"false", "clob":"false", "connectionTimeout":"1", - "class":"{DB_CLASS:com.mysql.jdbc.Driver}", + "class":"{DB_CLASS}", "dbdriver": "MySQL", "dsn":"jdbc:mysql://{host}:{port}/{database}", "custom":"useUnicode=true&characterEncoding=UTF8&serverTimezone=America%2FChicago&useLegacyDatetimeCode=true&autoReconnect=true&useSSL=false", diff --git a/.env.template b/.env.template index 1de8d8c18..69e19d5a4 100644 --- a/.env.template +++ b/.env.template @@ -1,6 +1,7 @@ -DB_HOST=localhost +DB_HOST=127.0.0.1 DB_PORT=3306 DB_USER=root DB_PASSWORD=mysql +DB_CLASS=com.mysql.jdbc.Driver DB_BUNDLEVERSION=8.0.19 DB_BUNDLENAME=com.mysql.cj \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 3ee72d1fd..c6624a981 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,6 +48,7 @@ before_script: # Seed our .env - echo "Seeding .env file" - touch .env + - printf "DB_HOST=127.0.0.1\n" >> .env - printf "DB_USER=root\n" >> .env - printf "DB_PASSWORD=\n" >> .env - printf "DB_BUNDLEVERSION=8.0.19\n" >> .env From 19438d4a567419a85a48ad569b2ed85d6f313bd4 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 10 Dec 2020 09:44:06 -0600 Subject: [PATCH 55/78] trying adding all configs --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c6624a981..96738355c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,7 +49,8 @@ before_script: - echo "Seeding .env file" - touch .env - printf "DB_HOST=127.0.0.1\n" >> .env - - printf "DB_USER=root\n" >> .env + - printf "DB_PORT=3306\n" >> .env + - printf "DB_USER=travis\n" >> .env - printf "DB_PASSWORD=\n" >> .env - printf "DB_BUNDLEVERSION=8.0.19\n" >> .env - printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env From b2a43c65baddb374989a7b564ceda36ed2a701d5 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 10 Dec 2020 09:56:00 -0600 Subject: [PATCH 56/78] mroe debugging --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 96738355c..07658bc77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,12 +60,14 @@ before_script: script: # install dependencies - box install - - cd apidocs && box install + #- cd apidocs && box install - cd $TRAVIS_BUILD_DIR # Startup the server to test - box server start serverConfigFile="server-${ENGINE}.json" --debug + - box cfconfig show + - curl http://localhost:8599/test-harness/index.cfm # Execute build via ANT - - ant -DisPreRelease=${COLDBOX_PRERELEASE} -Dcoldbox.version=$COLDBOX_VERSION -DisTravis=true -Dbuild.branch=$TRAVIS_BRANCH -Dbuild.number=$TRAVIS_BUILD_NUMBER -f build/build.xml $ANT_TARGET + #- ant -DisPreRelease=${COLDBOX_PRERELEASE} -Dcoldbox.version=$COLDBOX_VERSION -DisTravis=true -Dbuild.branch=$TRAVIS_BRANCH -Dbuild.number=$TRAVIS_BUILD_NUMBER -f build/build.xml $ANT_TARGET #- ls -l $TRAVIS_BUILD_DIR after_failure: From bff93472c38b5c0512a06d132a17497a2ed51f29 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 10 Dec 2020 10:08:44 -0600 Subject: [PATCH 57/78] COLDBOX-952 RendererEncapsulator: use of filter method for rendererVariables is inefficient --- system/web/RendererEncapsulator.cfm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/system/web/RendererEncapsulator.cfm b/system/web/RendererEncapsulator.cfm index ef09ed284..00b31d605 100644 --- a/system/web/RendererEncapsulator.cfm +++ b/system/web/RendererEncapsulator.cfm @@ -12,12 +12,12 @@ variables.renderedHelpers = {}; // Merge variables from renderer - variables.append( - attributes.rendererVariables.filter( function( key, value ){ - return !listFindNoCase( "local,attributes,arguments", arguments.key ); - } ), - true - ); + // Moved to simple for/loop to avoid closure memory issues and slowdowns + for( myVar in attributes.rendererVariables ) { + if( !listFindNoCase( "local,attributes,arguments", myVar ) ) { + variables[ myVar ] = attributes.rendererVariables[ myVar ]; + } + } // Localize context variables.event = attributes.event; From 6a742feaa14263e2c1d83df37d15db8a3ba756b9 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 10 Dec 2020 10:20:08 -0600 Subject: [PATCH 58/78] Jon is a HAWK!!!! Missing $ --- .cfconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cfconfig.json b/.cfconfig.json index d124226bd..b34a4341d 100644 --- a/.cfconfig.json +++ b/.cfconfig.json @@ -35,7 +35,7 @@ "blob":"false", "clob":"false", "connectionTimeout":"1", - "class":"{DB_CLASS}", + "class":"${DB_CLASS}", "dbdriver": "MySQL", "dsn":"jdbc:mysql://{host}:{port}/{database}", "custom":"useUnicode=true&characterEncoding=UTF8&serverTimezone=America%2FChicago&useLegacyDatetimeCode=true&autoReconnect=true&useSSL=false", From a2c0d4515292dbd9ae6836561e0065a82e4b0979 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 10 Dec 2020 10:26:48 -0600 Subject: [PATCH 59/78] yes eys yes finally working on mysql 8 thanks to @jclausen --- .travis.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 07658bc77..c56e8b18e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,8 +48,6 @@ before_script: # Seed our .env - echo "Seeding .env file" - touch .env - - printf "DB_HOST=127.0.0.1\n" >> .env - - printf "DB_PORT=3306\n" >> .env - printf "DB_USER=travis\n" >> .env - printf "DB_PASSWORD=\n" >> .env - printf "DB_BUNDLEVERSION=8.0.19\n" >> .env @@ -64,14 +62,13 @@ script: - cd $TRAVIS_BUILD_DIR # Startup the server to test - box server start serverConfigFile="server-${ENGINE}.json" --debug - - box cfconfig show - - curl http://localhost:8599/test-harness/index.cfm # Execute build via ANT - #- ant -DisPreRelease=${COLDBOX_PRERELEASE} -Dcoldbox.version=$COLDBOX_VERSION -DisTravis=true -Dbuild.branch=$TRAVIS_BRANCH -Dbuild.number=$TRAVIS_BUILD_NUMBER -f build/build.xml $ANT_TARGET + - ant -DisPreRelease=${COLDBOX_PRERELEASE} -Dcoldbox.version=$COLDBOX_VERSION -DisTravis=true -Dbuild.branch=$TRAVIS_BRANCH -Dbuild.number=$TRAVIS_BUILD_NUMBER -f build/build.xml $ANT_TARGET #- ls -l $TRAVIS_BUILD_DIR after_failure: - cd $TRAVIS_BUILD_DIR + - box cfconfig show # Get response from test server to see what went wrong - curl http://localhost:8599/tests/runner.cfm?reporter=text #- curl http://localhost:8599/tests/tools/IDEDictionaries/builderDictionary.cfm?text=true From 43f3af7aa6f3b0302b73f6e1e19956b16419beed Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 10 Dec 2020 10:40:55 -0600 Subject: [PATCH 60/78] COLDBOX-954 #resolve Refactor viewsHelperRef and layoutsHelperRef to local renderer variables instead of settings --- system/web/Renderer.cfc | 38 +++++++++++++----------- system/web/config/ApplicationLoader.cfc | 4 --- tests/specs/integration/RestfulTests.cfc | 3 +- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/system/web/Renderer.cfc b/system/web/Renderer.cfc index 7e01d13f3..cf8fa4fa3 100755 --- a/system/web/Renderer.cfc +++ b/system/web/Renderer.cfc @@ -51,7 +51,7 @@ component * @controller.inject coldbox */ function init( required controller ){ - // setup controller + // Register the Controller variables.controller = arguments.controller; // Register LogBox variables.logBox = arguments.controller.getLogBox(); @@ -65,14 +65,17 @@ component variables.wireBox = arguments.controller.getWireBox(); // Set Conventions, Settings and Properties - variables.layoutsConvention = variables.controller.getColdBoxSetting( "layoutsConvention" ); - variables.viewsConvention = variables.controller.getColdBoxSetting( "viewsConvention" ); - variables.appMapping = variables.controller.getSetting( "AppMapping" ); - variables.viewsExternalLocation = variables.controller.getSetting( "ViewsExternalLocation" ); - variables.layoutsExternalLocation = variables.controller.getSetting( "LayoutsExternalLocation" ); - variables.modulesConfig = variables.controller.getSetting( "modules" ); - variables.viewsHelper = variables.controller.getSetting( "viewsHelper" ); - variables.viewCaching = variables.controller.getSetting( "viewCaching" ); + variables.layoutsConvention = variables.controller.getColdBoxSetting( "layoutsConvention" ); + variables.viewsConvention = variables.controller.getColdBoxSetting( "viewsConvention" ); + variables.appMapping = variables.controller.getSetting( "AppMapping" ); + variables.viewsExternalLocation = variables.controller.getSetting( "ViewsExternalLocation" ); + variables.layoutsExternalLocation = variables.controller.getSetting( "LayoutsExternalLocation" ); + variables.modulesConfig = variables.controller.getSetting( "modules" ); + variables.viewsHelper = variables.controller.getSetting( "viewsHelper" ); + variables.viewCaching = variables.controller.getSetting( "viewCaching" ); + // Layouts + Views Reference Maps + variables.layoutsRefMap = {}; + variables.viewsRefMap = {}; // Verify View Helper Template extension + location if ( len( variables.viewsHelper ) ) { @@ -620,10 +623,12 @@ component // Check cached paths first if ( - structKeyExists( controller.getSetting( "layoutsRefMap" ), cbox_layoutLocationKey ) AND variables.isDiscoveryCaching + structKeyExists( variables.layoutsRefMap, cbox_layoutLocationKey ) + AND + variables.isDiscoveryCaching ) { lock name="#cbox_layoutLocationKey#.#lockName#" type="readonly" timeout="15" throwontimeout="true" { - cbox_layoutLocation = structFind( controller.getSetting( "layoutsRefMap" ), cbox_layoutLocationKey ); + cbox_layoutLocation = structFind( variables.layoutsRefMap, cbox_layoutLocationKey ); } } else { lock name="#cbox_layoutLocationKey#.#lockname#" type="exclusive" timeout="15" throwontimeout="true" { @@ -633,7 +638,7 @@ component explicitModule = cbox_explicitModule ); structInsert( - controller.getSetting( "layoutsRefMap" ), + variables.layoutsRefMap, cbox_layoutLocationKey, cbox_layoutLocation, true @@ -850,12 +855,11 @@ component var locationKey = arguments.view & arguments.module & arguments.explicitModule; var locationUDF = variables.locateView; var refMap = { viewPath : "", viewHelperPath : [] }; - var viewsRefMap = controller.getSetting( "viewsRefMap" ); // Check cached paths first ---> lock name="#locationKey#.#lockName#" type="readonly" timeout="15" throwontimeout="true" { - if ( structKeyExists( viewsRefMap, locationKey ) AND variables.isDiscoveryCaching ) { - return structFind( viewsRefMap, locationKey ); + if ( structKeyExists( variables.viewsRefMap, locationKey ) AND variables.isDiscoveryCaching ) { + return structFind( variables.viewsRefMap, locationKey ); } } @@ -896,10 +900,10 @@ component } // Lock and create view entry - if ( NOT structKeyExists( viewsRefMap, locationKey ) ) { + if ( NOT structKeyExists( variables.viewsRefMap, locationKey ) ) { lock name="#locationKey#.#lockName#" type="exclusive" timeout="15" throwontimeout="true" { structInsert( - viewsRefMap, + variables.viewsRefMap, locationKey, refMap, true diff --git a/system/web/config/ApplicationLoader.cfc b/system/web/config/ApplicationLoader.cfc index a90821c3a..288ba443e 100644 --- a/system/web/config/ApplicationLoader.cfc +++ b/system/web/config/ApplicationLoader.cfc @@ -233,10 +233,6 @@ component accessors="true" { // Incorporate their config.cfc settings and override structAppend( configStruct, coldboxSettings, true ); - // Common Structures for layouts and views - configStruct[ "layoutsRefMap" ] = {}; - configStruct[ "viewsRefMap" ] = {}; - /* ::::::::::::::::::::::::::::::::::::::::: COLDBOX SETTINGS :::::::::::::::::::::::::::::::::::::::::::: */ // Check the defaultEvent, if no length, default it diff --git a/tests/specs/integration/RestfulTests.cfc b/tests/specs/integration/RestfulTests.cfc index 93107a751..96105ecd2 100644 --- a/tests/specs/integration/RestfulTests.cfc +++ b/tests/specs/integration/RestfulTests.cfc @@ -10,8 +10,7 @@ component } ); it( "can handle allowed HTTP methods in action annotations", function(){ - prepareMock( getRequestContext() ).$( "getHTTPMethod", "POST" ); - var event = execute( event = "main.actionAllowedMethod", renderResults = true ); + var event = this.POST( "main.actionAllowedMethod" ); expect( event.getRenderedContent() ).toBe( "invalid http: main.actionAllowedMethod" ); } ); From dabe18b724b6e0b78c642fafd51ad9b01e980120 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 11 Dec 2020 17:13:46 -0600 Subject: [PATCH 61/78] WIREBOX-101 Completely rewrite of the Mapping object to script and performance optimizations --- system/ioc/config/Binder.cfc | 2374 +++++++++++------ system/ioc/config/DefaultBinder.cfc | 26 +- system/ioc/config/LogBox.cfc | 24 +- system/ioc/config/Mapping.cfc | 1824 +++++++------ system/ioc/config/Mixin.cfc | 15 +- tests/specs/ioc/config/BinderTest.cfc | 20 +- tests/specs/ioc/config/MappingTest.cfc | 13 +- .../ioc/config/samples/SampleWireBox.cfc | 2 +- tests/specs/ioc/config/samples/WireBox.cfc | 2 +- 9 files changed, 2546 insertions(+), 1754 deletions(-) diff --git a/system/ioc/config/Binder.cfc b/system/ioc/config/Binder.cfc index d0371bb4d..3325d9830 100644 --- a/system/ioc/config/Binder.cfc +++ b/system/ioc/config/Binder.cfc @@ -3,1119 +3,1869 @@ Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp www.ortussolutions.com ******************************************************************************** - Author : Luis Majano Date : 3/13/2009 Description : - This is a WireBox configuration binder object. You can use it to configure - a WireBox injector instance using our WireBox Mapping DSL. - This binder will hold all your object mappings, injector settings and more. +This is a WireBox configuration binder object. You can use it to configure +a WireBox injector instance using our WireBox Mapping DSL. +This binder will hold all your object mappings, injector settings and more. -----------------------------------------------------------------------> - - + - // Available WireBox public scopes - this.SCOPES = createObject("component","coldbox.system.ioc.Scopes"); - // Available WireBox public types - this.TYPES = createObject("component","coldbox.system.ioc.Types"); - // Utility class - this.UTILITY = createObject("component","coldbox.system.core.util.Util"); - // Contains the mappings currently being affected by the DSL. - currentMapping = []; - // Instance private scope - instance = {}; - // WireBox Defaults - DEFAULTS = { - //LogBox Defaults - logBoxConfig = "coldbox.system.ioc.config.LogBox", - // Scope Defaults - scopeRegistration = { - enabled = true, - scope = "application", - key = "wireBox" - }, - // CacheBox Integration Defaults - cacheBox = { - enabled = false, - configFile = "", - cacheFactory = "", - classNamespace = "coldbox.system.cache" - } - }; - // Startup the configuration - reset(); + // Available WireBox public scopes + this.SCOPES = createObject( "component", "coldbox.system.ioc.Scopes" ); + // Available WireBox public types + this.TYPES = createObject( "component", "coldbox.system.ioc.Types" ); + // Utility class + this.UTILITY = createObject( "component", "coldbox.system.core.util.Util" ); + // Contains the mappings currently being affected by the DSL. + currentMapping = []; + // Instance private scope + instance = {}; + // WireBox Defaults + DEFAULTS = { + // LogBox Defaults + logBoxConfig : "coldbox.system.ioc.config.LogBox", + // Scope Defaults + scopeRegistration : { enabled : true, scope : "application", key : "wireBox" }, + // CacheBox Integration Defaults + cacheBox : { + enabled : false, + configFile : "", + cacheFactory : "", + classNamespace : "coldbox.system.cache" + } + }; + // Startup the configuration + reset(); - - - - + + + + - // Setup incoming properties - instance.properties = arguments.properties; - // Setup Injector this binder is bound to. - instance.injector = arguments.injector; - // ColdBox Context binding if any? - instance.coldbox = instance.injector.getColdBox(); - // is coldbox linked - if( isObject(instance.coldbox) ){ - variables.appMapping = instance.coldbox.getSetting("AppMapping"); - } - - // If sent and a path, then create the data CFC - if( structKeyExists(arguments, "config") and isSimpleValue(arguments.config) ){ - arguments.config = createObject("component",arguments.config); - } - - // If sent and a data CFC instance - if( structKeyExists(arguments,"config") and isObject(arguments.config) ){ - // Decorate our data CFC - arguments.config.getPropertyMixin = this.utility.getMixerUtil().getPropertyMixin; - // Execute the configuration - arguments.config.configure(this); - // Load the raw data DSL - loadDataDSL( arguments.config.getPropertyMixin("wireBox","variables",structnew()) ); - } - - return this; + // Setup incoming properties + instance.properties = arguments.properties; + // Setup Injector this binder is bound to. + instance.injector = arguments.injector; + // ColdBox Context binding if any? + instance.coldbox = instance.injector.getColdBox(); + // is coldbox linked + if ( isObject( instance.coldbox ) ) { + variables.appMapping = instance.coldbox.getSetting( "AppMapping" ); + } + + // If sent and a path, then create the data CFC + if ( structKeyExists( arguments, "config" ) and isSimpleValue( arguments.config ) ) { + arguments.config = createObject( "component", arguments.config ); + } + + // If sent and a data CFC instance + if ( structKeyExists( arguments, "config" ) and isObject( arguments.config ) ) { + // Decorate our data CFC + arguments.config.getPropertyMixin = this.utility.getMixerUtil().getPropertyMixin; + // Execute the configuration + arguments.config.configure( this ); + // Load the raw data DSL + loadDataDSL( arguments.config.getPropertyMixin( "wireBox", "variables", structNew() ) ); + } + + return this; - - - + + + - - - + + + - - - + + + - - - + + + - + - // Main wirebox structure - variables.wirebox = {}; - // logBox File - instance.logBoxConfig = DEFAULTS.logBoxConfig; - // CacheBox integration - instance.cacheBox = DEFAULTS.cacheBox; - // Listeners - instance.listeners = []; - // Scope Registration - instance.scopeRegistration = DEFAULTS.scopeRegistration; - // Custom DSL namespaces - instance.customDSL = {}; - // Custom Storage Scopes - instance.customScopes = {}; - // Package Scan Locations - instance.scanLocations = createObject("java","java.util.LinkedHashMap").init(5); - // Object Mappings - instance.mappings = {}; - // Aspect Bindings - instance.aspectBindings = []; - // Parent Injector Mapping - instance.parentInjector = ""; - // Binding Properties - instance.properties = {}; - // Stop Recursion classes - instance.stopRecursions = []; - // Meatadata cache - instance.metadataCache = ''; + // Main wirebox structure + variables.wirebox = {}; + // logBox File + instance.logBoxConfig = DEFAULTS.logBoxConfig; + // CacheBox integration + instance.cacheBox = DEFAULTS.cacheBox; + // Listeners + instance.listeners = []; + // Scope Registration + instance.scopeRegistration = DEFAULTS.scopeRegistration; + // Custom DSL namespaces + instance.customDSL = {}; + // Custom Storage Scopes + instance.customScopes = {}; + // Package Scan Locations + instance.scanLocations = createObject( "java", "java.util.LinkedHashMap" ).init( 5 ); + // Object Mappings + instance.mappings = {}; + // Aspect Bindings + instance.aspectBindings = []; + // Parent Injector Mapping + instance.parentInjector = ""; + // Binding Properties + instance.properties = {}; + // Stop Recursion classes + instance.stopRecursions = []; + // Meatadata cache + instance.metadataCache = ""; - + - - - + + + - - + + - + - - - + + + - if( propertyExists(arguments.name) ){ - return instance.properties[arguments.name]; - } - if( structKeyExists(arguments,"default") ){ - return arguments.default; - } + if ( propertyExists( arguments.name ) ) { + return instance.properties[ arguments.name ]; + } + if ( structKeyExists( arguments, "default" ) ) { + return arguments.default; + } - - + + - - - - - + + + + + - - - - + + + + - + - - - + + + - - + + - + - + - - - + + + - - - - - - + + + + + + - - - - - - - - instance.mappings[ arguments.name ] = arguments.mapping; - - - - - - - - structDelete( instance.mappings, arguments.name ); - - + + + + + + + + instance.mappings[ arguments.name ] = arguments.mapping; + + + + + + + + structDelete( instance.mappings, arguments.name ); + + - - - - + + + + - + - - - - - + + + + + - var cName = listlast( arguments.path, "." ); + var cName = listLast( arguments.path, "." ); - if( arguments.prepend ){ - cName = arguments.namespace & cName; - } else { - cName = cName & arguments.namespace; - } + if ( arguments.prepend ) { + cName = arguments.namespace & cName; + } else { + cName = cName & arguments.namespace; + } - // directly map to a path - return map( cName, arguments.force ).to( arguments.path ); + // directly map to a path + return map( cName, arguments.force ).to( arguments.path ); - + - - - - - - - - - + + + + + + + + + - var directory = expandPath( "/#replace( arguments.packagePath, ".", "/", "all" )#" ); - var qObjects = ""; - var thisTargetPath = ""; - var tmpCurrentMapping = []; + var directory = expandPath( "/#replace( arguments.packagePath, ".", "/", "all" )#" ); + var qObjects = ""; + var thisTargetPath = ""; + var tmpCurrentMapping = []; - // Clear out any current mappings - currentMapping = []; + // Clear out any current mappings + currentMapping = []; - + - + - - - - + + + - - + + - + OR ( len( arguments.exclude ) AND NOT reFindNoCase( arguments.exclude, thisTargetPath ) ) + OR ( structKeyExists( arguments, "filter" ) AND arguments.filter( thisTargetPath ) ) + OR ( + NOT len( arguments.include ) AND NOT len( arguments.exclude ) AND NOT structKeyExists( + arguments, + "filter" + ) + )> - + - + - + - + - - - + - + - for( var mapping in getCurrentMapping() ) { - mapping.process( binder=this, injector=instance.injector ); - } - return this; + for ( var mapping in getCurrentMapping() ) { + mapping.process( binder = this, injector = instance.injector ); + } + return this; - - - + + + - // Clear out any current mappings - currentMapping = []; + // Clear out any current mappings + currentMapping = []; - // generate mapping entry for this dude. - var name = ""; - var x = 1; + // generate mapping entry for this dude. + var name = ""; + var x = 1; - // unflatten list - if( isSimpleValue( arguments.alias ) ){ arguments.alias = listToArray(arguments.alias); } + // unflatten list + if ( isSimpleValue( arguments.alias ) ) { + arguments.alias = listToArray( arguments.alias ); + } - // first entry - name = arguments.alias[1]; + // first entry + name = arguments.alias[ 1 ]; - // check if mapping exists, if so, just use and return. - if( structKeyExists( instance.mappings, name) and !arguments.force ){ - arrayAppend( currentMapping, instance.mappings[ name ] ); - return this; - } + // check if mapping exists, if so, just use and return. + if ( structKeyExists( instance.mappings, name ) and !arguments.force ) { + arrayAppend( currentMapping, instance.mappings[ name ] ); + return this; + } - // generate the mapping for the first name passed - instance.mappings[ name ] = createObject("component","coldbox.system.ioc.config.Mapping").init( name ); + // generate the mapping for the first name passed + instance.mappings[ name ] = createObject( "component", "coldbox.system.ioc.config.Mapping" ).init( name ); - // set the current mapping - arrayAppend( currentMapping, instance.mappings[ name ] ); + // set the current mapping + arrayAppend( currentMapping, instance.mappings[ name ] ); - // Set aliases, scopes and types - instance.mappings[ name ] - .setAlias( arguments.alias ) - .setType( this.TYPES.CFC ); + // Set aliases, scopes and types + instance.mappings[ name ].setAlias( arguments.alias ).setType( this.TYPES.CFC ); - // Loop and create alias references - for(x=2;x lte arrayLen(arguments.alias); x++){ - instance.mappings[ arguments.alias[x] ] = instance.mappings[ name ]; - } + // Loop and create alias references + for ( x = 2; x lte arrayLen( arguments.alias ); x++ ) { + instance.mappings[ arguments.alias[ x ] ] = instance.mappings[ name ]; + } - return this; + return this; - + - - + + - for( var mapping in getCurrentMapping() ) { - mapping.setPath( arguments.path ).setType( this.TYPES.CFC ); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.setPath( arguments.path ).setType( this.TYPES.CFC ); + } + return this; + + - + - // copy parent class's memento instance, exclude alias, name and path - for( var mapping in getCurrentMapping() ) { - mapping.processMemento( getMapping( arguments.alias ).getMemento(), "alias,name" ); - } - return this; + // copy parent class's memento instance, exclude alias, name and path + for ( var mapping in getCurrentMapping() ) { + mapping.processMemento( getMapping( arguments.alias ).getMemento(), "alias,name" ); + } + return this; - - - + + + - for( var mapping in getCurrentMapping() ) { - mapping.setType( this.TYPES.FACTORY ).setPath( arguments.factory ).setMethod( arguments.method ); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping + .setType( this.TYPES.FACTORY ) + .setPath( arguments.factory ) + .setMethod( arguments.method ); + } + return this; + + - - - - - - - - for( var mapping in getCurrentMapping() ) { - mapping.addDIMethodArgument(argumentCollection=arguments); - } - return this; - - + + + + + + + + for ( var mapping in getCurrentMapping() ) { + mapping.addDIMethodArgument( argumentCollection = arguments ); + } + return this; + + - - + + - for( var mapping in getCurrentMapping() ) { - mapping.setPath( arguments.path ).setType( this.TYPES.JAVA ); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.setPath( arguments.path ).setType( this.TYPES.JAVA ); + } + return this; + + - - + + - for( var mapping in getCurrentMapping() ) { - mapping.setPath( arguments.path ).setType( this.TYPES.WEBSERVICE ); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.setPath( arguments.path ).setType( this.TYPES.WEBSERVICE ); + } + return this; + + - - + + - for( var mapping in getCurrentMapping() ) { - mapping.setPath( arguments.path ).setType( this.TYPES.RSS ); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.setPath( arguments.path ).setType( this.TYPES.RSS ); + } + return this; + + - - + + - for( var mapping in getCurrentMapping() ) { - mapping.setDSL( arguments.dsl ).setType( this.TYPES.DSL ); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.setDSL( arguments.dsl ).setType( this.TYPES.DSL ); + } + return this; + + - - + + - for( var mapping in getCurrentMapping() ) { - mapping.setPath( arguments.provider ).setType( this.TYPES.PROVIDER ); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.setPath( arguments.provider ).setType( this.TYPES.PROVIDER ); + } + return this; + + - - + + - for( var mapping in getCurrentMapping() ) { - mapping.setValue( arguments.value ).setType( this.TYPES.CONSTANT ); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.setValue( arguments.value ).setType( this.TYPES.CONSTANT ); + } + return this; + + - - - - for( var mapping in getCurrentMapping() ) { - mapping.setConstructor( arguments.constructor ); - } - return this; - - + + + + for ( var mapping in getCurrentMapping() ) { + mapping.setConstructor( arguments.constructor ); + } + return this; + + - - - var key = ""; - for(key in arguments){ - for( var mapping in getCurrentMapping() ) { - mapping.addDIConstructorArgument(name=key,value=arguments[key]); - } + + + var key = ""; + for ( key in arguments ) { + for ( var mapping in getCurrentMapping() ) { + mapping.addDIConstructorArgument( name = key, value = arguments[ key ] ); } - return this; - - + } + return this; + + - - - for( var mapping in getCurrentMapping() ) { - mapping.setAutoInit( false ); - } - return this; - - - - - - - - for( var thisMapping in getCurrentMapping() ) { - thisMapping.setVirtualInheritance( mapping ); - } - return this; - - + + + for ( var mapping in getCurrentMapping() ) { + mapping.setAutoInit( false ); + } + return this; + + + + + + + + for ( var thisMapping in getCurrentMapping() ) { + thisMapping.setVirtualInheritance( mapping ); + } + return this; + + - - - for( var mapping in getCurrentMapping() ) { - mapping.setEagerInit( true ); - } - return this; - - + + + for ( var mapping in getCurrentMapping() ) { + mapping.setEagerInit( true ); + } + return this; + + - - - for( var mapping in getCurrentMapping() ) { - mapping.setAutowire( false ); - } - return this; - - + + + for ( var mapping in getCurrentMapping() ) { + mapping.setAutowire( false ); + } + return this; + + - - + + - if( mappingExists(arguments.alias) ){ - currentMapping = []; - currentMapping[ 1 ] = instance.mappings[arguments.alias]; - return this; - } - throw(message="The mapping '#arguments.alias# has not been initialized yet.'", - detail="Please use the map('#arguments.alias#') first to start working with a mapping", - type="Binder.InvalidMappingStateException"); + if ( mappingExists( arguments.alias ) ) { + currentMapping = []; + currentMapping[ 1 ] = instance.mappings[ arguments.alias ]; + return this; + } + throw( + message = "The mapping '#arguments.alias# has not been initialized yet.'", + detail = "Please use the map('#arguments.alias#') first to start working with a mapping", + type = "Binder.InvalidMappingStateException" + ); - + - - - - - - - + + + + + + + - for( var mapping in getCurrentMapping() ) { - mapping.addDIConstructorArgument(argumentCollection=arguments); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.addDIConstructorArgument( argumentCollection = arguments ); + } + return this; + + - - - - - - - - - for( var mapping in getCurrentMapping() ) { - mapping.addDISetter(argumentCollection=arguments); - } - return this; - - + + + + + + + + + for ( var mapping in getCurrentMapping() ) { + mapping.addDISetter( argumentCollection = arguments ); + } + return this; + + - - - - - - - - + + + + + + + + - for( var mapping in getCurrentMapping() ) { - mapping.addDIProperty(argumentCollection=arguments); - } - return this; - - + for ( var mapping in getCurrentMapping() ) { + mapping.addDIProperty( argumentCollection = arguments ); + } + return this; + + - - - - //inflate list - if( isSimpleValue(arguments.methods) ){ arguments.methods = listToArray(arguments.methods); } - // store list - for( var mapping in getCurrentMapping() ) { - mapping.setOnDIComplete( arguments.methods ); - } - return this; + + + + // inflate list + if ( isSimpleValue( arguments.methods ) ) { + arguments.methods = listToArray( arguments.methods ); + } + // store list + for ( var mapping in getCurrentMapping() ) { + mapping.setOnDIComplete( arguments.methods ); + } + return this; - + - - - + + + - for( var thisMapping in getCurrentMapping() ) { - thisMapping.addProviderMethod(argumentCollection=arguments); - } - return this; + for ( var thisMapping in getCurrentMapping() ) { + thisMapping.addProviderMethod( argumentCollection = arguments ); + } + return this; - + - - - - // check if invalid scope - if( NOT this.SCOPES.isValidScope(arguments.scope) AND NOT structKeyExists(instance.customScopes,arguments.scope) ){ - throw( message="Invalid WireBox Scope: '#arguments.scope#'", - detail="Please make sure you are using a valid scope, valid scopes are: #arrayToList(this.SCOPES.getValidScopes())# AND custom scopes: #structKeyList(instance.customScopes)#", - type="Binder.InvalidScopeMapping" ); - } - for( var mapping in getCurrentMapping() ) { - mapping.setScope( arguments.scope ); - } - return this; - - + + + + // check if invalid scope + if ( + NOT this.SCOPES.isValidScope( arguments.scope ) AND NOT structKeyExists( + instance.customScopes, + arguments.scope + ) + ) { + throw( + message = "Invalid WireBox Scope: '#arguments.scope#'", + detail = "Please make sure you are using a valid scope, valid scopes are: #arrayToList( this.SCOPES.getValidScopes() )# AND custom scopes: #structKeyList( instance.customScopes )#", + type = "Binder.InvalidScopeMapping" + ); + } + for ( var mapping in getCurrentMapping() ) { + mapping.setScope( arguments.scope ); + } + return this; + + - - - return this.into( this.SCOPES.SINGLETON ); + + + return this.into( this.SCOPES.SINGLETON ); - + - - - - for( var mapping in getCurrentMapping() ) { - mapping.setThreadSafe( true ); - } - return this; + + + + for ( var mapping in getCurrentMapping() ) { + mapping.setThreadSafe( true ); + } + return this; - + - - - - for( var mapping in getCurrentMapping() ) { - mapping.setThreadSafe( false ); - } - return this; + + + + for ( var mapping in getCurrentMapping() ) { + mapping.setThreadSafe( false ); + } + return this; - - - - - - - for( var mapping in getCurrentMapping() ) { - mapping.setInfluenceClosure( arguments.influenceClosure ); - } - return this; + + + + + + + for ( var mapping in getCurrentMapping() ) { + mapping.setInfluenceClosure( arguments.influenceClosure ); + } + return this; - + - - + + - for( var mapping in getCurrentMapping() ) { - mapping.setExtraAttributes( arguments.data ); - } - return this; + for ( var mapping in getCurrentMapping() ) { + mapping.setExtraAttributes( arguments.data ); + } + return this; - - - - - - - if( isSimpleValue( arguments.mixins ) ){ arguments.mixins = listToArray( arguments.mixins ); } - for( var mapping in getCurrentMapping() ) { - mapping.setMixins( arguments.mixins ); - } - return this; - - + + + + + + + if ( isSimpleValue( arguments.mixins ) ) { + arguments.mixins = listToArray( arguments.mixins ); + } + for ( var mapping in getCurrentMapping() ) { + mapping.setMixins( arguments.mixins ); + } + return this; + + - + - - - + + + - - - - // inflate incoming locations - if( isSimpleValue(arguments.classes) ){ arguments.classes = listToArray(arguments.classes); } - // Save them - instance.stopRecursions = arguments.classes; - - return this; + + + + // inflate incoming locations + if ( isSimpleValue( arguments.classes ) ) { + arguments.classes = listToArray( arguments.classes ); + } + // Save them + instance.stopRecursions = arguments.classes; + + return this; - + - + - - - - - + + + + + - + - - - + + + - + - - - + + + - - - - var x = 1; - - // inflate incoming locations - if( isSimpleValue(arguments.locations) ){ arguments.locations = listToArray(arguments.locations); } - - // Prepare Locations - for(x=1; x lte arrayLen(arguments.locations); x++){ - // Validate it is not registered already - if ( NOT structKeyExists(instance.scanLocations, arguments.locations[x]) AND len(arguments.locations[x]) ){ - // Process creation path & Absolute Path - instance.scanLocations[ arguments.locations[x] ] = expandPath( "/" & replace(arguments.locations[x],".","/","all") & "/" ); - } + + + + var x = 1; + + // inflate incoming locations + if ( isSimpleValue( arguments.locations ) ) { + arguments.locations = listToArray( arguments.locations ); + } + + // Prepare Locations + for ( x = 1; x lte arrayLen( arguments.locations ); x++ ) { + // Validate it is not registered already + if ( + NOT structKeyExists( instance.scanLocations, arguments.locations[ x ] ) AND len( + arguments.locations[ x ] + ) + ) { + // Process creation path & Absolute Path + instance.scanLocations[ arguments.locations[ x ] ] = expandPath( + "/" & replace( arguments.locations[ x ], ".", "/", "all" ) & "/" + ); } + } - return this; + return this; - + - - + + - var x = 1; + var x = 1; - // inflate incoming locations - if( isSimpleValue(arguments.locations) ){ arguments.locations = listToArray(arguments.locations); } + // inflate incoming locations + if ( isSimpleValue( arguments.locations ) ) { + arguments.locations = listToArray( arguments.locations ); + } - // Loop and remove - for(x=1;x lte arraylen(arguments.locations); x++ ){ - structDelete(instance.scanLocations, arguments.locations[x]); - } + // Loop and remove + for ( x = 1; x lte arrayLen( arguments.locations ); x++ ) { + structDelete( instance.scanLocations, arguments.locations[ x ] ); + } - + - - - - - - + + + + + + - - - + + + - - - - - + + + + + - for( var mapping in getCurrentMapping() ) { - // if key not passed, build a mapping name - if( NOT len(arguments.key) ){ - if( len( mapping.getPath() ) ){ - arguments.key = "wirebox-#mapping.getPath()#"; - } - else{ - arguments.key = "wirebox-#mapping.getName()#"; - } + for ( var mapping in getCurrentMapping() ) { + // if key not passed, build a mapping name + if ( NOT len( arguments.key ) ) { + if ( len( mapping.getPath() ) ) { + arguments.key = "wirebox-#mapping.getPath()#"; + } else { + arguments.key = "wirebox-#mapping.getName()#"; } - - // store the mapping info. - mapping.setScope( this.SCOPES.CACHEBOX ).setCacheProperties(argumentCollection=arguments); - } - return this; - - + // store the mapping info. + mapping.setScope( this.SCOPES.CACHEBOX ).setCacheProperties( argumentCollection = arguments ); + } + + return this; + + - - - + + + - - - - + + + + - + - - - - + + + + - + - - - + + + - + - - - - + + + + - - - + + + - + - - + + - + - - - + + + - + - - - - var wireBoxDSL = variables.wirebox; - var key = ""; - - // Coldbox Context Attached - if( isObject(instance.coldbox) ){ - // create scan location for model convention as the first one. - scanLocations( instance.coldbox.getSetting("ModelsInvocationPath") ); - } - - // Incoming raw DSL or use locally? - if ( structKeyExists(arguments,"rawDSL") ){ - wireBoxDSL = arguments.rawDSL; - } - - // Register LogBox Configuration - if( structKeyExists( wireBoxDSL, "logBoxConfig") ){ - logBoxConfig(wireBoxDSL.logBoxConfig); - } - - // Register Parent Injector - if( structKeyExists( wireBoxDSL, "parentInjector") ){ - parentInjector( wireBoxDSL.parentInjector ); - } - - // Register Server Scope Registration - if( structKeyExists( wireBoxDSL, "scopeRegistration") ){ - scopeRegistration(argumentCollection=wireBoxDSL.scopeRegistration); - } - - // Register CacheBox - if( structKeyExists( wireBoxDSL, "cacheBox") ){ - cacheBox(argumentCollection=wireBoxDSL.cacheBox); - } - - // Register metadataCache - if( structKeyExists( wireBoxDSL, "metadataCache") ){ - setMetadataCache( wireBoxDSL.metadataCache ); - } - - // Register Custom DSL - if( structKeyExists( wireBoxDSL, "customDSL") ){ - structAppend(instance.customDSL, wireBoxDSL.customDSL, true); - } - - // Register Custom Scopes - if( structKeyExists( wireBoxDSL, "customScopes") ){ - structAppend(instance.customScopes, wireBoxDSL.customScopes, true); - } - - // Append Register Scan Locations - if( structKeyExists( wireBoxDSL, "scanLocations") ){ - scanLocations( wireBoxDSL.scanLocations ); - } - - // Append Register Stop Recursions - if( structKeyExists( wireBoxDSL, "stopRecursions") ){ - stopRecursions( wireBoxDSL.stopRecursions ); - } - - // Register listeners - if( structKeyExists( wireBoxDSL, "listeners") ){ - for(key=1; key lte arrayLen(wireBoxDSL.listeners); key++ ){ - listener(argumentCollection=wireBoxDSL.listeners[key]); - } + + + + var wireBoxDSL = variables.wirebox; + var key = ""; + + // Coldbox Context Attached + if ( isObject( instance.coldbox ) ) { + // create scan location for model convention as the first one. + scanLocations( instance.coldbox.getSetting( "ModelsInvocationPath" ) ); + } + + // Incoming raw DSL or use locally? + if ( structKeyExists( arguments, "rawDSL" ) ) { + wireBoxDSL = arguments.rawDSL; + } + + // Register LogBox Configuration + if ( structKeyExists( wireBoxDSL, "logBoxConfig" ) ) { + logBoxConfig( wireBoxDSL.logBoxConfig ); + } + + // Register Parent Injector + if ( structKeyExists( wireBoxDSL, "parentInjector" ) ) { + parentInjector( wireBoxDSL.parentInjector ); + } + + // Register Server Scope Registration + if ( structKeyExists( wireBoxDSL, "scopeRegistration" ) ) { + scopeRegistration( argumentCollection = wireBoxDSL.scopeRegistration ); + } + + // Register CacheBox + if ( structKeyExists( wireBoxDSL, "cacheBox" ) ) { + cacheBox( argumentCollection = wireBoxDSL.cacheBox ); + } + + // Register metadataCache + if ( structKeyExists( wireBoxDSL, "metadataCache" ) ) { + setMetadataCache( wireBoxDSL.metadataCache ); + } + + // Register Custom DSL + if ( structKeyExists( wireBoxDSL, "customDSL" ) ) { + structAppend( instance.customDSL, wireBoxDSL.customDSL, true ); + } + + // Register Custom Scopes + if ( structKeyExists( wireBoxDSL, "customScopes" ) ) { + structAppend( + instance.customScopes, + wireBoxDSL.customScopes, + true + ); + } + + // Append Register Scan Locations + if ( structKeyExists( wireBoxDSL, "scanLocations" ) ) { + scanLocations( wireBoxDSL.scanLocations ); + } + + // Append Register Stop Recursions + if ( structKeyExists( wireBoxDSL, "stopRecursions" ) ) { + stopRecursions( wireBoxDSL.stopRecursions ); + } + + // Register listeners + if ( structKeyExists( wireBoxDSL, "listeners" ) ) { + for ( key = 1; key lte arrayLen( wireBoxDSL.listeners ); key++ ) { + listener( argumentCollection = wireBoxDSL.listeners[ key ] ); } - - // Register Mappings - if( structKeyExists( wireBoxDSL, "mappings") ){ - // iterate and register - for(key in wireboxDSL.mappings){ - // create mapping & process its data memento - map(key); - instance.mappings[ key ].processMemento( wireBoxDSL.mappings[key] ); - } + } + + // Register Mappings + if ( structKeyExists( wireBoxDSL, "mappings" ) ) { + // iterate and register + for ( key in wireboxDSL.mappings ) { + // create mapping & process its data memento + map( key ); + instance.mappings[ key ].processMemento( wireBoxDSL.mappings[ key ] ); } + } - + - - - + + + - + - - + + - + - var mappingError = ""; - instance.mappings.filter( function( key, thisMapping ){ + var mappingError = ""; + instance.mappings + .filter( function( key, thisMapping ){ return ( !thisMapping.isDiscovered() ); - } ).each( function( key, thisMapping ){ + } ) + .each( function( key, thisMapping ){ try { - // process the metadata - thisMapping.process( binder=this, injector=instance.injector ); - // is it eager? - if( thisMapping.isEagerInit() ){ - instance.injector.getInstance( thisMapping.getName() ); - } - } catch( any e ) { + // process the metadata + thisMapping.process( binder = this, injector = instance.injector ); + // is it eager? + if ( thisMapping.isEagerInit() ) { + instance.injector.getInstance( thisMapping.getName() ); + } + } catch ( any e ) { // Remove bad mapping instance.mappings.delete( key ); mappingError = e; } - } ); - if( !isSimpleValue( mappingError ) ) { - throw( object=mappingError ); - } - + if ( !isSimpleValue( mappingError ) ) { + throw( object = mappingError ); + } - + - + - - - - - + + + + + - // Name check? - if( NOT len(arguments.name) ){ - arguments.name = listLast(arguments.class,"."); - } - // add listener - arrayAppend(instance.listeners, arguments); - - if ( arguments.register ) { - getInjector().registerListener( arguments ); - } - - return this; + // Name check? + if ( NOT len( arguments.name ) ) { + arguments.name = listLast( arguments.class, "." ); + } + // add listener + arrayAppend( instance.listeners, arguments ); + + if ( arguments.register ) { + getInjector().registerListener( arguments ); + } + + return this; - + - + - - - - - // map eagerly - map(arguments.aspect).asEagerInit().asSingleton(); - - // register the aspect - for( var mapping in getCurrentMapping() ) { - mapping.setAspect( true ).setAspectAutoBinding( arguments.autoBinding ); - } + + + + + // map eagerly + map( arguments.aspect ).asEagerInit().asSingleton(); - return this; + // register the aspect + for ( var mapping in getCurrentMapping() ) { + mapping.setAspect( true ).setAspectAutoBinding( arguments.autoBinding ); + } + + return this; - - - - - - return createObject("component","coldbox.system.aop.Matcher").init(); - - - - - - - - - - // cleanup aspect - if( isSimpleValue(arguments.aspects) ){ arguments.aspects = listToArray(arguments.aspects); } - // register it - arrayAppend(instance.aspectBindings, arguments); + - return this; + + + + return createObject( "component", "coldbox.system.aop.Matcher" ).init(); - + - - - - + + + + + + + // cleanup aspect + if ( isSimpleValue( arguments.aspects ) ) { + arguments.aspects = listToArray( arguments.aspects ); + } + // register it + arrayAppend( instance.aspectBindings, arguments ); + + return this; + + - + + + + + diff --git a/system/ioc/config/DefaultBinder.cfc b/system/ioc/config/DefaultBinder.cfc index 071ae49a5..fbb0b2614 100644 --- a/system/ioc/config/DefaultBinder.cfc +++ b/system/ioc/config/DefaultBinder.cfc @@ -6,17 +6,17 @@ * WireBox injector is created **/ component extends="coldbox.system.ioc.config.Binder"{ - + /** * Configure WireBox, that's it! */ function configure(){ - + // The WireBox configuration structure DSL wireBox = { // Default LogBox Configuration file logBoxConfig = "coldbox.system.ioc.config.LogBox", - + // CacheBox Integration OFF by default cacheBox = { enabled = false @@ -24,38 +24,38 @@ component extends="coldbox.system.ioc.config.Binder"{ // cacheFactory = "" A reference to an already instantiated CacheBox CacheFactory // classNamespace = "" A class path namespace to use to create CacheBox: Default=coldbox.system.cache or wirebox.system.cache }, - + // Name of a CacheBox cache to store metadata in to speed up start time. // Since metadata is already stored in memory, this is only useful for a disk, etc cache that persists across restarts. metadataCache='', - + // Scope registration, automatically register a wirebox injector instance on any CF scope // By default it registeres itself on application scope scopeRegistration = { - enabled = true, - scope = "application", // server, cluster, session, application - key = "wireBox" + enabled= true, + scope = "application", // server, cluster, session, application + key = "wireBox" }, // DSL Namespace registrations customDSL = { // namespace = "mapping name" }, - + // Custom Storage Scopes customScopes = { // annotationName = "mapping name" }, - + // Package scan locations scanLocations = [], - + // Stop Recursions stopRecursions = [], - + // Parent Injector to assign to the configured injector, this must be an object reference parentInjector = "", - + // Register all event listeners here, they are created in the specified order listeners = [ // { class="", name="", properties={} } diff --git a/system/ioc/config/LogBox.cfc b/system/ioc/config/LogBox.cfc index d02edd9a3..97e527f9c 100644 --- a/system/ioc/config/LogBox.cfc +++ b/system/ioc/config/LogBox.cfc @@ -1,11 +1,11 @@ /** -* Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp -* www.ortussolutions.com -* --- -* The logging configuration object for WireBox Standalone version. -* You can make changes here to determine how WireBox logs information. For more -* information about logBox visit: https://logbox.ortusbooks.com -**/ + * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * www.ortussolutions.com + * --- + * The logging configuration object for WireBox Standalone version. + * You can make changes here to determine how WireBox logs information. For more + * information about logBox visit: https://logbox.ortusbooks.com + */ component{ /** @@ -16,17 +16,11 @@ component{ // Define Appenders appenders = { console = { - class="ConsoleAppender" + class = "ConsoleAppender" } - /**, - cflogs = { - class="coldbox.system.logging.appenders.CFAppender", - properties = { fileName="ColdBox-WireBox"} - } - **/ }, // Root Logger - root = { levelmax="INFO", appenders="*" } + root = { levelmax = "INFO", appenders = "*" } }; } diff --git a/system/ioc/config/Mapping.cfc b/system/ioc/config/Mapping.cfc index 5c484d098..306599d44 100644 --- a/system/ioc/config/Mapping.cfc +++ b/system/ioc/config/Mapping.cfc @@ -1,947 +1,993 @@ - - - - - - - - - - - // Configure Instance - instance = { - // Setup the mapping name - name = arguments.name, - // Setup the alias list for this mapping. - alias = [], - // Mapping Type - type = "", - // Mapping Value (If Any) - value = "", - // Mapped instantiation path or mapping - path = "", - // A factory method to execute on the mapping if this is a factory mapping - method = "", - // Mapped constructor - constructor = "init", - // Discovery and wiring flag - autoWire = "", - // Auto init or not - autoInit = true, - // Lazy load the mapping or not - eagerInit = "", - // The storage or visibility scope of the mapping - scope = "", - // A construction dsl - dsl = "", - // Caching parameters - cache = {provider="", key="", timeout="", lastAccessTimeout=""}, - // Explicit Constructor arguments - DIConstructorArgs = [], - // Explicit Properties - DIProperties = [], - // Explicit Setters - DISetters = [], - // Explicit method arguments - DIMethodArgs = [], - // Post Processors - onDIComplete = [], - // Flag used to distinguish between discovered and non-discovered mappings - discovered = false, - // original object's metadata - metadata = {}, - // discovered provider methods - providerMethods = [], - // AOP aspect - aspect = false, - // AutoAspectBinding - autoAspectBinding = true, - // Virtual Inhertiance - virtualInheritance = "", - // Extra Attributes - extraAttributes = {}, - // Mixins - mixins = [], - // Thread safety on wiring - threadSafe = "", - // A closure that can influence the creation of the instance - influenceClosure = "" - }; - - // DI definition structure - DIDefinition = { name="", value=JavaCast( "null", "" ), dsl=JavaCast( "null", "" ), scope="variables", javaCast=JavaCast( "null", "" ), ref=JavaCast( "null", "" ), required=false, argName="", type="any" }; - - return this; - - - - - - - - - - - - - - var x = 1; - - - // if excludes is passed as an array, convert to list - if( isArray( arguments.excludes ) ){ - arguments.excludes = arrayToList( arguments.excludes ); +/** + * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * www.ortussolutions.com + * --- + * I model a WireBox object mapping in all of its glory and splendour to create the + * object it represents + */ +component accessors="true"{ + + /** + * Mapping Properties + */ + + property name="name"; + property name="alias"; + property name="type"; + property name="value"; + property name="path"; + property name="method"; + property name="constructor"; + property name="autoWire"; + property name="autoInit"; + property name="eagerInit"; + property name="scope"; + property name="dsl"; + property name="cache"; + property name="DIConstructorArguments"; + property name="DIProperties"; + property name="DISetters"; + property name="DIMethodArguments"; + property name="onDIComplete"; + property name="discovered"; + property name="objectMetadata"; + property name="providerMethods"; + property name="aspect"; + property name="aspectAutoBinding"; + property name="virtualInheritance"; + property name="extraAttributes"; + property name="mixins"; + property name="threadSafe"; + property name="influenceClosure"; + + /** + * Constructor + * + * @name The mapping name + */ + function init( required name ){ + // Setup the mapping name + variables.name = arguments.name; + // Setup the alias list for this mapping. + variables.alias = []; + // Mapping Type + variables.type = ""; + // Mapping Value (If Any) + variables.value = ""; + // Mapped instantiation path or mapping + variables.path = ""; + // A factory method to execute on the mapping if this is a factory mapping + variables.method = ""; + // Mapped constructor + variables.constructor = "init"; + // Discovery and wiring flag + variables.autoWire = ""; + // Auto init or not + variables.autoInit = true; + // Lazy load the mapping or not + variables.eagerInit = ""; + // The storage or visibility scope of the mapping + variables.scope = ""; + // A construction dsl + variables.dsl = ""; + // Caching parameters + variables.cache = { + provider : "", + key : "", + timeout : "", + lastAccessTimeout : "" + }; + // Explicit Constructor arguments + variables.DIConstructorArguments = []; + // Explicit Properties + variables.DIProperties = []; + // Explicit Setters + variables.DISetters = []; + // Explicit method arguments + variables.DIMethodArguments = []; + // Post Processors + variables.onDIComplete = []; + // Flag used to distinguish between discovered and non-discovered mappings + variables.discovered = false; + // original object's metadata + variables.objectMetadata = {}; + // discovered provider methods + variables.providerMethods = []; + // AOP aspect + variables.aspect = false; + // aspectAutoBinding + variables.aspectAutoBinding = true; + // Virtual Inhertiance + variables.virtualInheritance = ""; + // Extra Attributes + variables.extraAttributes = {}; + // Mixins + variables.mixins = []; + // Thread safety on wiring + variables.threadSafe = ""; + // A closure that can influence the creation of the mapping + variables.influenceClosure = ""; + + return this; + } + + /** + * Get the mapping's memento structure + */ + struct function getMemento(){ + return variables.filter( function( k, v ){ + return ( !isCustomFunction( v ) ); + } ); + } + + /** + * Process a mapping memento + * + * @memento The data memento to process + * @excludes List of memento keys to not process + */ + Mapping function processMemento( required memento, excludes="" ){ + // if excludes is passed as an array, convert to list + if ( isArray( arguments.excludes ) ) { + arguments.excludes = arrayToList( arguments.excludes ); + } + + // append incoming memento data + for ( var key in arguments.memento ) { + // if current key is in excludes list, skip and continue to next loop + if ( listFindNoCase( arguments.excludes, key ) ) { + continue; } - // append incoming memento data - for( var key in arguments.memento ){ - - // if current key is in excludes list, skip and continue to next loop - if( listFindNoCase( arguments.excludes, key ) ){ - continue; + switch ( key ) { + // process cache properties + case "cache": { + setCacheProperties( argumentCollection = arguments.memento.cache ); + break; } - switch( key ){ - - // process cache properties - case "cache" : { - setCacheProperties( argumentCollection=arguments.memento.cache ); - break; - } - - // process constructor args - case "DIConstructorArgs" : { - for( x=1; x lte arrayLen( arguments.memento.DIConstructorArgs ); x++ ){ - addDIConstructorArgument( argumentCollection=arguments.memento.DIConstructorArgs[ x ] ); - } - break; - } - - // process properties - case "DIProperties" : { - for( x=1; x lte arrayLen( arguments.memento.DIProperties ); x++){ - addDIProperty( argumentCollection=arguments.memento.DIProperties[ x ] ); - } - break; + // process constructor args + case "DIConstructorArguments": { + for ( var x = 1; x lte arrayLen( arguments.memento.DIConstructorArguments ); x++ ) { + addDIConstructorArgument( argumentCollection = arguments.memento.DIConstructorArguments[ x ] ); } + break; + } - // process DISetters - case "DISetters" : { - for( x=1; x lte arrayLen( arguments.memento.DISetters ); x++){ - addDISetter( argumentCollection=arguments.memento.DISetters[ x ] ); - } - break; + // process properties + case "DIProperties": { + for ( var x = 1; x lte arrayLen( arguments.memento.DIProperties ); x++ ) { + addDIProperty( argumentCollection = arguments.memento.DIProperties[ x ] ); } + break; + } - // process DIMethodArgs - case "DIMethodArgs" : { - for( x=1; x lte arrayLen( arguments.memento.DIMethodArgs ); x++){ - addDIMethodArgument( argumentCollection=arguments.memento.DIMethodArgs[ x ] ); - } - break; + // process DISetters + case "DISetters": { + for ( var x = 1; x lte arrayLen( arguments.memento.DISetters ); x++ ) { + addDISetter( argumentCollection = arguments.memento.DISetters[ x ] ); } + break; + } - // process path - case "path" : { - // Only override if it doesn't exist or empty - if( !instance.keyExists( "path" ) OR !len( instance.path ) ){ - instance[ "path" ] = arguments.memento[ "path" ]; - } - break; + // process DIMethodArguments + case "DIMethodArguments": { + for ( var x = 1; x lte arrayLen( arguments.memento.DIMethodArguments ); x++ ) { + addDIMethodArgument( argumentCollection = arguments.memento.DIMethodArguments[ x ] ); } + break; + } - default:{ - instance[ key ] = arguments.memento[ key ]; - break; + // process path + case "path": { + // Only override if it doesn't exist or empty + if ( !len( variables.path ) ) { + variables.path = arguments.memento[ "path" ]; } - }// end switch + break; + } + default: { + variables[ key ] = arguments.memento[ key ]; + break; + } } - - return this; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - structAppend( instance.cache, arguments, true); + // end switch + } + + return this; + } + + /** + * Checks if the mapping needs virtual inheritace or not + */ + boolean function isVirtualInheritance(){ + return len( variables.virtualInheritance ) GT 0; + } + + /** + * Flag describing if you are using autowire or not as Boolean + */ + boolean function isAutoWire(){ + return variables.autowire; + } + + /** + * Flag describing if this mapping is an AOP aspect or not + */ + boolean function isAspect(){ + return variables.aspect; + } + + /** + * Is this mapping an auto aspect binding + */ + boolean function isAspectAutoBinding(){ + return variables.aspectAutoBinding; + } + + /** + * Using auto init or not + */ + boolean function isAutoInit(){ + return variables.autoInit; + } + + /** + * Does this mapping have a DSL construction element or not as Boolean + */ + boolean function isDSL(){ + return ( len( variables.dsl ) GT 0 ); + } + + /** + * Set the cache properties for this mapping + * + * @key Cache key to use + * @timeout Object Timeout + * @lastAccessTimeout Object Last Access Timeout + * @provider The Cache Provider to use + */ + function setCacheProperties( + required key, + timeout ="", + lastAccessTimeout="", + provider ="default" + ){ + structAppend( variables.cache, arguments, true ); + return this; + } + + /** + * Get the cache properties struct + */ + struct function getCacheProperties(){ + return variables.cache; + } + + /** + * Add a new constructor argument to this mapping + * + * @name The name of the constructor argument (Not used for: JAVA,WEBSERVICE) + * @ref The reference mapping id this constructor argument maps to + * @dsl The construction dsl this argument references. If used, the name value must be used. + * @value The explicit value of the constructor argument, if passed. + * @javaCast The type of javaCast() to use on the value of the argument. Only used if using dsl or ref arguments + * @required If the argument is required or not, by default we assume required DI arguments + * @type The type of the argument + */ + Mapping function addDIConstructorArgument( + name, + ref, + dsl="", + value, + javaCast, + required required=true, + type ="any" + ){ + // check if already registered, if it is, just return + for ( var x = 1; x lte arrayLen( variables.DIConstructorArguments ); x++ ) { + if ( + structKeyExists( arguments, "name" ) AND + structKeyExists( variables.DIConstructorArguments[ x ], "name" ) AND + variables.DIConstructorArguments[ x ].name == arguments.name + ) { + return this; + } + } + + // Register new constructor argument. + var defintion = getNewDIDefinition(); + structAppend( defintion, arguments, true ); + arrayAppend( variables.DIConstructorArguments, defintion ); + + return this; + } + + /** + * Add a new method argument to this mapping + * + * @name The name of the method argument (Not used for: JAVA,WEBSERVICE) + * @ref The reference mapping id this method argument maps to + * @dsl The construction dsl this argument references. If used, the name value must be used. + * @value The explicit value of the method argument, if passed. + * @javaCast The type of javaCast() to use on the value of the argument. Only used if using dsl or ref arguments + * @required If the argument is required or not, by default we assume required DI arguments + * @type The type of the argument + */ + Mapping function addDIMethodArgument( + name, + ref, + dsl, + value, + javaCast, + required required=true, + type ="any" + ){ + // check if already registered, if it is, just return + for ( var x = 1; x lte arrayLen( variables.DIMethodArguments ); x++ ) { + if ( + structKeyExists( variables.DIMethodArguments[ x ], "name" ) AND + variables.DIMethodArguments[ x ].name == arguments.name + ) { + return this; + } + } + + // Register new constructor argument. + var defintion = getNewDIDefinition(); + structAppend( defintion, arguments, true ); + arrayAppend( variables.DIMethodArguments, defintion ); + + return this; + } + + /** + * Add a new property di definition + * + * @name The name of the property to inject + * @ref The reference mapping id this property maps to + * @dsl The construction dsl this property references. If used, the name value must be used. + * @value The explicit value of the property, if passed. + * @javaCast The type of javaCast() to use on the value of the value. Only used if using dsl or ref arguments + * @scope The scope in the CFC to inject the property to. By default it will inject it to the variables scope + * @required If the property is required or not, by default we assume required DI + * @type The type of the property + */ + Mapping function addDIProperty( + required name, + ref, + dsl, + value, + javaCast, + scope ="variables", + required required=true, + type ="any" + ){ + // check if already registered, if it is, just return + for ( var x = 1; x lte arrayLen( variables.DIProperties ); x++ ) { + if ( variables.DIProperties[ x ].name eq arguments.name ) { + return this; + } + } + + var definition = getNewDIDefinition(); + structAppend( definition, arguments, true ); + arrayAppend( variables.DIProperties, definition ); + + return this; + } + + /** + * Add a new DI Setter Definition + * + * @name The name of the setter to inject + * @ref The reference mapping id this setter maps to + * @dsl The construction dsl this setter references. If used, the name value must be used. + * @value The explicit value of the setter, if passed. + * @javaCast The type of javaCast() to use on the value of the value. Only used if using dsl or ref arguments + * @argName The name of the argument to use, if not passed, we default it to the setter name + */ + Mapping function addDISetter( + required name, + ref, + dsl, + value, + javaCast, + argName + ){ + // check if already registered, if it is, just return + for ( var x = 1; x lte arrayLen( variables.DISetters ); x++ ) { + if ( variables.DISetters[ x ].name eq arguments.name ) { + return this; + } + } + + // Get new definition + var definition = getNewDIDefinition(); + // Remove scope for setter injection + definition.scope = ""; + // Verify argument name, if not default it to setter name + if ( NOT structKeyExists( arguments, "argName" ) OR len( arguments.argName ) EQ 0 ) { + arguments.argName = arguments.name; + } + // save incoming params + structAppend( definition, arguments, true ); + // save new DI setter injection + arrayAppend( variables.DISetters, definition ); + + return this; + } + + /** + * Checks if this mapping has already been processed or not + */ + boolean function isDiscovered(){ + return variables.discovered; + } + + /** + * Is this mapping eager initialized or not as Boolean + */ + boolean function isEagerInit(){ + return variables.eagerInit; + } + + /** + * Add a new provider method to this mapping + * + * @method The provided method to override as a provider + * @mapping The mapping to provide via the selected method + */ + Mapping function addProviderMethod( required method, required mapping ){ + arrayAppend( variables.providerMethods, arguments ); + return this; + } + + /** + * --------------------------------------------------- + * Processing Methods + * --------------------------------------------------- + */ + + /** + * Process a mapping for metadata discovery and more + * + * @binder The binder requesting the processing + * @injector The calling injector processing the mappping + * @metadata The metadata of an a-la-carte processing, use instead of retrieveing again + * + * @return Mapping + */ + Mapping function process( + required binder, + required injector, + metadata + ){ + var md = variables.objectMetadata; + var mappings = arguments.binder.getMappings(); + var eventManager = arguments.injector.getEventManager(); + var cacheProperties = {}; + + // Short circuit, if mapping already discovered, then just exit out. + if( variables.discovered ){ return this; - - - - - - - - - - - - - - - - - - - - - - var def = getDIDefinition(); - var x = 1; - // check if already registered, if it is, just return - for(x=1; x lte arrayLen(instance.DIConstructorArgs); x++){ - if( structKeyExists( arguments, "name" ) AND structKeyExists( instance.DIConstructorArgs[ x ], "name" ) AND - instance.DIConstructorArgs[ x ].name eq arguments.name ){ return this;} - } - // Register new constructor argument. - structAppend(def, arguments, true); - arrayAppend( instance.DIConstructorArgs, def ); + } + + // Generate a lock token + if ( isSimpleValue( variables.path ) ){ + var lockToken = variables.path; + } else { + var lockToken = createUUID(); + } + + // Lock for discovery based on path location, only done once per mapping + lock + name ="Mapping.#arguments.injector.getInjectorID()#.MetadataProcessing.#lockToken#" + type ="exclusive" + timeout ="20" + throwOnTimeout="true" + { + // announce inspection + var iData = { + mapping : this, + binder : arguments.binder, + injector : arguments.binder.getInjector() + }; + eventManager.announce( "beforeInstanceInspection", iData ); - return this; - - - - - - - - - - - - - - var def = getDIDefinition(); - var x = 1; - // check if already registered, if it is, just return - for(x=1; x lte arrayLen(instance.DIMethodArgs); x++){ - if( structKeyExists(instance.DIMethodArgs[ x ],"name") AND - instance.DIMethodArgs[ x ].name eq arguments.name ){ return this;} - } - structAppend(def, arguments, true); - arrayAppend( instance.DIMethodArgs, def ); - return this; - - - - - - - - - - - - - - - - - - - - - - - - - var def = getDIDefinition(); - var x = 1; - // check if already registered, if it is, just return - for( x=1; x lte arrayLen( instance.DIProperties ); x++ ){ - if( instance.DIProperties[ x ].name eq arguments.name ){ return this;} - } - structAppend( def, arguments, true ); - arrayAppend( instance.DIProperties, def ); - return this; - - - - - - - - - - - - - - - - - - var def = getDIDefinition(); - var x = 1; - - // check if already registered, if it is, just return - for(x=1; x lte arrayLen(instance.DISetters); x++){ - if( instance.DISetters[ x ].name eq arguments.name ){ return this;} - } - // Remove scope for setter injection - def.scope = ""; - // Verify argument name, if not default it to setter name - if( NOT structKeyExists(arguments,"argName") OR len(arguments.argName) EQ 0 ){ - arguments.argName = arguments.name; - } - // save incoming params - structAppend(def, arguments, true); - // save new DI setter injection - arrayAppend( instance.DISetters, def ); - return this; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if( NOT instance.discovered ){ - // announce inspection - iData = {mapping=this,binder=arguments.binder,injector=arguments.binder.getInjector()}; - eventManager.announce("beforeInstanceInspection",iData); - - // Processing only done for CFC's,rest just mark and return - if( instance.type neq arguments.binder.TYPES.CFC ){ - if( NOT len(instance.scope) ){ instance.scope = "noscope"; } - if( NOT len(instance.autowire) ){ instance.autowire = true; } - if( NOT len(instance.eagerInit) ){ instance.eagerInit = false; } - if( NOT len(instance.threadSafe) ){ instance.threadSafe = false; } - // finished processing mark as discovered - instance.discovered = true; - // announce it - eventManager.announce("afterInstanceInspection",iData); - return; - } - - // Get the instance's metadata first, so we can start processing. - if( structKeyExists(arguments,"metadata") ){ - md = arguments.metadata; - } - else{ - var produceMetadataUDF = function() { return injector.getUtil().getInheritedMetaData(instance.path, binder.getStopRecursions()); }; - - // Are we caching metadata? - if( len( binder.getMetadataCache() ) ) { - // Get from cache or produce on demand - md = injector.getCacheBox().getCache( binder.getMetadataCache() ).getOrSet( - instance.path, - produceMetadataUDF - ); - } else { - md = produceMetadataUDF(); - } + // Processing only done for CFC's,rest just mark and return + if ( variables.type neq arguments.binder.TYPES.CFC ) { + if ( NOT len( variables.scope ) ) { + variables.scope = "noscope"; } + if ( NOT len( variables.autowire ) ) { + variables.autowire = true; + } + if ( NOT len( variables.eagerInit ) ) { + variables.eagerInit = false; + } + if ( NOT len( variables.threadSafe ) ) { + variables.threadSafe = false; + } + // finished processing mark as discovered + variables.discovered = true; + // announce it + eventManager.announce( "afterInstanceInspection", iData ); + return this; + } - // Store Metadata - instance.metadata = md; - - // Process persistence if not set already by configuration as it takes precedence - if( NOT len(instance.scope) ){ - // Singleton Processing - if( structKeyExists(md,"singleton") ){ instance.scope = arguments.binder.SCOPES.SINGLETON; } - // Registered Scope Processing - if( structKeyExists(md,"scope") ){ instance.scope = md.scope; } - // CacheBox scope processing if cachebox annotation found, or cache annotation found - if( structKeyExists(md,"cacheBox") OR ( structKeyExists(md,"cache") AND isBoolean(md.cache) AND md.cache ) ){ - instance.scope = arguments.binder.SCOPES.CACHEBOX; - } - - // check if scope found? If so, then set it to no scope. - if( NOT len(instance.scope) ){ instance.scope = "noscope"; } + // Get the metadata first, so we can start processing. + if ( structKeyExists( arguments, "metadata" ) ) { + md = arguments.metadata; + } else { + // Are we caching metadata? or just using it + if ( len( arguments.binder.getMetadataCache() ) ) { + // Get from cache or produce on demand + md = arguments.injector + .getCacheBox() + .getCache( arguments.binder.getMetadataCache() ) + .getOrSet( variables.path, produceMetadataUDF( arguments.injector, arguments.binder ) ); + } else { + md = produceMetadataUDF( arguments.injector, arguments.binder ); + } + } - } // end of persistence checks + // Store Metadata + variables.objectMetadata = md; - // Cachebox Persistence Processing - if( instance.scope EQ arguments.binder.SCOPES.CACHEBOX ){ - // Check if we already have a key, maybe added via configuration - if( NOT len( instance.cache.key ) ){ - instance.cache.key = "wirebox-#instance.name#"; - } - // Check the default provider now to see if set by configuration - if( NOT len( instance.cache.provider) ){ - // default it first - instance.cache.provider = "default"; - // Now check the annotations for the provider - if( structKeyExists(md,"cacheBox") AND len(md.cacheBox) ){ - instance.cache.provider = md.cacheBox; - } - } - // Check if timeouts set by configuration or discovery - if( NOT len( instance.cache.timeout ) ){ - // Discovery by annocations - if( structKeyExists(md,"cachetimeout") AND isNumeric(md.cacheTimeout) ){ - instance.cache.timeout = md.cacheTimeout; - } - } - // Check if lastAccessTimeout set by configuration or discovery - if( NOT len( instance.cache.lastAccessTimeout ) ){ - // Discovery by annocations - if( structKeyExists(md,"cacheLastAccessTimeout") AND isNumeric(md.cacheLastAccessTimeout) ){ - instance.cache.lastAccessTimeout = md.cacheLastAccessTimeout; - } - } + // Process persistence if not set already by configuration as it takes precedence + if ( NOT len( variables.scope ) ) { + // Singleton Processing + if ( structKeyExists( md, "singleton" ) ) { + variables.scope = arguments.binder.SCOPES.SINGLETON; } - - // Alias annotations if found, then append them as aliases. - if( structKeyExists(md, "alias") ){ - thisAliases = listToArray(md.alias); - instance.alias.addAll( thisAliases ); - // register alias references on binder - for(x=1; x lte arrayLen(thisAliases); x++){ - mappings[ thisAliases[ x ] ] = this; - } + // Registered Scope Processing + if ( structKeyExists( md, "scope" ) ) { + variables.scope = md.scope; + } + // CacheBox scope processing if cachebox annotation found, or cache annotation found + if ( + structKeyExists( md, "cacheBox" ) OR ( + structKeyExists( md, "cache" ) AND isBoolean( md.cache ) AND md.cache + ) + ) { + variables.scope = arguments.binder.SCOPES.CACHEBOX; } - // eagerInit annotation - if( NOT len(instance.eagerInit) ){ - if( structKeyExists(md,"eagerInit") ){ - instance.eagerInit = true; - } - else{ - // defaults to lazy loading - instance.eagerInit = false; - } + // check if scope found? If so, then set it to no scope. + if ( NOT len( variables.scope ) ) { + variables.scope = "noscope"; } + } + // end of persistence checks - // threadSafe wiring annotation - if( NOT len(instance.threadSafe) ){ - if( structKeyExists(md,"threadSafe") AND NOT len(md.threadSafe)){ - instance.threadSafe = true; + // Cachebox Persistence Processing + if ( variables.scope EQ arguments.binder.SCOPES.CACHEBOX ) { + // Check if we already have a key, maybe added via configuration + if ( NOT len( variables.cache.key ) ) { + variables.cache.key = "wirebox-#variables.name#"; + } + // Check the default provider now to see if set by configuration + if ( NOT len( variables.cache.provider ) ) { + // default it first + variables.cache.provider = "default"; + // Now check the annotations for the provider + if ( structKeyExists( md, "cacheBox" ) AND len( md.cacheBox ) ) { + variables.cache.provider = md.cacheBox; } - else if( structKeyExists(md,"threadSafe") AND len(md.threadSafe) AND isBoolean(md.threadSafe) ){ - instance.threadSafe = md.threadSafe; + } + // Check if timeouts set by configuration or discovery + if ( NOT len( variables.cache.timeout ) ) { + // Discovery by annocations + if ( structKeyExists( md, "cachetimeout" ) AND isNumeric( md.cacheTimeout ) ) { + variables.cache.timeout = md.cacheTimeout; } - else{ - // defaults to non thread safe wiring - instance.threadSafe = false; + } + // Check if lastAccessTimeout set by configuration or discovery + if ( NOT len( variables.cache.lastAccessTimeout ) ) { + // Discovery by annocations + if ( + structKeyExists( md, "cacheLastAccessTimeout" ) AND isNumeric( + md.cacheLastAccessTimeout + ) + ) { + variables.cache.lastAccessTimeout = md.cacheLastAccessTimeout; } } + } - // mixins annotation only if not overriden - if( NOT arrayLen(instance.mixins) ){ - if( structKeyExists(md,"mixins") ){ - instance.mixins = listToArray( md.mixins ); - } + // Alias annotations if found, then append them as aliases. + if ( structKeyExists( md, "alias" ) ) { + var thisAliases = listToArray( md.alias ); + variables.alias.addAll( thisAliases ); + // register alias references on binder + for ( var x = 1; x lte arrayLen( thisAliases ); x++ ) { + mappings[ thisAliases[ x ] ] = this; } + } - // check if the autowire NOT set, so we can discover it. - if( NOT len(instance.autowire) ){ - // Check if autowire annotation found or autowire already set - if( structKeyExists(md,"autowire") and isBoolean(md.autowire) ){ - instance.autoWire = md.autowire; - } - else{ - // default to true - instance.autoWire = true; - } + // eagerInit annotation only if not overriden + if ( NOT len( variables.eagerInit ) ) { + if ( structKeyExists( md, "eagerInit" ) ) { + variables.eagerInit = true; + } else { + // defaults to lazy loading + variables.eagerInit = false; } + } - // look for parent metadata on the instance referring to an abstract parent (by alias) to copy - // dependencies and definitions from - if( structKeyExists(md, "parent") and len(trim(md.parent))){ - arguments.binder.parent(alias:md.parent); + // threadSafe wiring annotation + if ( NOT len( variables.threadSafe ) ) { + if ( structKeyExists( md, "threadSafe" ) AND NOT len( md.threadSafe ) ) { + variables.threadSafe = true; + } else if ( + structKeyExists( md, "threadSafe" ) AND len( md.threadSafe ) AND isBoolean( md.threadSafe ) + ) { + variables.threadSafe = md.threadSafe; + } else { + // defaults to non thread safe wiring + variables.threadSafe = false; } + } - // Only process if autowiring - if( instance.autoWire ){ - // Process Methods, Constructors and Properties only if non autowire annotation check found on component. - processDIMetadata( arguments.binder, md ); + // mixins annotation only if not overriden + if ( NOT arrayLen( variables.mixins ) ) { + if ( structKeyExists( md, "mixins" ) ) { + variables.mixins = listToArray( md.mixins ); } + } - // AOP AutoBinding only if both @classMatcher and @methodMatcher exist - if( isAspectAutoBinding() AND structKeyExists(md,"classMatcher") AND structKeyExists(md,"methodMatcher") ){ - processAOPBinding( arguments.binder, md); + // autowire only if not overriden + if ( NOT len( variables.autowire ) ) { + // Check if autowire annotation found or autowire already set + if ( structKeyExists( md, "autowire" ) and isBoolean( md.autowire ) ) { + variables.autoWire = md.autowire; + } else { + // default to true + variables.autoWire = true; } + } - // finished processing mark as discovered - instance.discovered = true; + // look for parent metadata referring to an abstract parent (by alias) to copy + // dependencies and definitions from + if ( structKeyExists( md, "parent" ) and len( trim( md.parent ) ) ) { + arguments.binder.parent( alias : md.parent ); + } - // announce it - eventManager.announce("afterInstanceInspection",iData); - } - - - - - - - - - - - - var classes = listFirst(arguments.metadata.classMatcher,":"); - var methods = listFirst(arguments.metadata.methodMatcher,":"); - var classMatcher = ""; - var methodMatcher = ""; - - // determine class matching - switch(classes){ - case "any" : { classMatcher = arguments.binder.match().any(); break; } - case "annotatedWith" : { - // annotation value? - if( listLen(arguments.metadata.classMatcher,":") eq 3 ){ - classMatcher = arguments.binder.match().annotatedWith( getToken(arguments.metadata.classMatcher,2,":"), getToken(arguments.metadata.classMatcher,3,":") ); - } - // No annotation value - else{ - classMatcher = arguments.binder.match().annotatedWith( getToken(arguments.metadata.classMatcher,2,":") ); - } - break; + // Only process if autowiring + if ( variables.autoWire ) { + // Process Methods, Constructors and Properties only if non autowire annotation check found on component. + processDIMetadata( arguments.binder, md ); + } + + // AOP AutoBinding only if both @classMatcher and @methodMatcher exist + if ( + isAspectAutoBinding() AND structKeyExists( md, "classMatcher" ) AND structKeyExists( + md, + "methodMatcher" + ) + ) { + processAOPBinding( arguments.binder, md ); + } + + // finished processing mark as discovered + variables.discovered = true; + + // announce it + eventManager.announce( "afterInstanceInspection", iData ); + } // End lock + + return this; + } + + /** + * --------------------------------------------------- + * Private Methods + * --------------------------------------------------- + */ + + /** + * Produce metadata helper + * + * @injector The injector to use + * @binder The binder to use + * + * @return Metadata struct + */ + private function produceMetadataUDF( required injector, required binder ){ + return arguments + .injector + .getUtil() + .getInheritedMetaData( variables.path, arguments.binder.getStopRecursions() ); + }; + + /** + * Process the AOP self binding aspects + * + * @binder The binder requesting the processing + * @metadata The metadata to process + * + * @return Mapping + */ + private Mapping function processAOPBinding( required binder, required metadata ){ + var classes = listFirst( arguments.metadata.classMatcher, ":" ); + var methods = listFirst( arguments.metadata.methodMatcher, ":" ); + var classMatcher = ""; + var methodMatcher = ""; + + // determine class matching + switch ( classes ) { + case "any": { + classMatcher = arguments.binder.match().any(); + break; + } + case "annotatedWith": { + // annotation value? + if ( listLen( arguments.metadata.classMatcher, ":" ) eq 3 ) { + classMatcher = arguments.binder + .match() + .annotatedWith( + getToken( arguments.metadata.classMatcher, 2, ":" ), + getToken( arguments.metadata.classMatcher, 3, ":" ) + ); } - case "mappings" : { classMatcher = arguments.binder.match().mappings( getToken(arguments.metadata.classMatcher,2,":") ); break; } - case "instanceOf" : { classMatcher = arguments.binder.match().instanceOf( getToken(arguments.metadata.classMatcher,2,":") ); break; } - case "regex" : { classMatcher = arguments.binder.match().regex( getToken(arguments.metadata.classMatcher,2,":") ); break; } - default: { - // throw, no matching matchers - throw(message="Invalid Class Matcher: #classes#", - type="Mapping.InvalidAOPClassMatcher", - detail="Valid matchers are 'any,annotatedWith:annotation,annotatedWith:annotation:value,mappings:XXX,instanceOf:XXX,regex:XXX'"); + // No annotation value + else { + classMatcher = arguments.binder + .match() + .annotatedWith( getToken( arguments.metadata.classMatcher, 2, ":" ) ); } + break; + } + case "mappings": { + classMatcher = arguments.binder + .match() + .mappings( getToken( arguments.metadata.classMatcher, 2, ":" ) ); + break; + } + case "instanceOf": { + classMatcher = arguments.binder + .match() + .instanceOf( getToken( arguments.metadata.classMatcher, 2, ":" ) ); + break; + } + case "regex": { + classMatcher = arguments.binder + .match() + .regex( getToken( arguments.metadata.classMatcher, 2, ":" ) ); + break; } + default: { + // throw, no matching matchers + throw( + message = "Invalid Class Matcher: #classes#", + type = "Mapping.InvalidAOPClassMatcher", + detail = "Valid matchers are 'any,annotatedWith:annotation,annotatedWith:annotation:value,mappings:XXX,instanceOf:XXX,regex:XXX'" + ); + } + } - // determine method matching - switch(methods){ - case "any" : { methodMatcher = arguments.binder.match().any(); break; } - case "annotatedWith" : { - // annotation value? - if( listLen(arguments.metadata.classMatcher,":") eq 3 ){ - methodMatcher = arguments.binder.match().annotatedWith( getToken(arguments.metadata.methodMatcher,2,":"), getToken(arguments.metadata.methodMatcher,3,":") ); - } - // No annotation value - else{ - methodMatcher = arguments.binder.match().annotatedWith( getToken(arguments.metadata.methodMatcher,2,":") ); - } - break; + // determine method matching + switch ( methods ) { + case "any": { + methodMatcher = arguments.binder.match().any(); + break; + } + case "annotatedWith": { + // annotation value? + if ( listLen( arguments.metadata.classMatcher, ":" ) eq 3 ) { + methodMatcher = arguments.binder + .match() + .annotatedWith( + getToken( arguments.metadata.methodMatcher, 2, ":" ), + getToken( arguments.metadata.methodMatcher, 3, ":" ) + ); } - case "methods" : { methodMatcher = arguments.binder.match().methods( getToken(arguments.metadata.methodMatcher,2,":") ); break; } - case "instanceOf" : { methodMatcher = arguments.binder.match().instanceOf( getToken(arguments.metadata.methodMatcher,2,":") ); break; } - case "regex" : { methodMatcher = arguments.binder.match().regex( getToken(arguments.metadata.methodMatcher,2,":") ); break; } - default: { - // throw, no matching matchers - throw(message="Invalid Method Matcher: #classes#", - type="Mapping.InvalidAOPMethodMatcher", - detail="Valid matchers are 'any,annotatedWith:annotation,annotatedWith:annotation:value,methods:XXX,instanceOf:XXX,regex:XXX'"); - } - } - - // Bind the Aspect to this Mapping - arguments.binder.bindAspect(classMatcher,methodMatcher,getName()); - - - - - - - - - - var x = 1; - var y = 1; - var md = arguments.metadata; - var fncLen = 0; - var params = ""; - - // Look For properties for annotation injections - if( structKeyExists(md,"properties") and ArrayLen(md.properties) GT 0){ - // Loop over each property and identify injectable properties - for(x=1; x lte ArrayLen(md.properties); x=x+1 ){ - // Check if property not discovered or if inject annotation is found - if( structKeyExists(md.properties[ x ],"inject") ){ - // prepare default params, we do this so we do not alter the md as it is cached by cf + // No annotation value + else { + methodMatcher = arguments.binder + .match() + .annotatedWith( getToken( arguments.metadata.methodMatcher, 2, ":" ) ); + } + break; + } + case "methods": { + methodMatcher = arguments.binder + .match() + .methods( getToken( arguments.metadata.methodMatcher, 2, ":" ) ); + break; + } + case "instanceOf": { + methodMatcher = arguments.binder + .match() + .instanceOf( getToken( arguments.metadata.methodMatcher, 2, ":" ) ); + break; + } + case "regex": { + methodMatcher = arguments.binder + .match() + .regex( getToken( arguments.metadata.methodMatcher, 2, ":" ) ); + break; + } + default: { + // throw, no matching matchers + throw( + message = "Invalid Method Matcher: #classes#", + type = "Mapping.InvalidAOPMethodMatcher", + detail = "Valid matchers are 'any,annotatedWith:annotation,annotatedWith:annotation:value,methods:XXX,instanceOf:XXX,regex:XXX'" + ); + } + } + + // Bind the Aspect to this Mapping + arguments.binder.bindAspect( classMatcher, methodMatcher, getName() ); + + return this; + } + + /** + * Process methods/properties for dependency injection + * + * @binder The binder requesting the processing + * @metadata The metadata to process + * @dependencies The dependencies structure + * + * @return Mapping + */ + private Mapping function processDIMetadata( required binder, required metadata, dependencies={} ){ + var x = 1; + var y = 1; + var md = arguments.metadata; + var fncLen = 0; + var params = ""; + + // Look For properties for annotation injections + if ( structKeyExists( md, "properties" ) and arrayLen( md.properties ) GT 0 ) { + // Loop over each property and identify injectable properties + for ( x = 1; x lte arrayLen( md.properties ); x = x + 1 ) { + // Check if property not discovered or if inject annotation is found + if ( structKeyExists( md.properties[ x ], "inject" ) ) { + // prepare default params, we do this so we do not alter the md as it is cached by cf + params = { + scope : "variables", + inject : "model", + name : md.properties[ x ].name, + required : true, + type : "any" + }; + // default property type + if ( structKeyExists( md.properties[ x ], "type" ) ) { + params.type = md.properties[ x ].type; + } + // default injection scope, if not found in object + if ( structKeyExists( md.properties[ x ], "scope" ) ) { + params.scope = md.properties[ x ].scope; + } + // Get injection if it exists + if ( len( md.properties[ x ].inject ) ) { + params.inject = md.properties[ x ].inject; + } + // Get required + if ( + structKeyExists( md.properties[ x ], "required" ) and isBoolean( + md.properties[ x ].required + ) + ) { + params.required = md.properties[ x ].required; + } + // Add to property to mappings + addDIProperty( + name = params.name, + dsl = params.inject, + scope = params.scope, + required = params.required, + type = params.type + ); + } + } + } + // end DI properties + + // Method DI discovery + if ( structKeyExists( md, "functions" ) ) { + fncLen = arrayLen( md.functions ); + for ( x = 1; x lte fncLen; x++ ) { + // Verify Processing or do we continue to next iteration for processing + // This is to avoid overriding by parent trees in inheritance chains + if ( structKeyExists( arguments.dependencies, md.functions[ x ].name ) ) { + continue; + } + + // Constructor Processing if found + if ( md.functions[ x ].name eq variables.constructor ) { + // Loop Over Arguments to process them for dependencies + for ( y = 1; y lte arrayLen( md.functions[ x ].parameters ); y++ ) { + // prepare params as we do not alter md as cf caches it params = { - scope="variables", inject="model", name=md.properties[ x ].name, required=true, type="any" + required : false, + inject : "model", + name : md.functions[ x ].parameters[ y ].name, + type : "any" }; - // default property type - if( structKeyExists( md.properties[ x ], "type" ) ){ - params.type = md.properties[ x ].type; + // check type annotation + if ( structKeyExists( md.functions[ x ].parameters[ y ], "type" ) ) { + params.type = md.functions[ x ].parameters[ y ].type; } - // default injection scope, if not found in object - if( structKeyExists(md.properties[ x ],"scope") ){ - params.scope = md.properties[ x ].scope; + // Check required annotation + if ( structKeyExists( md.functions[ x ].parameters[ y ], "required" ) ) { + params.required = md.functions[ x ].parameters[ y ].required; } - // Get injection if it exists - if( len(md.properties[ x ].inject) ){ - params.inject = md.properties[ x ].inject; - } - // Get required - if( structKeyExists( md.properties[ x ], "required" ) and isBoolean( md.properties[ x ].required ) ){ - params.required = md.properties[ x ].required; - } - // Add to property to mappings - addDIProperty( name=params.name, dsl=params.inject, scope=params.scope, required=params.required, type=params.type ); - } - - } - }//end DI properties - - // Method DI discovery - if( structKeyExists(md, "functions") ){ - fncLen = arrayLen(md.functions); - for(x=1; x lte fncLen; x++ ){ - - // Verify Processing or do we continue to next iteration for processing - // This is to avoid overriding by parent trees in inheritance chains - if( structKeyExists(arguments.dependencies, md.functions[ x ].name) ){ - continue; - } - - // Constructor Processing if found - if( md.functions[ x ].name eq instance.constructor ){ - // Loop Over Arguments to process them for dependencies - for(y=1;y lte arrayLen(md.functions[ x ].parameters); y++){ - - // prepare params as we do not alter md as cf caches it - params = { - required = false, inject="model", name=md.functions[ x ].parameters[y].name, type="any" - }; - // check type annotation - if( structKeyExists( md.functions[ x ].parameters[ y ], "type" ) ){ - params.type = md.functions[ x ].parameters[ y ].type; - } - // Check required annotation - if( structKeyExists(md.functions[ x ].parameters[y], "required") ){ - params.required = md.functions[ x ].parameters[y].required; - } - // Check injection annotation, if not found then no injection - if( structKeyExists(md.functions[ x ].parameters[y],"inject") ){ - - // Check if inject has value, else default it to 'model' or 'id' namespace - if( len(md.functions[ x ].parameters[y].inject) ){ - params.inject = md.functions[ x ].parameters[y].inject; - } - - // ADD Constructor argument - addDIConstructorArgument(name=params.name, - dsl=params.inject, - required=params.required, - type=params.type); + // Check injection annotation, if not found then no injection + if ( structKeyExists( md.functions[ x ].parameters[ y ], "inject" ) ) { + // Check if inject has value, else default it to 'model' or 'id' namespace + if ( len( md.functions[ x ].parameters[ y ].inject ) ) { + params.inject = md.functions[ x ].parameters[ y ].inject; } + // ADD Constructor argument + addDIConstructorArgument( + name = params.name, + dsl = params.inject, + required = params.required, + type = params.type + ); } - // add constructor to found list, so it is processed only once in recursions - arguments.dependencies[md.functions[ x ].name] = "constructor"; - } - - // Setter discovery, MUST be inject annotation marked to be processed. - if( left(md.functions[ x ].name,3) eq "set" AND structKeyExists(md.functions[ x ],"inject")){ - - // setup setter params in order to avoid touching the md struct as cf caches it - params = {inject="model",name=right(md.functions[ x ].name, Len(md.functions[ x ].name)-3)}; - - // Check DSL marker if it has a value else use default of Model - if( len(md.functions[ x ].inject) ){ - params.inject = md.functions[ x ].inject; - } - // Add to setter to mappings and recursion lookup - addDISetter(name=params.name,dsl=params.inject); - arguments.dependencies[md.functions[ x ].name] = "setter"; - } - - // Provider Methods Discovery - if( structKeyExists( md.functions[ x ], "provider") AND len(md.functions[ x ].provider)){ - addProviderMethod(md.functions[ x ].name, md.functions[ x ].provider); - arguments.dependencies[md.functions[ x ].name] = "provider"; } + // add constructor to found list, so it is processed only once in recursions + arguments.dependencies[ md.functions[ x ].name ] = "constructor"; + } - // onDIComplete Method Discovery - if( structKeyExists( md.functions[ x ], "onDIComplete") ){ - arrayAppend(instance.onDIComplete, md.functions[ x ].name ); - arguments.dependencies[md.functions[ x ].name] = "onDIComplete"; - } - - }//end loop of functions - }//end if functions found - - - + // Setter discovery, MUST be inject annotation marked to be processed. + if ( left( md.functions[ x ].name, 3 ) eq "set" AND structKeyExists( md.functions[ x ], "inject" ) ) { + // setup setter params in order to avoid touching the md struct as cf caches it + params = { + inject : "model", + name : right( md.functions[ x ].name, len( md.functions[ x ].name ) - 3 ) + }; + + // Check DSL marker if it has a value else use default of Model + if ( len( md.functions[ x ].inject ) ) { + params.inject = md.functions[ x ].inject; + } + // Add to setter to mappings and recursion lookup + addDISetter( name = params.name, dsl = params.inject ); + arguments.dependencies[ md.functions[ x ].name ] = "setter"; + } - - - - + // Provider Methods Discovery + if ( structKeyExists( md.functions[ x ], "provider" ) AND len( md.functions[ x ].provider ) ) { + addProviderMethod( md.functions[ x ].name, md.functions[ x ].provider ); + arguments.dependencies[ md.functions[ x ].name ] = "provider"; + } - \ No newline at end of file + // onDIComplete Method Discovery + if ( structKeyExists( md.functions[ x ], "onDIComplete" ) ) { + arrayAppend( variables.onDIComplete, md.functions[ x ].name ); + arguments.dependencies[ md.functions[ x ].name ] = "onDIComplete"; + } + } + // end loop of functions + } + // end if functions found + + return this; + } + + /** + * Get a new DI definition structure + */ + private struct function getNewDIDefinition(){ + return { + "name" : "", + "value" : javacast( "null", "" ), + "dsl" : javacast( "null", "" ), + "scope" : "variables", + "javaCast" : javacast( "null", "" ), + "ref" : javacast( "null", "" ), + "required" : false, + "argName" : "", + "type" : "any" + }; + } +} \ No newline at end of file diff --git a/system/ioc/config/Mixin.cfc b/system/ioc/config/Mixin.cfc index 1651756dd..e989983ba 100644 --- a/system/ioc/config/Mixin.cfc +++ b/system/ioc/config/Mixin.cfc @@ -1,11 +1,10 @@ -component{ +component { function $init( required mixins ){ - // Include the mixins - for( var thisMixin in arguments.mixins ){ + for ( var thisMixin in arguments.mixins ) { thisMixin = trim( thisMixin ); - if( listLast( thisMixin, "." ) != "cfm" ){ + if ( listLast( thisMixin, "." ) != "cfm" ) { include "#thisMixin#.cfm"; } else { include "#thisMixin#"; @@ -13,13 +12,13 @@ component{ } // Expose them - for( var key in variables ){ - if( isCustomFunction( variables[ key ] ) AND !structKeyExists( this, key ) ){ + for ( var key in variables ) { + if ( isCustomFunction( variables[ key ] ) AND !structKeyExists( this, key ) ) { this[ key ] = variables[ key ]; } } return this; } - -} \ No newline at end of file + +} diff --git a/tests/specs/ioc/config/BinderTest.cfc b/tests/specs/ioc/config/BinderTest.cfc index 0b198f30f..9991925fc 100755 --- a/tests/specs/ioc/config/BinderTest.cfc +++ b/tests/specs/ioc/config/BinderTest.cfc @@ -12,7 +12,8 @@ this.TYPES = createObject( "component", "coldbox.system.ioc.Types" ); mockInjector = createMock( "coldbox.system.ioc.Injector" ) .setColdBox( createStub().$( "getSetting", "coldbox.test" ) ) - .setEventManager( createStub().$( "announce" ) ); + .setEventManager( createStub().$( "announce" ) ) + .setUtility( new coldbox.system.core.util.Util() ); config = createObject( "component", "coldbox.system.ioc.config.Binder" ).init( injector = mockInjector, config = dataConfigPath @@ -256,21 +257,24 @@ } function testEagerInit(){ - config.mapPath( "Test" ); - mapping = config.getMapping( "Test" ); - assertEquals( "", mapping.isEagerInit() ); + config.mapPath( "tests.resources.Test" ); + mapping = config.getMapping( "Test" ).process( + config, + mockInjector + ); + expect( mapping.isEagerInit() ).toBeFalse(); - config.mapPath( "Test" ).asEagerInit(); + config.mapPath( "tests.resources.Test" ).asEagerInit(); mapping = config.getMapping( "Test" ); assertEquals( true, mapping.isEagerInit() ); } function testNoAutowire(){ - config.mapPath( "Test" ); + config.mapPath( "tests.resources.Test" ); mapping = config.getMapping( "Test" ); - assertEquals( "", mapping.isAutowire() ); + assertEquals( "", mapping.getAutoWire() ); - config.mapPath( "Test" ).noAutowire(); + config.mapPath( "tests.resources.Test" ).noAutowire(); mapping = config.getMapping( "Test" ); assertEquals( false, mapping.isAutowire() ); } diff --git a/tests/specs/ioc/config/MappingTest.cfc b/tests/specs/ioc/config/MappingTest.cfc index d15536bdf..cd0ab855e 100755 --- a/tests/specs/ioc/config/MappingTest.cfc +++ b/tests/specs/ioc/config/MappingTest.cfc @@ -11,7 +11,6 @@ assertEquals( "UnitTest", mapping.getName() ); } - function testProcessMemento(){ var data = { alias : [ "funky" ], @@ -21,7 +20,7 @@ threadSafe : true, scope : "singleton", cache : { key : "data", timeout : "30" }, - DIConstructorArgs : [ + DIConstructorArguments : [ { name : "val", value : "0" }, { name : "transfer", ref : "transfer" } ], @@ -41,7 +40,7 @@ argName : "MyService" } ], - DIMethodArgs : [ + DIMethodArguments : [ { name : "Joke", value : "You!" }, { name : "service", ref : "MyService" } ] @@ -54,7 +53,7 @@ assertEquals( data.type, mapping.getTYpe() ); assertEquals( data.path, mapping.getPath() ); assertEquals( "init", mapping.getConstructor() ); - assertEquals( "", mapping.isAutowire() ); + assertEquals( "", mapping.getAutoWire() ); assertEquals( true, mapping.isAutoInit() ); assertEquals( true, mapping.isEagerInit() ); assertEquals( data.scope, mapping.getScope() ); @@ -98,7 +97,7 @@ eagerInit : true, scope : "singleton", cache : { key : "data", timeout : "30" }, - DIConstructorArgs : [ + DIConstructorArguments : [ { name : "val", value : "0" }, { name : "transfer", ref : "transfer" } ], @@ -111,7 +110,7 @@ argName : "MyService" } ], - DIMethodArgs : [ + DIMethodArguments : [ { name : "Joke", value : "You!" }, { name : "service", ref : "MyService" } ] @@ -132,7 +131,7 @@ // process memento should have copied over all other data except the ones above assertEquals( data.type, mapping.getTYpe() ); assertEquals( "init", mapping.getConstructor() ); - assertEquals( "", mapping.isAutowire() ); + assertEquals( "", mapping.getAutoWire() ); assertEquals( true, mapping.isAutoInit() ); assertEquals( true, mapping.isEagerInit() ); assertEquals( data.scope, mapping.getScope() ); diff --git a/tests/specs/ioc/config/samples/SampleWireBox.cfc b/tests/specs/ioc/config/samples/SampleWireBox.cfc index 8ee702f50..721430b73 100755 --- a/tests/specs/ioc/config/samples/SampleWireBox.cfc +++ b/tests/specs/ioc/config/samples/SampleWireBox.cfc @@ -59,7 +59,7 @@ WireBox injector is created buffer : { path : "java.lang.StringBuilder", type : binder.TYPES.JAVA, - DIConstructorArgs : [ + DIConstructorArguments : [ { name : "buffer", value : "16", diff --git a/tests/specs/ioc/config/samples/WireBox.cfc b/tests/specs/ioc/config/samples/WireBox.cfc index fefc12950..e27d7d1d3 100755 --- a/tests/specs/ioc/config/samples/WireBox.cfc +++ b/tests/specs/ioc/config/samples/WireBox.cfc @@ -59,7 +59,7 @@ WireBox injector is created buffer : { path : "java.lang.StringBuilder", type : this.TYPES.JAVA, - DIConstructorArgs : [ + DIConstructorArguments : [ { name : "buffer", value : "16", From 15d3134ffd5b57241daa00ae1001a692fd60031c Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 11 Dec 2020 18:09:53 -0600 Subject: [PATCH 62/78] acf incompats ready to roll for full mapping migrations --- system/Bootstrap.cfc | 14 +- system/aop/Matcher.cfc | 144 ++++++++-------- system/ioc/config/Mapping.cfc | 201 ++++++++-------------- tests/specs/ioc/aop/MatcherTest.cfc | 18 +- tests/specs/ioc/config/samples/LogBox.cfc | 15 +- 5 files changed, 168 insertions(+), 224 deletions(-) diff --git a/system/Bootstrap.cfc b/system/Bootstrap.cfc index 853617e8b..11c9a8279 100644 --- a/system/Bootstrap.cfc +++ b/system/Bootstrap.cfc @@ -25,13 +25,13 @@ component serializable="false" accessors="true"{ property name="COLDBOX_FAIL_FAST"; // param the properties with defaults - param name="COLDBOX_CONFIG_FILE" default=""; - param name="COLDBOX_APP_ROOT_PATH" default="#getDirectoryFromPath( getbaseTemplatePath() )#"; - param name="COLDBOX_APP_KEY" default="cbController"; - param name="COLDBOX_APP_MAPPING" default=""; - param name="appHash" default="#hash( getBaseTemplatePath() )#"; - param name="lockTimeout" default="30" type="numeric"; - param name="COLDBOX_FAIL_FAST" default="true"; + param name="COLDBOX_CONFIG_FILE" default=""; + param name="COLDBOX_APP_ROOT_PATH" default="#getDirectoryFromPath( getbaseTemplatePath() )#"; + param name="COLDBOX_APP_KEY" default="cbController"; + param name="COLDBOX_APP_MAPPING" default=""; + param name="appHash" default="#hash( getBaseTemplatePath() )#"; + param name="lockTimeout" default="30" type="numeric"; + param name="COLDBOX_FAIL_FAST" default="true"; /** * Constructor, called by your Application CFC diff --git a/system/aop/Matcher.cfc b/system/aop/Matcher.cfc index c5f950848..90fc44ca8 100644 --- a/system/aop/Matcher.cfc +++ b/system/aop/Matcher.cfc @@ -50,7 +50,7 @@ component accessors="true"{ /** * Constructor */ - function init(){ + function init(){ reset(); return this; } @@ -58,7 +58,7 @@ component accessors="true"{ /** * Reset the matcher memento to defaults */ - function reset(){ + function reset(){ // prepare instance for this matcher variables.any = false; variables.returns = ""; @@ -67,14 +67,14 @@ component accessors="true"{ variables.instanceOf = ""; variables.regex = ""; variables.methods = ""; - + // Aggregators variables.and = ""; - variables.or = ""; - - return this; + variables.or = ""; + + return this; } - + /** * Get the matcher memento */ @@ -88,60 +88,60 @@ component accessors="true"{ } return memento; } - + /** * Matches a class to this matcher according to its criteria * @target The target to match against to * @mapping The target mapping to match against * @mapping.doc_generic coldbox.system.ioc.config.Mapping */ - boolean function matchClass( required target, required mapping ){ + boolean function matchClass( required target, required mapping ){ var results = matchClassRules( argumentCollection=arguments ); - + // AND matcher set? - if( isObject( variables.and ) ){ - return ( results AND variables.and.matchClass( argumentCollection=arguments ) ); + if( isObject( variables.and ) ){ + return ( results AND variables.and.matchClass( argumentCollection=arguments ) ); } // OR matcher set? - if( isObject( variables.or ) ){ - return ( results OR variables.or.matchClass( argumentCollection=arguments ) ); + if( isObject( variables.or ) ){ + return ( results OR variables.or.matchClass( argumentCollection=arguments ) ); } - - return results; + + return results; } - + /** * Matches a method to this matcher according to its criteria * @metadata The UDF metadata to use for matching */ - boolean function matchMethod( required metadata ){ + boolean function matchMethod( required metadata ){ var results = matchMethodRules( arguments.metadata ); - + // AND matcher set? - if( isObject( variables.and ) ){ - return ( results AND variables.and.matchMethod( arguments.metadata ) ); + if( isObject( variables.and ) ){ + return ( results AND variables.and.matchMethod( arguments.metadata ) ); } // OR matcher set? - if( isObject( variables.or ) ){ - return ( results OR variables.or.matchMethod( arguments.metadata ) ); + if( isObject( variables.or ) ){ + return ( results OR variables.or.matchMethod( arguments.metadata ) ); } - - return results; + + return results; } - + /** * Go through all the rules in this matcher and match * @metadata The UDF metadata to use for matching */ - private boolean function matchMethodRules( required metadata ){ + private boolean function matchMethodRules( required metadata ){ // Some metadata defaults var name = arguments.metadata.name; var returns = "any"; - - if( structKeyExists( arguments.metadata, "returntype" ) ){ - returns = arguments.metadata.returntype; + + if( structKeyExists( arguments.metadata, "returntype" ) ){ + returns = arguments.metadata.returntype; } - + // Start with any() if( variables.any ){ return true; } // Check explicit methods @@ -159,32 +159,32 @@ component accessors="true"{ // annotation if( len( variables.annotation ) AND structKeyExists( arguments.metadata, variables.annotation ) ){ // No annotation value - if( NOT structKeyExists( variables, "annotationValue" ) ){ - return true; + if( NOT structKeyExists( variables, "annotationValue" ) ){ + return true; } - + // check annotation value if( structKeyExists( variables, "annotationValue" ) AND arguments.metadata[ variables.annotation ] EQ variables.annotationValue ){ - return true; + return true; } } - - return false; + + return false; } - + /** * Go through all the rules in this matcher and match * @target The target to match against to * @mapping The target mapping to match against * @mapping.doc_generic coldbox.system.ioc.config.Mapping */ - private boolean function matchClassRules( required target, required mapping ){ + private boolean function matchClassRules( required target, required mapping ){ var md = arguments.mapping.getObjectMetadata(); var path = reReplace( md.name, "(\/|\\)", ".", "all" ); - + // Start with any() - if( variables.any ){ - return true; + if( variables.any ){ + return true; } // Check explicit mappings if( len( variables.mappings ) AND listFindNoCase( variables.mappings, arguments.mapping.getName() ) ){ @@ -201,42 +201,42 @@ component accessors="true"{ // annotation if( len( variables.annotation ) AND structKeyExists( md, variables.annotation ) ){ // No annotation value - if( NOT structKeyExists( variables, "annotationValue" ) ){ - return true; + if( NOT structKeyExists( variables, "annotationValue" ) ){ + return true; } - + // check annotation value if( structKeyExists( variables, "annotationValue" ) AND md[ variables.annotation ] EQ variables.annotationValue ){ - return true; + return true; } } - - return false; + + return false; } - + /** * Match against any method name or class path */ - function any(){ + function any(){ variables.any = true; return this; } - + /** * Match against return types in methods only * @type The type of return to match against. Only for method matching */ - function returns( required type ){ + function returns( required type ){ variables.returns = arguments.type; return this; } - + /** * Matches annotations on components or methods with or without a value * @annotation The annotation to discover * @value The value of the annotation that must match. OPTIONAL */ - function annotatedWith( required annotation, value ){ + function annotatedWith( required annotation, value ){ variables.annotation = arguments.annotation; // the value of the annotation if( structKeyExists( arguments, "value" ) ){ @@ -244,67 +244,67 @@ component accessors="true"{ } return this; } - + /** * Match one, list or array of mapping names. Class Matching Only. * @mappings One, list or array of mappings to match */ - function mappings( required mappings ){ - if( isArray( arguments.mappings ) ){ - arguments.mappings = arrayToList( arguments.mappings ); + function mappings( required mappings ){ + if( isArray( arguments.mappings ) ){ + arguments.mappings = arrayToList( arguments.mappings ); } variables.mappings = arguments.mappings; return this; } - + /** * Matches against a family of components according to the passed classPath. Class Matching Only. * @classPath The class path to verify instance of */ - function instanceOf( required classPath ){ + function instanceOf( required classPath ){ variables.instanceOf = arguments.classPath; return this; } - + /** * Matches a class path or method name to this regular expression * @regex The regular expression to match against */ - function regex( required regex ){ + function regex( required regex ){ variables.regex = arguments.regex; - return this; + return this; } - + /** * A list, one or an array of methods to explicitly match * @methods One, list or array of methods to match */ - function methods( required methods ){ - if( isArray( arguments.methods ) ){ - arguments.methods = arrayToList( arguments.methods ); + function methods( required methods ){ + if( isArray( arguments.methods ) ){ + arguments.methods = arrayToList( arguments.methods ); } variables.methods = arguments.methods; return this; } - + /** * AND this matcher with another matcher * @matcher The matcher to AND this matcher with * @matcher.doc_generic coldbox.system.aop.Matcher */ - function andMatch( required matcher ){ + function andMatch( required matcher ){ variables.and = arguments.matcher; return this; } - + /** * OR this matcher with another matcher * @matcher The matcher to AND this matcher with * @matcher.doc_generic coldbox.system.aop.Matcher */ - function orMatch( required matcher ){ + function orMatch( required matcher ){ variables.or = arguments.matcher; return this; } - + } \ No newline at end of file diff --git a/system/ioc/config/Mapping.cfc b/system/ioc/config/Mapping.cfc index 306599d44..9db65a8c9 100644 --- a/system/ioc/config/Mapping.cfc +++ b/system/ioc/config/Mapping.cfc @@ -12,31 +12,31 @@ component accessors="true"{ */ property name="name"; - property name="alias"; + property name="alias" type="array"; property name="type"; property name="value"; property name="path"; property name="method"; property name="constructor"; property name="autoWire"; - property name="autoInit"; + property name="autoInit" type="boolean"; property name="eagerInit"; property name="scope"; property name="dsl"; - property name="cache"; + property name="cache" type="struct"; property name="DIConstructorArguments"; - property name="DIProperties"; - property name="DISetters"; - property name="DIMethodArguments"; - property name="onDIComplete"; - property name="discovered"; - property name="objectMetadata"; - property name="providerMethods"; - property name="aspect"; - property name="aspectAutoBinding"; + property name="DIProperties" type="array"; + property name="DISetters" type="array"; + property name="DIMethodArguments" type="array"; + property name="onDIComplete" type="array"; + property name="discovered" type="boolean"; + property name="objectMetadata" type="struct"; + property name="providerMethods" type="array"; + property name="aspect" type="boolean"; + property name="aspectAutoBinding" type="boolean"; property name="virtualInheritance"; - property name="extraAttributes"; - property name="mixins"; + property name="extraAttributes" type="struct"; + property name="mixins" type="array"; property name="threadSafe"; property name="influenceClosure"; @@ -121,7 +121,7 @@ component accessors="true"{ } /** - * Process a mapping memento + * Process a mapping memento. Basically takes in a struct of data to process the mapping's data with. * * @memento The data memento to process * @excludes List of memento keys to not process @@ -465,7 +465,6 @@ component accessors="true"{ metadata ){ var md = variables.objectMetadata; - var mappings = arguments.binder.getMappings(); var eventManager = arguments.injector.getEventManager(); var cacheProperties = {}; @@ -602,6 +601,7 @@ component accessors="true"{ var thisAliases = listToArray( md.alias ); variables.alias.addAll( thisAliases ); // register alias references on binder + var mappings = arguments.binder.getMappings(); for ( var x = 1; x lte arrayLen( thisAliases ); x++ ) { mappings[ thisAliases[ x ] ] = this; } @@ -836,140 +836,79 @@ component accessors="true"{ * @return Mapping */ private Mapping function processDIMetadata( required binder, required metadata, dependencies={} ){ - var x = 1; - var y = 1; - var md = arguments.metadata; - var fncLen = 0; - var params = ""; - - // Look For properties for annotation injections - if ( structKeyExists( md, "properties" ) and arrayLen( md.properties ) GT 0 ) { - // Loop over each property and identify injectable properties - for ( x = 1; x lte arrayLen( md.properties ); x = x + 1 ) { - // Check if property not discovered or if inject annotation is found - if ( structKeyExists( md.properties[ x ], "inject" ) ) { - // prepare default params, we do this so we do not alter the md as it is cached by cf - params = { - scope : "variables", - inject : "model", - name : md.properties[ x ].name, - required : true, - type : "any" - }; - // default property type - if ( structKeyExists( md.properties[ x ], "type" ) ) { - params.type = md.properties[ x ].type; - } - // default injection scope, if not found in object - if ( structKeyExists( md.properties[ x ], "scope" ) ) { - params.scope = md.properties[ x ].scope; - } - // Get injection if it exists - if ( len( md.properties[ x ].inject ) ) { - params.inject = md.properties[ x ].inject; - } - // Get required - if ( - structKeyExists( md.properties[ x ], "required" ) and isBoolean( - md.properties[ x ].required - ) - ) { - params.required = md.properties[ x ].required; - } - // Add to property to mappings - addDIProperty( - name = params.name, - dsl = params.inject, - scope = params.scope, - required = params.required, - type = params.type - ); - } - } - } - // end DI properties - - // Method DI discovery - if ( structKeyExists( md, "functions" ) ) { - fncLen = arrayLen( md.functions ); - for ( x = 1; x lte fncLen; x++ ) { - // Verify Processing or do we continue to next iteration for processing - // This is to avoid overriding by parent trees in inheritance chains - if ( structKeyExists( arguments.dependencies, md.functions[ x ].name ) ) { - continue; - } - + // Shortcut + var md = arguments.metadata; + + // Look For properties for annotation injections and register them with the mapping + param md.properties = []; + md.properties + // Only process injectable properties + .filter( function( thisProperty ) { + return structKeyExists( thisProperty, "inject" ); + } ) + // Process each property + .each( function( thisProperty ){ + addDIProperty( + name : arguments.thisProperty.name, + dsl : ( len( arguments.thisProperty.inject ) ? arguments.thisProperty.inject : "model" ), + scope : ( structKeyExists( arguments.thisProperty, "scope" ) ? arguments.thisProperty.scope : "variables" ), + required : ( structKeyExists( arguments.thisProperty, "required" ) ? arguments.thisProperty.required : true ), + type : ( structKeyExists( arguments.thisProperty, "type" ) ? arguments.thisProperty.type : "any" ) + ); + } ); + + // Look For functions for setter injections and more and register them with the mapping + param md.functions = []; + md.functions + // Verify Processing or do we continue to next iteration for processing + // This is to avoid overriding by parent trees in inheritance chains + .filter( function( thisFunction ) { + return !structKeyExists( dependencies, thisFunction.name ); + } ) + .each( function( thisFunction ){ // Constructor Processing if found - if ( md.functions[ x ].name eq variables.constructor ) { - // Loop Over Arguments to process them for dependencies - for ( y = 1; y lte arrayLen( md.functions[ x ].parameters ); y++ ) { - // prepare params as we do not alter md as cf caches it - params = { - required : false, - inject : "model", - name : md.functions[ x ].parameters[ y ].name, - type : "any" - }; - // check type annotation - if ( structKeyExists( md.functions[ x ].parameters[ y ], "type" ) ) { - params.type = md.functions[ x ].parameters[ y ].type; - } - // Check required annotation - if ( structKeyExists( md.functions[ x ].parameters[ y ], "required" ) ) { - params.required = md.functions[ x ].parameters[ y ].required; - } + if ( thisFunction.name eq variables.constructor ) { + // Process parameters for constructor injection + for( var thisParam in thisFunction.parameters ){ // Check injection annotation, if not found then no injection - if ( structKeyExists( md.functions[ x ].parameters[ y ], "inject" ) ) { - // Check if inject has value, else default it to 'model' or 'id' namespace - if ( len( md.functions[ x ].parameters[ y ].inject ) ) { - params.inject = md.functions[ x ].parameters[ y ].inject; - } - + if ( structKeyExists( thisParam, "inject" ) ) { // ADD Constructor argument addDIConstructorArgument( - name = params.name, - dsl = params.inject, - required = params.required, - type = params.type + name = thisParam.name, + dsl = ( len( thisParam.inject ) ? thisParam.inject : "model" ), + required = ( structKeyExists( thisParam, "required" ) ? thisParam.required : false ), + type = ( structKeyExists( thisParam, "type" ) ? thisParam.type : "any" ) ); } + } // add constructor to found list, so it is processed only once in recursions - arguments.dependencies[ md.functions[ x ].name ] = "constructor"; + dependencies[ thisFunction.name ] = "constructor"; } // Setter discovery, MUST be inject annotation marked to be processed. - if ( left( md.functions[ x ].name, 3 ) eq "set" AND structKeyExists( md.functions[ x ], "inject" ) ) { - // setup setter params in order to avoid touching the md struct as cf caches it - params = { - inject : "model", - name : right( md.functions[ x ].name, len( md.functions[ x ].name ) - 3 ) - }; - - // Check DSL marker if it has a value else use default of Model - if ( len( md.functions[ x ].inject ) ) { - params.inject = md.functions[ x ].inject; - } + if ( left( thisFunction.name, 3 ) eq "set" AND structKeyExists( thisFunction, "inject" ) ) { // Add to setter to mappings and recursion lookup - addDISetter( name = params.name, dsl = params.inject ); - arguments.dependencies[ md.functions[ x ].name ] = "setter"; + addDISetter( + name : right( thisFunction.name, len( thisFunction.name ) - 3 ), + dsl : ( len( thisFunction.inject ) ? thisFunction.inject : "model" ) + ); + dependencies[ thisFunction.name ] = "setter"; } // Provider Methods Discovery - if ( structKeyExists( md.functions[ x ], "provider" ) AND len( md.functions[ x ].provider ) ) { - addProviderMethod( md.functions[ x ].name, md.functions[ x ].provider ); - arguments.dependencies[ md.functions[ x ].name ] = "provider"; + if ( structKeyExists( thisFunction, "provider" ) AND len( thisFunction.provider ) ) { + addProviderMethod( thisFunction.name, thisFunction.provider ); + dependencies[ thisFunction.name ] = "provider"; } // onDIComplete Method Discovery - if ( structKeyExists( md.functions[ x ], "onDIComplete" ) ) { - arrayAppend( variables.onDIComplete, md.functions[ x ].name ); - arguments.dependencies[ md.functions[ x ].name ] = "onDIComplete"; + if ( structKeyExists( thisFunction, "onDIComplete" ) ) { + arrayAppend( variables.onDIComplete, thisFunction.name ); + dependencies[ thisFunction.name ] = "onDIComplete"; } - } - // end loop of functions - } - // end if functions found + + } ); // End function processing return this; } diff --git a/tests/specs/ioc/aop/MatcherTest.cfc b/tests/specs/ioc/aop/MatcherTest.cfc index c5b3e18e1..7d6656402 100755 --- a/tests/specs/ioc/aop/MatcherTest.cfc +++ b/tests/specs/ioc/aop/MatcherTest.cfc @@ -9,7 +9,11 @@ var mockMapping = createMock( "coldbox.system.ioc.config.Mapping" ) .init( "UnitTest" ) .setPath( getMetadata( this ).name ) - .$( "getObjectMetadata", getMetadata( this ) ); + .$( + method : "getObjectMetadata", + returns : getMetadata( this ), + preserveReturnType : false + ); // any matcher.any(); @@ -44,7 +48,11 @@ var mockMapping = createMock( "coldbox.system.ioc.config.Mapping" ) .init( "UnitTest" ) .setPath( getMetadata( this ).name ) - .$( "getObjectMetadata", getMetadata( this ) ); + .$( + method : "getObjectMetadata", + returns : getMetadata( this ), + preserveReturnType : false + ); // New AND Matcher andM = createMock( "coldbox.system.aop.Matcher" ).init(); @@ -96,7 +104,11 @@ var mockMapping = createMock( "coldbox.system.ioc.config.Mapping" ) .init( "UnitTest" ) .setPath( getMetadata( this ).name ) - .$( "getObjectMetadata", getMetadata( this ) ); + .$( + method : "getObjectMetadata", + returns : getMetadata( this ), + preserveReturnType : false + ); var fncmd = getMetadata( variables.testMatchMethod ); // any diff --git a/tests/specs/ioc/config/samples/LogBox.cfc b/tests/specs/ioc/config/samples/LogBox.cfc index 2608cc3f0..ef361544b 100644 --- a/tests/specs/ioc/config/samples/LogBox.cfc +++ b/tests/specs/ioc/config/samples/LogBox.cfc @@ -9,21 +9,14 @@ component { function configure(){ var system = createObject( "java", "java.lang.System" ); - var homeDir = expandPath( "/coldbox/tests" ); + var homeDir = expandPath( "/tests" ); logBox = {}; - // Define Appenders logBox.appenders = { - fileAppender : { - class : "coldbox.system.logging.appenders.RollingFileAppender", - properties : { - fileMaxArchives : 5, - filename : "commandbox", - filepath : homeDir & "/logs", - async : true - } + consoleAppender : { + class : "ConsoleAppender" } }; @@ -31,7 +24,7 @@ component { logBox.root = { levelmax : "INFO", levelMin : "FATAL", - appenders : "fileAppender" + appenders : "consoleAppender" }; } From cef3f5a6e82c191bde5a030fc30b3c39c0d7bde8 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 11 Dec 2020 18:13:27 -0600 Subject: [PATCH 63/78] removing code coverage directly, so we can execute only if needed. too many issues latetly --- tests/runner-async.cfm | 2 +- tests/runner-cachebox.cfm | 2 +- tests/runner-core.cfm | 2 +- tests/runner-integration.cfm | 2 +- tests/runner-logbox.cfm | 2 +- tests/runner-wirebox.cfm | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/runner-async.cfm b/tests/runner-async.cfm index 5881086db..c25be6151 100644 --- a/tests/runner-async.cfm +++ b/tests/runner-async.cfm @@ -9,7 +9,7 @@ - + diff --git a/tests/runner-cachebox.cfm b/tests/runner-cachebox.cfm index 79c9171aa..f47e141ee 100644 --- a/tests/runner-cachebox.cfm +++ b/tests/runner-cachebox.cfm @@ -9,7 +9,7 @@ - + diff --git a/tests/runner-core.cfm b/tests/runner-core.cfm index dc2262f6e..2f883f3dd 100644 --- a/tests/runner-core.cfm +++ b/tests/runner-core.cfm @@ -9,7 +9,7 @@ - + diff --git a/tests/runner-integration.cfm b/tests/runner-integration.cfm index cccc885c6..6fcddeb45 100644 --- a/tests/runner-integration.cfm +++ b/tests/runner-integration.cfm @@ -9,7 +9,7 @@ - + diff --git a/tests/runner-logbox.cfm b/tests/runner-logbox.cfm index 63442fe3d..4f287d6ee 100644 --- a/tests/runner-logbox.cfm +++ b/tests/runner-logbox.cfm @@ -9,7 +9,7 @@ - + diff --git a/tests/runner-wirebox.cfm b/tests/runner-wirebox.cfm index db3915866..7a8f2a27f 100644 --- a/tests/runner-wirebox.cfm +++ b/tests/runner-wirebox.cfm @@ -9,7 +9,7 @@ - + From 76dc53896c4c65d2fcda3ce6e4cdbf40b3de3df6 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 11 Dec 2020 18:26:58 -0600 Subject: [PATCH 64/78] activating docs again --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c56e8b18e..d1ee681e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ before_script: script: # install dependencies - box install - #- cd apidocs && box install + - cd apidocs && box install - cd $TRAVIS_BUILD_DIR # Startup the server to test - box server start serverConfigFile="server-${ENGINE}.json" --debug From 349c6c370e4b870d12b8f2a90d9f6beec3d5b6a2 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Fri, 11 Dec 2020 18:49:38 -0600 Subject: [PATCH 65/78] WIREBOX-102 #resolve ACF incompats with future combinations due to dumb elvis operator bug --- system/async/proxies/BiFunction.cfc | 4 ++-- tests/specs/async/AsyncManagerSpec.cfc | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/system/async/proxies/BiFunction.cfc b/system/async/proxies/BiFunction.cfc index 8be3f7a11..0e86729d4 100644 --- a/system/async/proxies/BiFunction.cfc +++ b/system/async/proxies/BiFunction.cfc @@ -23,8 +23,8 @@ component extends="BaseProxy" { try { lock name="#getConcurrentEngineLockName()#" type="exclusive" timeout="60" { return variables.target( - arguments.t ?: javaCast( "null", "" ), - arguments.u ?: javaCast( "null", "" ) + isNull( arguments.t ) ? javaCast( "null", "" ) : arguments.t, + isNull( arguments.u ) ? javaCast( "null", "" ) : arguments.u ); } } finally { diff --git a/tests/specs/async/AsyncManagerSpec.cfc b/tests/specs/async/AsyncManagerSpec.cfc index f023e23d5..55351e672 100644 --- a/tests/specs/async/AsyncManagerSpec.cfc +++ b/tests/specs/async/AsyncManagerSpec.cfc @@ -173,6 +173,7 @@ component extends="BaseAsyncSpec" { debug( "calculating BMI" ); var combinedFuture = weightFuture.thenCombine( heightFuture, function( weight, height ){ + writeDump( var = arguments, output="console" ); var heightInMeters = arguments.height / 100; return arguments.weight / ( heightInMeters * heightInMeters ); } ); From e4cf5dd6f97afd3e11e9fd025e120e2c3a195977 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 14 Dec 2020 16:58:55 -0600 Subject: [PATCH 66/78] COLDBOX-955 #resolve If in an Ajax request and an exception occurs using Whoops the view is unusable --- system/exceptions/Whoops.cfm | 23 ++++++++++++++++++++++- test-harness/includes/rev-manifest.json | 1 + test-harness/layouts/Main.cfm | 5 +++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/system/exceptions/Whoops.cfm b/system/exceptions/Whoops.cfm index a08734157..51282c749 100644 --- a/system/exceptions/Whoops.cfm +++ b/system/exceptions/Whoops.cfm @@ -2,12 +2,14 @@ // Detect Session Scope local.sessionScopeExists = getApplicationMetadata().sessionManagement; + // Detect host try { local.thisInetHost = createObject( "java", "java.net.InetAddress" ).getLocalHost().getHostName(); } catch ( any e ) { local.thisInetHost = "localhost"; } + // Build event details local.eventDetails = { "Error Code" : ( oException.getErrorCode() != 0 ) ? oException.getErrorCode() : "", @@ -102,9 +104,28 @@ }; } + // Get exception information and mark the safe environment token local.e = oException.getExceptionStruct(); stackFrames = arrayLen( local.e.TagContext ); local.safeEnvironment = "development"; + + // Is this an Ajax Request? If so, present the plain exception templates + local.requestHeaders = getHTTPRequestData( false ).headers; + if( + structKeyExists( local.requestHeaders, "X-Requested-With" ) + && + local.requestHeaders[ "X-Requested-With" ] eq "XMLHttpRequest" + ){ + // Development report + if( local.eventDetails.environment eq local.safeEnvironment ){ + include "BugReport.cfm"; + } + // Production Report + else { + include "BugReport-Public.cfm"; + } + return; + } @@ -337,7 +358,7 @@
- #oException.displayScope( getHTTPRequestData( false ).headers )# + #oException.displayScope( local.requestHeaders )#
diff --git a/test-harness/includes/rev-manifest.json b/test-harness/includes/rev-manifest.json index 044ef5908..e4b012dc7 100644 --- a/test-harness/includes/rev-manifest.json +++ b/test-harness/includes/rev-manifest.json @@ -1,4 +1,5 @@ { "test-harness/includes/js/jquery.js": "/test-harness/includes/js/jquery.1.js", + "test-harness/includes/js/bootstrap.min.js": "/test-harness/includes/js/bootstrap.min.js", "test-harness/modules_app/contentbox-custom/_modules/cctManager/includes/js/cctManager.js": "/modules_app/contentbox-custom/_modules/cctManager/includes/js/cctManager.8613ac4947980152a2e0.js" } \ No newline at end of file diff --git a/test-harness/layouts/Main.cfm b/test-harness/layouts/Main.cfm index a74e4cba4..ceab95c5f 100644 --- a/test-harness/layouts/Main.cfm +++ b/test-harness/layouts/Main.cfm @@ -96,6 +96,11 @@ $('.dropdown-toggle').dropdown(); // Tooltips $("[rel=tooltip]").tooltip(); + // Load Exception via AJAX + $.get( + "index.cfm/testerror" + ); + }) From f716fa1c71a5964dc8b72f86128f8e2c1bcbffcb Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 14 Dec 2020 17:06:12 -0600 Subject: [PATCH 67/78] remove exception tester --- system/ioc/config/Mapping.cfc | 9 ++++----- test-harness/layouts/Main.cfm | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/system/ioc/config/Mapping.cfc b/system/ioc/config/Mapping.cfc index 9db65a8c9..f80ab30e5 100644 --- a/system/ioc/config/Mapping.cfc +++ b/system/ioc/config/Mapping.cfc @@ -874,13 +874,12 @@ component accessors="true"{ if ( structKeyExists( thisParam, "inject" ) ) { // ADD Constructor argument addDIConstructorArgument( - name = thisParam.name, - dsl = ( len( thisParam.inject ) ? thisParam.inject : "model" ), - required = ( structKeyExists( thisParam, "required" ) ? thisParam.required : false ), - type = ( structKeyExists( thisParam, "type" ) ? thisParam.type : "any" ) + name : thisParam.name, + dsl : ( len( thisParam.inject ) ? thisParam.inject : "model" ), + required : ( structKeyExists( thisParam, "required" ) ? thisParam.required : false ), + type : ( structKeyExists( thisParam, "type" ) ? thisParam.type : "any" ) ); } - } // add constructor to found list, so it is processed only once in recursions dependencies[ thisFunction.name ] = "constructor"; diff --git a/test-harness/layouts/Main.cfm b/test-harness/layouts/Main.cfm index ceab95c5f..b5c180e3c 100644 --- a/test-harness/layouts/Main.cfm +++ b/test-harness/layouts/Main.cfm @@ -97,9 +97,9 @@ // Tooltips $("[rel=tooltip]").tooltip(); // Load Exception via AJAX - $.get( - "index.cfm/testerror" - ); + //$.get( + // "index.cfm/testerror" + //); }) From db6f99d4c8d9d5bfe8f5276ad4b6e52e3a1b9ab4 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 14 Dec 2020 17:53:53 -0600 Subject: [PATCH 68/78] more debugging on exceptions --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d1ee681e6..eedb1920d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,7 +73,7 @@ after_failure: - curl http://localhost:8599/tests/runner.cfm?reporter=text #- curl http://localhost:8599/tests/tools/IDEDictionaries/builderDictionary.cfm?text=true # Spit out our Commandbox log in case we need to debug - - box server log name=$ENGINE + - box server log serverConfigFile="server-${ENGINE}.json" #- cat `box system-log` - ls -lr $TRAVIS_BUILD_DIR From b4465f6ca04949da914e4f7c37d1c53331b94bf1 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Mon, 14 Dec 2020 17:55:19 -0600 Subject: [PATCH 69/78] more cleanups --- tests/specs/integration/EventExecutions.cfc | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/specs/integration/EventExecutions.cfc b/tests/specs/integration/EventExecutions.cfc index dbc40235c..15f1fd5f6 100644 --- a/tests/specs/integration/EventExecutions.cfc +++ b/tests/specs/integration/EventExecutions.cfc @@ -5,6 +5,7 @@ component extends="tests.resources.BaseIntegrationTest"{ function run(){ describe( "Event Execution System", function(){ beforeEach( function( currentSpec ){ + structDelete( request, "_lastInvalidEvent" ); // Setup as a new ColdBox request, VERY IMPORTANT. ELSE EVERYTHING LOOKS LIKE THE SAME REQUEST. setup(); } ); From 98e5bf33be2e0e2411553b2e58ec1d069d727460 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 16 Dec 2020 10:05:08 -0600 Subject: [PATCH 70/78] WIREBOX-104 #resolve New WireBox config: autoProcessMappings which can be used to auto process metadata inspections on startup. WIREBOX-103 #resolve Complete rewrite of the WireBox Binder to script and optimizations WIREBOX-101 #resolve Completely rewrite of the Mapping object to script and performance optimizations WIREBOX-100 #resolve Create a processEagerInits() so it can process them at wirebox load --- .cfconfig.json | 1 + system/ioc/Injector.cfc | 13 +- system/ioc/Scopes.cfc | 1 + system/ioc/config/Binder.cfc | 2618 ++++++++++--------------- system/ioc/config/Mapping.cfc | 26 +- tests/specs/ioc/BuilderTest.cfc | 7 +- tests/specs/ioc/InjectorTest.cfc | 46 +- tests/specs/ioc/aop/MixerTest.cfc | 4 +- tests/specs/ioc/config/BinderTest.cfc | 2 +- tests/tmp/scribble.cfm | 14 +- 10 files changed, 1134 insertions(+), 1598 deletions(-) diff --git a/.cfconfig.json b/.cfconfig.json index b34a4341d..581c97583 100644 --- a/.cfconfig.json +++ b/.cfconfig.json @@ -1,4 +1,5 @@ { + "adminPassword" : "coldbox", "ajaxDebugWindowEnabled": false, "debuggingEnabled": false, "debuggingReportExecutionTimes": false, diff --git a/system/ioc/Injector.cfc b/system/ioc/Injector.cfc index 07ee439e5..3b26f9373 100644 --- a/system/ioc/Injector.cfc +++ b/system/ioc/Injector.cfc @@ -256,14 +256,19 @@ component serializable="false" accessors="true" implements="coldbox.system.ioc.I variables.eventManager.registerInterceptor( interceptorObject=variables.binder, interceptorName="wirebox-binder" ); } - // Check if binder has onLoad convention + // process mappings for metadata and initialization. + if( variables.binder.getAutoProcessMappings() ){ + variables.binder.processMappings(); + } + + // Process Eager Inits + variables.binder.processEagerInits(); + + // Check if binder has onLoad convention and execute callback if( structKeyExists( variables.binder, "onLoad" ) ){ variables.binder.onLoad( this ); } - // process mappings for metadata and initialization. - //variables.binder.processMappings(); - // Announce To Listeners we are online iData.injector = this; variables.eventManager.announce( "afterInjectorConfiguration", iData ); diff --git a/system/ioc/Scopes.cfc b/system/ioc/Scopes.cfc index cf5a1f906..ec4330c76 100644 --- a/system/ioc/Scopes.cfc +++ b/system/ioc/Scopes.cfc @@ -18,6 +18,7 @@ component{ /** * Verify if an incoming scope is valid + * * @scope The scope to check */ boolean function isValidScope( required scope ){ diff --git a/system/ioc/config/Binder.cfc b/system/ioc/config/Binder.cfc index 3325d9830..b3a6aafe0 100644 --- a/system/ioc/config/Binder.cfc +++ b/system/ioc/config/Binder.cfc @@ -1,35 +1,138 @@ - - - +/** + * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * www.ortussolutions.com + * --- + * This is the WireBox configuration Binder. You use it to configure an injector instance. + * This binder will hold all your object mappings and injector settings. + */ +component accessors="true" { + + /** + * The current mapping pointer for DSL configurations + */ + property name="currentMapping" type="array"; + + /** + * The configuration properties/settings for the app context or the injector context (standalone) + */ + property name="properties" type="struct"; + + /** + * The injector reference this binder is bound to + */ + property name="injector"; + + /** + * The ColdBox reference this binder is bound to, this can be null + */ + property name="coldbox"; + + /** + * Main WireBox configuration structure + */ + property name="wirebox" type="struct"; + + /** + * Main CacheBox configuration structure + */ + property name="cachebox" type="struct"; + + /** + * The configuration CFC for this binder + */ + property name="config"; + + /** + * The shortcut application mapping string + */ + property name="appMapping"; + + /** + * The LogBox config file + */ + property name="logBoxConfig"; + + /** + * The Listeners + */ + property name="listeners" type="array"; + + /** + * The scope registration config + */ + property name="scopeRegistration" type="struct"; + + /** + * The custom DSL namespaces + */ + property name="customDSL" type="struct"; + + /** + * The custom scopes + */ + property name="customScopes" type="struct"; + + /** + * The scan locations for this binder + */ + property name="scanLocations" type="struct"; + + /** + * The collection of mappings + */ + property name="mappings" type="struct"; + + /** + * The aspects binded to mappings + */ + property name="aspectBindings" type="array"; + + /** + * The parent injector mapping + */ + property name="parentInjector"; + + /** + * The stop recursions for the binder + */ + property name="stopRecursions" type="array"; + + /** + * The metadata cache for this binder + */ + property name="metadataCache"; + + /** + * Boolean indicator if on startup all mappings will be processed for metdata inspections or + * lazy loaded. We default to lazy load due to performance. + */ + property name="autoProcessMappings" type="boolean"; + + /** + * The configuration DEFAULTS struct + */ + property + name ="DEFAULTS" + setter="false" + type ="struct"; + + /** + * -------------------------------------------------- + * Binder Public References + * -------------------------------------------------- + * One day move as static references + */ + // Available WireBox public scopes - this.SCOPES = createObject( "component", "coldbox.system.ioc.Scopes" ); + this.SCOPES = new coldbox.system.ioc.Scopes(); // Available WireBox public types - this.TYPES = createObject( "component", "coldbox.system.ioc.Types" ); - // Utility class - this.UTILITY = createObject( "component", "coldbox.system.core.util.Util" ); - // Contains the mappings currently being affected by the DSL. - currentMapping = []; - // Instance private scope - instance = {}; - // WireBox Defaults - DEFAULTS = { - // LogBox Defaults + this.TYPES = new coldbox.system.ioc.Types(); + + // WireBox Operational Defaults + variables.DEFAULTS = { + // LogBox Default Config logBoxConfig : "coldbox.system.ioc.config.LogBox", - // Scope Defaults + // Scope Registration scopeRegistration : { enabled : true, scope : "application", key : "wireBox" }, // CacheBox Integration Defaults cacheBox : { @@ -37,350 +140,243 @@ This binder will hold all your object mappings, injector settings and more. configFile : "", cacheFactory : "", classNamespace : "coldbox.system.cache" - } + }, + // Auto process mappings on startup + // We lazy process all mappings until requested + autoProcessMappings : false }; + // Startup the configuration reset(); - - - - - - - - + + /** + * Constructor + * + * @injector The injector this binder is bound to + * @config The WireBox Injector Data Configuration CFC instance or instantiation path to it. Leave blank if using this configuration object programatically + * @properties A structure of binding properties to passthrough to the Binder Configuration CFC + */ + function init( + required injector, + config, + struct properties = {} + ){ // Setup incoming properties - instance.properties = arguments.properties; + variables.properties = arguments.properties; // Setup Injector this binder is bound to. - instance.injector = arguments.injector; + variables.injector = arguments.injector; // ColdBox Context binding if any? - instance.coldbox = instance.injector.getColdBox(); + variables.coldbox = variables.injector.getColdBox(); // is coldbox linked - if ( isObject( instance.coldbox ) ) { - variables.appMapping = instance.coldbox.getSetting( "AppMapping" ); + if ( isObject( variables.coldbox ) ) { + variables.appMapping = variables.coldbox.getSetting( "AppMapping" ); } - // If sent and a path, then create the data CFC - if ( structKeyExists( arguments, "config" ) and isSimpleValue( arguments.config ) ) { + // If Config CFC sent and a path, then create the data CFC + if ( !isNull( arguments.config ) and isSimpleValue( arguments.config ) ) { arguments.config = createObject( "component", arguments.config ); } - // If sent and a data CFC instance - if ( structKeyExists( arguments, "config" ) and isObject( arguments.config ) ) { + // If sent and a data CFC variables + if ( !isNull( arguments.config ) and isObject( arguments.config ) ) { // Decorate our data CFC - arguments.config.getPropertyMixin = this.utility.getMixerUtil().getPropertyMixin; + arguments.config.getPropertyMixin = variables.injector.getUtil().getMixerUtil().getPropertyMixin; // Execute the configuration arguments.config.configure( this ); // Load the raw data DSL - loadDataDSL( arguments.config.getPropertyMixin( "wireBox", "variables", structNew() ) ); + loadDataDSL( arguments.config.getPropertyMixin( "wireBox", "variables", {} ) ); } return this; - - - - - - - - - - - - - - - - - - - - - - - - - - + } + + /** + * The main configuration method that must be overriden by a specific WireBox Binder configuration object + */ + function configure(){ + // Implemented by concrete classes + } + + /** + * Reset the configuration back to the original binder defaults + */ + Binder function reset(){ + // Contains the mappings currently being affected by the DSL. + variables.currentMapping = []; // Main wirebox structure - variables.wirebox = {}; + variables.wirebox = {}; // logBox File - instance.logBoxConfig = DEFAULTS.logBoxConfig; + variables.logBoxConfig = variables.DEFAULTS.logBoxConfig; // CacheBox integration - instance.cacheBox = DEFAULTS.cacheBox; - // Listeners - instance.listeners = []; + variables.cacheBox = variables.DEFAULTS.cacheBox; // Scope Registration - instance.scopeRegistration = DEFAULTS.scopeRegistration; + variables.scopeRegistration = variables.DEFAULTS.scopeRegistration; // Custom DSL namespaces - instance.customDSL = {}; + variables.customDSL = {}; // Custom Storage Scopes - instance.customScopes = {}; + variables.customScopes = {}; // Package Scan Locations - instance.scanLocations = createObject( "java", "java.util.LinkedHashMap" ).init( 5 ); + variables.scanLocations = structNew( "ordered" ); + // Parent Injector Mapping + variables.parentInjector = ""; + // Stop Recursion classes + variables.stopRecursions = []; + // Listeners + variables.listeners = []; // Object Mappings - instance.mappings = {}; + variables.mappings = {}; // Aspect Bindings - instance.aspectBindings = []; - // Parent Injector Mapping - instance.parentInjector = ""; + variables.aspectBindings = []; // Binding Properties - instance.properties = {}; - // Stop Recursion classes - instance.stopRecursions = []; + variables.properties = {}; // Meatadata cache - instance.metadataCache = ""; - - - - - - - - - - - - - - - - - - - - - - - if ( propertyExists( arguments.name ) ) { - return instance.properties[ arguments.name ]; - } - if ( structKeyExists( arguments, "default" ) ) { + variables.metadataCache = ""; + // Auto Process Mappings + variables.autoProcessMappings = variables.DEFAULTS.autoProcessMappings; + + return this; + } + + /** + * -------------------------------------------------- + * Binder Property Binding Methods + * -------------------------------------------------- + */ + + /** + * Get a binded property. If not found it will try to return the default value passed, else it returns an exception + * + * @name The name of the property to get + * @defaultValue The default value if property is not found + * + * @throws PropertyNotFoundException - If the property is not found and no default sent + * + * @return Property value + */ + function getProperty( required name, defaultValue ){ + // Prop Check + if ( structKeyExists( variables.properties, arguments.name ) ) { + return variables.properties[ arguments.name ]; + } + + // TODO: remove by v7 + // Deprecated Check + if ( !isNull( arguments.default ) ) { return arguments.default; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - instance.mappings[ arguments.name ] = arguments.mapping; - - - - - - - - structDelete( instance.mappings, arguments.name ); - - - - - - - - - - - - - - - - - - + + // Default Value + if ( !isNull( arguments.defaultValue ) ) { + return arguments.defaultValue; + } + + // Throw exception + throw( + message = "The property requested #arguments.name# was not found", + detail = "Properties defined are #structKeyList( variables.properties )#", + type = "PropertyNotFoundException" + ); + } + + /** + * Create a new binding property + * + * @name The name of the property to set + * @value The value of the property + */ + Binder function setProperty( required name, required value ){ + variables.properties[ arguments.name ] = arguments.value; + return this; + } + + /** + * Verify if a property exists + * + * @name The name of the property to verify + */ + Boolean function propertyExists( required name ){ + return structKeyExists( variables.properties, arguments.name ); + } + + /** + * -------------------------------------------------- + * Binder Mapping Methods + * -------------------------------------------------- + */ + + /** + * Get a specific object mapping + * + * @name The name of the mapping + * + * @throws MappingNotFoundException - If the named mapping has not been registered + * @return coldbox.system.ioc.config.Mapping + */ + Mapping function getMapping( required name ){ + if ( structKeyExists( variables.mappings, arguments.name ) ) { + return variables.mappings[ arguments.name ]; + } + + throw( + message = "Mapping #arguments.name# has not been registered", + detail = "Registered mappings are: #structKeyList( variables.mappings )#", + type = "MappingNotFoundException" + ); + } + + /** + * Set a mapping object into the mappings map + * + * @name The name of the mapping + * @mapping The mapping object to register + */ + Binder function setMapping( required name, required mapping ){ + variables.mappings[ arguments.name ] = arguments.mapping; + return this; + } + + /** + * Destroys a registered mapping by name + * + * @name The name of the mapping + * + * @return A boolean indicator if the mapping was removed or not + */ + boolean function unMap( required name ){ + return structDelete( variables.mappings, arguments.name ); + } + + /** + * Verifies if a mapping exists in this binder or not + * + * @name The name of the mapping + */ + boolean function mappingExists( required name ){ + return structKeyExists( variables.mappings, arguments.name ); + } + + /** + * -------------------------------------------------- + * Binder Mapping DSL methods + * -------------------------------------------------- + */ + + /** + * Directly map to a path by using the last part of the path as the alias. + * This is equivalent to map('MyService').to('model.MyService'). + * Only use if the name of the alias is the same as the last part of the path. + * + * @path The class path to the object to map + * @namespace Provide namespace to merge it in + * @prepend Where to attach the namespace, at the beginning of the name or end of the name. Defaults to end of name + * @force Forces the registration of the mapping in case it already exists + */ + function mapPath( + required path, + namespace = "", + boolean prepend = false, + boolean force = false + ){ var cName = listLast( arguments.path, "." ); if ( arguments.prepend ) { @@ -391,170 +387,140 @@ This binder will hold all your object mappings, injector settings and more. // directly map to a path return map( cName, arguments.force ).to( arguments.path ); - - - - - - - - - - - - - - - var directory = expandPath( "/#replace( arguments.packagePath, ".", "/", "all" )#" ); - var qObjects = ""; - var thisTargetPath = ""; - var tmpCurrentMapping = []; + } + + /** + * Maps an entire instantiation path directory, please note that the unique name of each file will be used and also processed for alias inspection + * + * @packagePath The instantiation packagePath to map + * @include An include regex that if matches will only include CFCs that match this case insensitive regex + * @exclude An exclude regex that if matches will exclude CFCs that match this case insensitive regex + * @influence The influence closure or UDF that will receive the currently working mapping so you can influence it during the iterations + * @filter The filter closure or UDF that will receive the path of the CFC to process and returns TRUE to continue processing or FALSE to skip processing + * @namespace Provide namespace to merge it in + * @prepend Where to attach the namespace, at the beginning of the name or end of the name. Defaults to end of name + * @process If true, all mappings discovered will be automatically processed for metdata and inspections. Default is false, everything lazy loads + * + * @throws DirectoryNotFoundException - If the requested package path does not exist. + */ + Binder function mapDirectory( + required packagePath, + include = "", + exclude = "", + influence, + filter, + namespace = "", + boolean prepend = false, + boolean process = false + ){ + // check directory exists + var targetDirectory = expandPath( "/#replace( arguments.packagePath, ".", "/", "all" )#" ); + if ( NOT directoryExists( targetDirectory ) ) { + throw( + message = "Directory does not exist", + detail = "Directory: #targetDirectory#", + type = "DirectoryNotFoundException" + ); + } // Clear out any current mappings - currentMapping = []; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - for ( var mapping in getCurrentMapping() ) { - mapping.process( binder = this, injector = instance.injector ); - } + variables.currentMapping = []; + + // Scan + Process Objects + directoryList( + targetDirectory, // path + true, // recurse + "path", // list info + "*.cfc" // filter + ) + // Skip hidden files/dirs and also paths not in the include/exclude lists + .filter( function( thisPath ){ + // Skip hidden dirs (like .Appledouble) + if( left( arguments.thisPath, 1 ) EQ "." ){ + return false; + } + // If any of the following are true, then process it, else skip it + return ( + // We have an include list and the path matches + ( len( include ) AND reFindNoCase( include, arguments.thisPath ) ) + OR + // We have an exclude list and the path doesn't match + ( len( exclude ) AND NOT reFindNoCase( exclude, arguments.thisPath ) ) + // We have a closure filter, we ask the filter + OR + ( !isNull( filter ) AND filter( arguments.thisPath ) ) + OR + // No include, no exclude and no filter + ( NOT len( include ) AND NOT len( exclude ) AND isNull( filter ) ) + ); + } ) + // Transform the path to something usable for object creation + // leading slash, append package path, remove .cfc and /\ with . notation + .map( function( thisPath ){ + // Remove root directory from path to get relative path + arguments.thisPath = replaceNoCase( arguments.thisPath, targetDirectory, "" ); + // Process rest of manips + return reReplace( + reReplace( packagePath, "^/", "" ) & replaceNoCase( arguments.thisPath, ".cfc", "" ), + "(/|\\)", + ".", + "all" + ); + } ) + // Process the path + .each( function( thisPath ){ + // Backup the current array of mappings + var tmpCurrentMapping = variables.currentMapping; + + // Map the path + mapPath( + path : arguments.thisPath, + namespace : namespace, + prepend : prepend, + force : true + ); + + // Are we influencing? + if( !isNull( influence ) ){ + influence( this, arguments.thisPath, variables.currentMapping[ 1 ] ); + } + + /** + * Do this right away so aliases are picked up before this mapping potentially gets overwritten + * This is neccessary for multuple CFCs with the same name in different folders, but with unique aliases + * TODO: Move to async + */ + if( process ){ + variables.currentMapping[ 1 ].process( binder = this, injector = variables.injector ); + } + + // Merge the full array of mappings back together + arrayAppend( tmpCurrentMapping, variables.currentMapping[ 1 ] ); + variables.currentMapping = tmpCurrentMapping; + } ); + + return this; + } + + /** + * Auto process a mapping once defined, this is usually done if there are critical annotations that must be read upon startup, else avoid it and let them lazy load + */ + Binder function process(){ + variables.mappings.each( function( thisMapping ){ + arguments.thisMapping.process( binder : this, injector : variables.injector ); + } ); return this; - - - - - - - - + } + + /** + * Create a mapping to an object + * + * @alias A single alias or a list or an array of aliases for this mapping. Remember an object can be refered by many names + * @force Forces the registration of the mapping in case it already exists + */ + Binder function map( required alias, boolean force = false ){ // Clear out any current mappings - currentMapping = []; - - // generate mapping entry for this dude. - var name = ""; - var x = 1; + variables.currentMapping = []; // unflatten list if ( isSimpleValue( arguments.alias ) ) { @@ -562,867 +528,586 @@ This binder will hold all your object mappings, injector settings and more. } // first entry - name = arguments.alias[ 1 ]; + var name = arguments.alias[ 1 ]; // check if mapping exists, if so, just use and return. - if ( structKeyExists( instance.mappings, name ) and !arguments.force ) { - arrayAppend( currentMapping, instance.mappings[ name ] ); + if ( structKeyExists( variables.mappings, name ) and !arguments.force ) { + arrayAppend( variables.currentMapping, variables.mappings[ name ] ); return this; } // generate the mapping for the first name passed - instance.mappings[ name ] = createObject( "component", "coldbox.system.ioc.config.Mapping" ).init( name ); + variables.mappings[ name ] = new coldbox.system.ioc.config.Mapping( name ); - // set the current mapping - arrayAppend( currentMapping, instance.mappings[ name ] ); + // set as the current mapping + arrayAppend( variables.currentMapping, variables.mappings[ name ] ); // Set aliases, scopes and types - instance.mappings[ name ].setAlias( arguments.alias ).setType( this.TYPES.CFC ); + variables.mappings[ name ].setAlias( arguments.alias ).setType( this.TYPES.CFC ); // Loop and create alias references - for ( x = 2; x lte arrayLen( arguments.alias ); x++ ) { - instance.mappings[ arguments.alias[ x ] ] = instance.mappings[ name ]; + for ( var x = 2; x lte arrayLen( arguments.alias ); x++ ) { + variables.mappings[ arguments.alias[ x ] ] = variables.mappings[ name ]; } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map to a destination CFC class path. + * + * @path The class path to the object to map + */ + Binder function to( required path ){ + for ( var mapping in variables.currentMapping ) { mapping.setPath( arguments.path ).setType( this.TYPES.CFC ); } return this; - - - - - - - + } + + /** + * this method lets you use an abstract or parent mapping as a template for other like objects + * + * @alias The parent class to copy dependencies and definitions from + */ + Binder function parent( required alias ){ // copy parent class's memento instance, exclude alias, name and path - for ( var mapping in getCurrentMapping() ) { + for ( var mapping in variables.currentMapping ) { mapping.processMemento( getMapping( arguments.alias ).getMemento(), "alias,name" ); } return this; - - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map an alias to a factory and its executing method + * + * @factory The mapping factory reference name + * @method The method to execute + */ + Binder function toFactoryMethod( required factory, required method ){ + for ( var mapping in variables.currentMapping ) { mapping .setType( this.TYPES.FACTORY ) .setPath( arguments.factory ) .setMethod( arguments.method ); } return this; - - - - - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map a method argument to a factory method + * + * @name The name of the method argument (Not used for: JAVA,WEBSERVICE) + * @ref The reference mapping id this method argument maps to + * @dsl The construction dsl this argument references. If used, the name value must be used. + * @value The explicit value of the method argument, if passed. + * @javaCast The type of javaCast() to use on the value of the argument. Only used if using dsl or ref arguments + * @required If the argument is required or not, by default we assume required DI arguments + * @type The type of the argument + */ + Binder function methodArg( + name, + ref, + dsl, + value, + javaCast, + required required=true, + type = "any" + ){ + for ( var mapping in variables.currentMapping ) { mapping.addDIMethodArgument( argumentCollection = arguments ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map to a java destination class path + * + * @path The class path to the object to map + */ + Binder function toJava( required path ){ + for ( var mapping in variables.currentMapping ) { mapping.setPath( arguments.path ).setType( this.TYPES.JAVA ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map to a webservice destination class path + * + * @path The webservice path to the object to map + */ + Binder function toWebservice( required path ){ + for ( var mapping in variables.currentMapping ) { mapping.setPath( arguments.path ).setType( this.TYPES.WEBSERVICE ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map to an rss destination + * + * @path The rss path to the object to map + */ + Binder function toRSS( required path ){ + for ( var mapping in variables.currentMapping ) { mapping.setPath( arguments.path ).setType( this.TYPES.RSS ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map to a dsl that will be used to create the mapped object + * + * @dsl The dsl to the object to map + */ + Binder function toDSL( required dsl ){ + for ( var mapping in variables.currentMapping ) { mapping.setDSL( arguments.dsl ).setType( this.TYPES.DSL ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map to a provider object that must implement coldbox.system.ioc.IProvider or a closure or UDF + * + * @provider The provider to map to + */ + Binder function toProvider( required provider ){ + for ( var mapping in variables.currentMapping ) { mapping.setPath( arguments.provider ).setType( this.TYPES.PROVIDER ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map to a constant value + * + * @value The value to bind to + */ + Binder function toValue( required value ){ + for ( var mapping in variables.currentMapping ) { mapping.setValue( arguments.value ).setType( this.TYPES.CONSTANT ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * You can choose what method will be treated as the constructor. By default the value is 'init', so don't call this method if that is the case + * + * @constructor The constructor method to use for the mapped object + */ + Binder function constructor( required constructor ){ + for ( var mapping in variables.currentMapping ) { mapping.setConstructor( arguments.constructor ); } return this; - - - - - - - var key = ""; - for ( key in arguments ) { - for ( var mapping in getCurrentMapping() ) { - mapping.addDIConstructorArgument( name = key, value = arguments[ key ] ); + } + + /** + * Positional or named value arguments to use when initializing the mapping. (CFC-only) + */ + Binder function initWith(){ + for ( var thisArg in arguments ) { + for ( var mapping in variables.currentMapping ) { + mapping.addDIConstructorArgument( name = thisArg, value = arguments[ thisArg ] ); } } return this; - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * If you call this method on an object mapping, the object's constructor will not be called. By default all constructors are called + */ + Binder function noInit(){ + for ( var mapping in variables.currentMapping ) { mapping.setAutoInit( false ); } return this; - - - - - - - - for ( var thisMapping in getCurrentMapping() ) { - thisMapping.setVirtualInheritance( mapping ); + } + + /** + * Tells WireBox to do a virtual inheritance mixin of the target and this passed mapping + * + * @mapping The mapping name of CFC to create the virtual inheritance from + */ + Binder function virtualInheritance( required mapping ){ + for ( var thisMapping in variables.currentMapping ) { + thisMapping.setVirtualInheritance( arguments.mapping ); } return this; - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * If this method is called, the mapped object will be created once the injector starts up. Basically, not lazy loaded + */ + Binder function asEagerInit(){ + for ( var mapping in variables.currentMapping ) { mapping.setEagerInit( true ); } return this; - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * If you call this method on an object mapping, the object will NOT be inspected for injection/wiring metadata, it will use ONLY whatever you define in the mapping + */ + Binder function noAutowire(){ + for ( var mapping in variables.currentMapping ) { mapping.setAutowire( false ); } return this; - - - - - - - + } + + /** + * Used to set the current working mapping name in place for the maping DSL. An exception is thrown if the mapping does not exist yet. + * + * @alias The name of the mapping to set as the current working mapping + * + * @throws InvalidMappingStateException - If the alias has not been registered yet + */ + Binder function with( required alias ){ + // Check if it has been registered yet if ( mappingExists( arguments.alias ) ) { - currentMapping = []; - currentMapping[ 1 ] = instance.mappings[ arguments.alias ]; + variables.currentMapping = [ variables.mappings[ arguments.alias ] ]; return this; } throw( - message = "The mapping '#arguments.alias# has not been initialized yet.'", - detail = "Please use the map('#arguments.alias#') first to start working with a mapping", - type = "Binder.InvalidMappingStateException" + message = "The mapping '#arguments.alias#' has not been registered yet", + type = "InvalidMappingStateException" ); - - - - - - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map a constructor argument to a mapping + * + * @name The name of the constructor argument (Not used for: JAVA,WEBSERVICE) + * @ref The reference mapping id this constructor argument maps to + * @dsl The construction dsl this argument references. If used, the name value must be used. + * @value The explicit value of the constructor argument, if passed. + * @javaCast The type of javaCast() to use on the value of the argument. Only used if using dsl or ref arguments + * @required If the argument is required or not, by default we assume required DI arguments + * @type The type of the argument + */ + Binder function initArg( + name, + ref, + dsl = "", + value, + javaCast, + required required=true, + type = "any" + ){ + for ( var mapping in variables.currentMapping ) { mapping.addDIConstructorArgument( argumentCollection = arguments ); } return this; - - - - - - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map setter injection + * + * @name The name of the setter to inject + * @ref The reference mapping id this setter maps to + * @dsl The construction dsl this setter references. If used, the name value must be used. + * @value The explicit value of the setter, if passed. + * @javaCast The type of javaCast() to use on the value of the value. Only used if using dsl or ref arguments + * @argName The name of the argument to use, if not passed, we default it to the setter name + */ + Binder function setter( + required name, + ref, + dsl, + value, + javaCast, + argName + ){ + for ( var mapping in variables.currentMapping ) { mapping.addDISetter( argumentCollection = arguments ); } return this; - - - - - - - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Map property injection + * + * @name The name of the property to inject + * @ref The reference mapping id this property maps to + * @dsl The construction dsl this property references. If used, the name value must be used. + * @value The explicit value of the property, if passed. + * @javaCast The type of javaCast() to use on the value of the value. Only used if using dsl or ref arguments + * @scope The scope in the CFC to inject the property to. By default it will inject it to the variables scope + * @required If the property is required or not, by default we assume required DI + * @type The type of the property + */ + Binder function property( + required name, + ref, + dsl, + value, + javaCast, + scope = "variables", + required required=true, + type = "any" + ){ + for ( var mapping in variables.currentMapping ) { mapping.addDIProperty( argumentCollection = arguments ); } return this; - - - - - - - + } + + /** + * The methods to execute once DI completes on the mapping + * + * @methods A list or an array of methods to execute once the mapping is created, inited and DI has happened. + */ + Binder function onDIComplete( required methods ){ // inflate list if ( isSimpleValue( arguments.methods ) ) { arguments.methods = listToArray( arguments.methods ); } // store list - for ( var mapping in getCurrentMapping() ) { + for ( var mapping in variables.currentMapping ) { mapping.setOnDIComplete( arguments.methods ); } return this; - - - - - - - - - for ( var thisMapping in getCurrentMapping() ) { + } + + /** + * Add a new provider method mapping + * + * @method The provided method to override or inject as a provider + * @mapping The mapping to provide via the selected method + */ + Binder function providerMethod( required method, required mapping ){ + for ( var thisMapping in variables.currentMapping ) { thisMapping.addProviderMethod( argumentCollection = arguments ); } return this; - - - - - - - + } + + /** + * Map an object into a specific persistence scope + * + * @scope The scope to map to, use a valid WireBox Scope by using binder.SCOPES.* or a custom scope + * + * @throws InvalidScopeMapping - Trying to register into an invalid scope + */ + Binder function into( required scope ){ // check if invalid scope if ( NOT this.SCOPES.isValidScope( arguments.scope ) AND NOT structKeyExists( - instance.customScopes, + variables.customScopes, arguments.scope ) ) { throw( message = "Invalid WireBox Scope: '#arguments.scope#'", - detail = "Please make sure you are using a valid scope, valid scopes are: #arrayToList( this.SCOPES.getValidScopes() )# AND custom scopes: #structKeyList( instance.customScopes )#", + detail = "Please make sure you are using a valid scope, valid scopes are: #arrayToList( this.SCOPES.getValidScopes() )# AND custom scopes: #structKeyList( variables.customScopes )#", type = "Binder.InvalidScopeMapping" ); } - for ( var mapping in getCurrentMapping() ) { + for ( var mapping in variables.currentMapping ) { mapping.setScope( arguments.scope ); } return this; - - - - - - + } + + /** + * Map as a singleton, shortcut to using 'in( this.SCOPES.SINGLETON )' + */ + Binder function asSingleton(){ return this.into( this.SCOPES.SINGLETON ); - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Tells persistence scopes to build, wire, and do onDIComplete() on objects in an isolated lock. This will disallow circular references unless object providers are used. By default all object's constructors are the only thread safe areas + */ + Binder function threadSafe(){ + for ( var mapping in variables.currentMapping ) { mapping.setThreadSafe( true ); } return this; - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * This is the default wiring of objects that allow circular dependencies. By default all object's constructors are the only thread safe areas + */ + Binder function notThreadSafe(){ + for ( var mapping in variables.currentMapping ) { mapping.setThreadSafe( false ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * This is a closure that will be able to influence the creation of the instance + * + * @influenceClosure The closure to use for influencing constructions + */ + Binder function withInfluence( required influenceClosure ){ + for ( var mapping in variables.currentMapping ) { mapping.setInfluenceClosure( arguments.influenceClosure ); } return this; - - - - - - - - for ( var mapping in getCurrentMapping() ) { + } + + /** + * Adds a structure of metadata to be stored with the mapping for later retrieval by the developer in events, manually or builders + * + * @data The data structure to store with the mapping + */ + Binder function extraAttributes( required struct data ){ + for ( var mapping in variables.currentMapping ) { mapping.setExtraAttributes( arguments.data ); } return this; - - - - - - - + } + + /** + * Adds one, a list or an array of UDF templates to mixin to a CFC + * + * @mixins The udf include location(s) to mixin at runtime + */ + Binder function mixins( required mixins ){ if ( isSimpleValue( arguments.mixins ) ) { arguments.mixins = listToArray( arguments.mixins ); } - for ( var mapping in getCurrentMapping() ) { + for ( var mapping in variables.currentMapping ) { mapping.setMixins( arguments.mixins ); } return this; - - - - - - - - - - - - - - + } + + /** + * Link a parent injector to this configuration binder + * + * @injector A parent injector to link + */ + Binder function parentInjector( required injector ){ + variables.parentInjector = arguments.injector; + return this; + } + + /** + * Configure the stop recursion classes + * + * @classes A list or array of classes to use so the injector can stop when looking for dependencies in inheritance chains + */ + Binder function stopRecursions( required classes ){ // inflate incoming locations if ( isSimpleValue( arguments.classes ) ) { arguments.classes = listToArray( arguments.classes ); } // Save them - instance.stopRecursions = arguments.classes; + variables.stopRecursions = arguments.classes; return this; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var x = 1; - + } + + /** + * Use to define injector scope registration + * + * @enabled Enable registration or not (defaults=false) Boolean + * @scope The scope to register on, defaults to application scope + * @key The key to use in the scope, defaults to wireBox + */ + Binder function scopeRegistration( + boolean enabled = variables.DEFAULTS.scopeRegistration.enabled, + scope = variables.DEFAULTS.scopeRegistration.scope, + key = variables.DEFAULTS.scopeRegistration.key + ){ + structAppend( variables.scopeRegistration, arguments, true ); + return this; + } + + /** + * Register one or more package scan locations for CFC lookups + * + * @locations A list or array of locations to add to package scanning.e.g.: ['coldbox','com.myapp','transfer'] + */ + Binder function scanLocations( required locations ){ // inflate incoming locations if ( isSimpleValue( arguments.locations ) ) { arguments.locations = listToArray( arguments.locations ); } - - // Prepare Locations - for ( x = 1; x lte arrayLen( arguments.locations ); x++ ) { - // Validate it is not registered already - if ( - NOT structKeyExists( instance.scanLocations, arguments.locations[ x ] ) AND len( - arguments.locations[ x ] - ) - ) { + // Process locations + arguments.locations + .filter( function( thisLocation ){ + return ( + !structKeyExists( variables.scanLocations, arguments.thisLocation ) + AND + len( arguments.thisLocation ) + ); + } ) + .each( function( thisLocation ){ // Process creation path & Absolute Path - instance.scanLocations[ arguments.locations[ x ] ] = expandPath( - "/" & replace( arguments.locations[ x ], ".", "/", "all" ) & "/" + variables.scanLocations[ thisLocation ] = expandPath( + "/" & replace( thisLocation, ".", "/", "all" ) & "/" ); - } - } + } ); return this; - - - - - - - - var x = 1; - + } + + /** + * Try to remove all the scan locations passed in + * + * @locations Locations to remove from the lookup. A list or array of locations + */ + function removeScanLocations( required locations ){ // inflate incoming locations if ( isSimpleValue( arguments.locations ) ) { arguments.locations = listToArray( arguments.locations ); } // Loop and remove - for ( x = 1; x lte arrayLen( arguments.locations ); x++ ) { - structDelete( instance.scanLocations, arguments.locations[ x ] ); - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - for ( var mapping in getCurrentMapping() ) { + arguments.locations.each( function( thisLocation ){ + structDelete( variables.scanLocations, thisLocation ); + } ); + } + + /** + * Configurate CacheBox operations + * + * @configFile The configuration file to use for loading CacheBox if creating it + * @cacheFactory The CacheBox cache factory instance to link WireBox to + * @enabled Enable or Disable CacheBox Integration, if you call this method then enabled is set to true as most likely you are trying to enable it + * @classNamespace The package namespace to use for creating or connecting to CacheBox. Defaults to: coldbox.system.cache + */ + Binder function cachebox( + configFile = "", + cacheFactory = "", + boolean enabled = true, + classNamespace = variables.DEFAULTS.cachebox.classNamespace + ){ + structAppend( variables.cacheBox, arguments, true ); + return this; + } + + /** + * Alias to get cachebox configuration + * + * @deprecated Remove by v7: use getCacheBox() instead + */ + struct function getCacheBoxConfig(){ + return variables.cachebox; + } + + /** + * Map an object into CacheBox + * + * @key You can override the key it will use for storing in cache. By default it uses the name of the mapping + * @timeout Object Timeout, else defaults to whatever the default is in the choosen cache + * @lastAccessTimeout Object Timeout, else defaults to whatever the default is in the choosen cache + * @provider Uses the 'default' cache provider by default + */ + Binder function inCacheBox( + key = "", + timeout = "", + lastAccessTimeout = "", + provider = "default" + ){ + for ( var mapping in variables.currentMapping ) { // if key not passed, build a mapping name if ( NOT len( arguments.key ) ) { if ( len( mapping.getPath() ) ) { @@ -1431,196 +1116,102 @@ This binder will hold all your object mappings, injector settings and more. arguments.key = "wirebox-#mapping.getName()#"; } } - // store the mapping info. mapping.setScope( this.SCOPES.CACHEBOX ).setCacheProperties( argumentCollection = arguments ); } return this; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + } + + /** + * Register a new custom dsl namespace + * + * @namespace The namespace you would like to register + * @path The instantiation path to the CFC that implements this scope, it must have an init() method and implement: coldbox.system.ioc.dsl.IDSLBuilder + */ + Binder function mapDSL( required namespace, required path ){ + variables.customDSL[ arguments.namespace ] = arguments.path; + return this; + } + + /** + * Register a new WireBox custom scope + * + * @annotation The unique scope name to register. This translates to an annotation value on CFCs + * @path The path to the CFC that implements this scope, it must have an init() method and implement: coldbox.system.ioc.scopes.IScope + */ + Binder function mapScope( required annotation, required path ){ + variables.customScopes[ arguments.annotation ] = arguments.path; + return this; + } + + /** + * Set the logBox Configuration to use + * + * @config The logbox configuration struct + */ + Binder function logBoxConfig( required config ){ + variables.logBoxConfig = arguments.config; + return this; + } + + /** + * Load a data configuration CFCs data DSL into this configuration + * + * @rawDSL The data configuration DSL structure to load, else look internally + */ + Binder function loadDataDSL( struct rawDSL ){ var wireBoxDSL = variables.wirebox; - var key = ""; // Coldbox Context Attached - if ( isObject( instance.coldbox ) ) { + if ( isObject( variables.coldbox ) ) { // create scan location for model convention as the first one. - scanLocations( instance.coldbox.getSetting( "ModelsInvocationPath" ) ); + this.scanLocations( variables.coldbox.getSetting( "ModelsInvocationPath" ) ); } // Incoming raw DSL or use locally? - if ( structKeyExists( arguments, "rawDSL" ) ) { + if ( !isNull( arguments.rawDSL ) ) { wireBoxDSL = arguments.rawDSL; } // Register LogBox Configuration if ( structKeyExists( wireBoxDSL, "logBoxConfig" ) ) { - logBoxConfig( wireBoxDSL.logBoxConfig ); + variables.logBoxConfig = wireBoxDSL.logBoxConfig; } // Register Parent Injector if ( structKeyExists( wireBoxDSL, "parentInjector" ) ) { - parentInjector( wireBoxDSL.parentInjector ); + variables.parentInjector = wireBoxDSL.parentInjector; } // Register Server Scope Registration if ( structKeyExists( wireBoxDSL, "scopeRegistration" ) ) { - scopeRegistration( argumentCollection = wireBoxDSL.scopeRegistration ); + this.scopeRegistration( argumentCollection = wireBoxDSL.scopeRegistration ); } // Register CacheBox if ( structKeyExists( wireBoxDSL, "cacheBox" ) ) { - cacheBox( argumentCollection = wireBoxDSL.cacheBox ); + this.cacheBox( argumentCollection = wireBoxDSL.cacheBox ); } // Register metadataCache if ( structKeyExists( wireBoxDSL, "metadataCache" ) ) { - setMetadataCache( wireBoxDSL.metadataCache ); + variables.metadataCache = wireBoxDSL.metadataCache; } // Register Custom DSL if ( structKeyExists( wireBoxDSL, "customDSL" ) ) { - structAppend( instance.customDSL, wireBoxDSL.customDSL, true ); + structAppend( + variables.customDSL, + wireBoxDSL.customDSL, + true + ); } // Register Custom Scopes if ( structKeyExists( wireBoxDSL, "customScopes" ) ) { structAppend( - instance.customScopes, + variables.customScopes, wireBoxDSL.customScopes, true ); @@ -1628,244 +1219,171 @@ This binder will hold all your object mappings, injector settings and more. // Append Register Scan Locations if ( structKeyExists( wireBoxDSL, "scanLocations" ) ) { - scanLocations( wireBoxDSL.scanLocations ); + this.scanLocations( wireBoxDSL.scanLocations ); } // Append Register Stop Recursions if ( structKeyExists( wireBoxDSL, "stopRecursions" ) ) { - stopRecursions( wireBoxDSL.stopRecursions ); + this.stopRecursions( wireBoxDSL.stopRecursions ); } // Register listeners if ( structKeyExists( wireBoxDSL, "listeners" ) ) { - for ( key = 1; key lte arrayLen( wireBoxDSL.listeners ); key++ ) { - listener( argumentCollection = wireBoxDSL.listeners[ key ] ); + for ( var thisListener in wireboxDSL.listeners ) { + this.listener( argumentCollection = thisListener ); } } // Register Mappings if ( structKeyExists( wireBoxDSL, "mappings" ) ) { // iterate and register - for ( key in wireboxDSL.mappings ) { + for ( var key in wireboxDSL.mappings ) { // create mapping & process its data memento map( key ); - instance.mappings[ key ].processMemento( wireBoxDSL.mappings[ key ] ); + variables.mappings[ key ].processMemento( wireBoxDSL.mappings[ key ] ); } } - - - - - - - - - - - - - - - - - - - - - + + return this; + } + + /** + * Get the mapping's memento structure + */ + struct function getMemento(){ + return variables.filter( function( k, v ){ + return ( !isCustomFunction( v ) ); + } ); + } + + /** + * Discover all eager inits in the Binder and build them. + */ + Binder function processEagerInits(){ + variables.mappings + .filter( function( key, thisMapping ){ + return ( arguments.thisMapping.isEagerInit() ); + } ) + .each( function( key, thisMapping ){ + variables.injector.getInstance( arguments.thisMapping.getName() ); + } ); + + return this; + } + + /** + * Process all registered mappings, called by injector when ready to start serving requests + * Processing means that we will iterate over all NON discovered mappings and call each + * mapping's `process()` method so all metadata can be read and registered. + */ + Binder function processMappings(){ var mappingError = ""; - instance.mappings + + variables.mappings .filter( function( key, thisMapping ){ - return ( !thisMapping.isDiscovered() ); + return ( !arguments.thisMapping.isDiscovered() ); } ) .each( function( key, thisMapping ){ try { // process the metadata - thisMapping.process( binder = this, injector = instance.injector ); - // is it eager? - if ( thisMapping.isEagerInit() ) { - instance.injector.getInstance( thisMapping.getName() ); - } + arguments.thisMapping.process( binder = this, injector = variables.injector ); } catch ( any e ) { // Remove bad mapping - instance.mappings.delete( key ); + variables.mappings.delete( key ); mappingError = e; } } ); + + // Verify exceptions if ( !isSimpleValue( mappingError ) ) { throw( object = mappingError ); } - - - - - - - - - - - - + + return this; + } + + /** + * Add a new listener configuration + * + * @class The class of the listener + * @properties The structure of properties for the listner + * @name The name of the listener + * @register If true, registers the listener right away + */ + Binder function listener( + required class, + struct properties = {}, + name = "", + boolean register = false + ){ // Name check? if ( NOT len( arguments.name ) ) { arguments.name = listLast( arguments.class, "." ); } + // add listener - arrayAppend( instance.listeners, arguments ); + arrayAppend( variables.listeners, arguments ); if ( arguments.register ) { getInjector().registerListener( arguments ); } return this; - - - - - - - - - - - - - - - - // map eagerly + } + + /** + * -------------------------------------------------- + * AOP Mapping Methods + * -------------------------------------------------- + */ + + /** + * Map a new aspect + * + * @aspect The name or aliases of the aspect + * @autoBinding Allow autobinding of this aspect or not? Defaults to true + */ + Binder function mapAspect( required aspect, boolean autoBinding = true ){ + // map the aspect map( arguments.aspect ).asEagerInit().asSingleton(); // register the aspect - for ( var mapping in getCurrentMapping() ) { + for ( var mapping in variables.currentMapping ) { mapping.setAspect( true ).setAspectAutoBinding( arguments.autoBinding ); } return this; - - - - - - - return createObject( "component", "coldbox.system.aop.Matcher" ).init(); - - - - - - - - - + } + + /** + * Create a new matcher class for usage in class or method matching + * + * @return coldbox.system.aop.Matcher + */ + function match(){ + return new coldbox.system.aop.Matcher(); + } + + /** + * Bind a aspects to classes and methods + * + * @classes The class matcher that will be affected with this aspect binding + * @methods The method matcher that will be affected with this aspect binding + * @aspects The name or list of names or array of names of aspects to apply to the classes and method matchers + */ + Binder function bindAspect( + required classes, + required methods, + required aspects + ){ // cleanup aspect if ( isSimpleValue( arguments.aspects ) ) { arguments.aspects = listToArray( arguments.aspects ); } // register it - arrayAppend( instance.aspectBindings, arguments ); + arrayAppend( variables.aspectBindings, arguments ); return this; - - - - - - - - - - + } + +} diff --git a/system/ioc/config/Mapping.cfc b/system/ioc/config/Mapping.cfc index f80ab30e5..4411d2cf1 100644 --- a/system/ioc/config/Mapping.cfc +++ b/system/ioc/config/Mapping.cfc @@ -25,18 +25,18 @@ component accessors="true"{ property name="dsl"; property name="cache" type="struct"; property name="DIConstructorArguments"; - property name="DIProperties" type="array"; - property name="DISetters" type="array"; + property name="DIProperties" type="array"; + property name="DISetters" type="array"; property name="DIMethodArguments" type="array"; - property name="onDIComplete" type="array"; - property name="discovered" type="boolean"; - property name="objectMetadata" type="struct"; - property name="providerMethods" type="array"; - property name="aspect" type="boolean"; + property name="onDIComplete" type="array"; + property name="discovered" type="boolean"; + property name="objectMetadata" type="struct"; + property name="providerMethods" type="array"; + property name="aspect" type="boolean"; property name="aspectAutoBinding" type="boolean"; property name="virtualInheritance"; property name="extraAttributes" type="struct"; - property name="mixins" type="array"; + property name="mixins" type="array"; property name="threadSafe"; property name="influenceClosure"; @@ -209,28 +209,28 @@ component accessors="true"{ * Flag describing if you are using autowire or not as Boolean */ boolean function isAutoWire(){ - return variables.autowire; + return ( isBoolean( variables.autowire ) ? variables.autowire : false ); } /** * Flag describing if this mapping is an AOP aspect or not */ boolean function isAspect(){ - return variables.aspect; + return ( isBoolean( variables.aspect ) ? variables.aspect : false ); } /** * Is this mapping an auto aspect binding */ boolean function isAspectAutoBinding(){ - return variables.aspectAutoBinding; + return ( isBoolean( variables.aspectAutoBinding ) ? variables.aspectAutoBinding : false ); } /** * Using auto init or not */ boolean function isAutoInit(){ - return variables.autoInit; + return ( isBoolean( variables.autoInit ) ? variables.autoInit : false ); } /** @@ -430,7 +430,7 @@ component accessors="true"{ * Is this mapping eager initialized or not as Boolean */ boolean function isEagerInit(){ - return variables.eagerInit; + return ( isBoolean( variables.eagerInit ) ? variables.eagerInit : false ); } /** diff --git a/tests/specs/ioc/BuilderTest.cfc b/tests/specs/ioc/BuilderTest.cfc index ec12d1aee..dc279bfad 100755 --- a/tests/specs/ioc/BuilderTest.cfc +++ b/tests/specs/ioc/BuilderTest.cfc @@ -225,7 +225,8 @@ function testregisterCustomBuilders(){ var customDSL = { coolLuis : "coldbox.tests.specs.ioc.dsl.MyTestingDSL" }; - var mockBinder = createMock( "coldbox.system.ioc.config.Binder" ).$( "getCustomDSL", customDSL ); + var mockBinder = createMock( "coldbox.system.ioc.config.Binder" ).setCustomDSL( customDSL ); + mockInjector.setBinder( mockBinder ); builder.registerCustomBuilders(); @@ -239,7 +240,7 @@ dsl : "coolLuis:woopee" }; var customDSL = { coolLuis : "coldbox.tests.specs.ioc.dsl.MyTestingDSL" }; - var mockBinder = createMock( "coldbox.system.ioc.config.Binder" ).$( "getCustomDSL", customDSL ); + var mockBinder = createMock( "coldbox.system.ioc.config.Binder" ).setCustomDSL( customDSL ); mockInjector.setBinder( mockBinder ); builder.registerCustomBuilders(); @@ -327,7 +328,7 @@ dsl : "wirebox:properties" }; props = { prop1 : "hello", name : "luis" }; - mockBinder = createMock( "coldbox.system.ioc.config.Binder" ).$( "getProperties", props ); + mockBinder = createMock( "coldbox.system.ioc.config.Binder" ).setProperties( props ); mockInjector.setBinder( mockBinder ); p = builder.getWireBoxDSL( data ); assertEquals( props, p ); diff --git a/tests/specs/ioc/InjectorTest.cfc b/tests/specs/ioc/InjectorTest.cfc index 056a937d2..77c04bfd1 100755 --- a/tests/specs/ioc/InjectorTest.cfc +++ b/tests/specs/ioc/InjectorTest.cfc @@ -86,8 +86,8 @@ function testRegisterListeners(){ makePublic( injector, "registerListeners" ); - // Mocking - listeners = [ + // Mock listeners + var listeners = [ { class : "coldbox.tests.specs.ioc.config.listeners.MyListener", name : "myDude", @@ -99,8 +99,10 @@ properties : {} } ]; - binder = injector.getBinder(); - prepareMock( binder ).$( "getListeners", listeners ); + injector + .getBinder() + .setListeners( listeners ); + prepareMock( injector.getEventManager() ).$( "register" ); injector.registerListeners(); @@ -115,7 +117,10 @@ properties : {} } ]; - prepareMock( binder ).$( "getListeners", listeners ); + injector + .getBinder() + .setListeners( listeners ); + try { injector.registerListeners(); } catch ( "Injector.ListenerCreationException" e ) { @@ -126,16 +131,19 @@ function testdoScopeRegistration(){ makePublic( injector, "doScopeRegistration" ); - scopeReg = { - key : "wirebox", + + injector.getBinder().scopeRegistration( + key : "mockWireBox", scope : "application" - }; + ); - binder = injector.getBinder(); - prepareMock( binder ).$( "getScopeRegistration", scopeReg ); - injector.doScopeRegistration(); - structKeyExists( application, "wirebox" ); - structDelete( application, "wirebox" ); + try{ + structDelete( application, "mockWireBox" ); + injector.doScopeRegistration(); + structKeyExists( application, "mockWireBox" ); + } finally{ + structDelete( application, "mockWireBox" ); + } } function testConfigureCacheBox(){ @@ -205,17 +213,15 @@ } function testRemoveFromScope(){ - scopeReg = { + injector.getBinder().scopeRegistration( enabled : true, - key : "wirebox", + key : "mockWireBox", scope : "application" - }; - binder = injector.getBinder(); - prepareMock( binder ).$( "getScopeRegistration", scopeReg ); - application.wirebox = createStub(); + ); + application.mockWireBox = createStub(); injector.removeFromScope(); - assertFalse( structKeyExists( application, "wirebox" ) ); + assertFalse( structKeyExists( application, "mockWireBox" ) ); } function testAutowireCallsGetInheritedMetaDataForTargetID(){ diff --git a/tests/specs/ioc/aop/MixerTest.cfc b/tests/specs/ioc/aop/MixerTest.cfc index 85a2625ca..95b572e23 100755 --- a/tests/specs/ioc/aop/MixerTest.cfc +++ b/tests/specs/ioc/aop/MixerTest.cfc @@ -100,14 +100,14 @@ component extends="tests.resources.BaseIntegrationTest" { } function testBuildClassMatchDictionary(){ - aspects = [ + var aspects = [ { classes : createMock( "coldbox.system.aop.Matcher" ).init().$( "matchClass", true ), methods : createMock( "coldbox.system.aop.Matcher" ).init(), aspects : "Transaction" } ]; - mockBinder.$( "getAspectBindings", aspects ); + mockBinder.setAspectBindings( aspects ); mockMapping = createMock( "coldbox.system.ioc.config.Mapping" ).$( "getName", "unitTest" ); makePublic( mixer, "buildClassMatchDictionary" ); diff --git a/tests/specs/ioc/config/BinderTest.cfc b/tests/specs/ioc/config/BinderTest.cfc index 9991925fc..d1d219b8c 100755 --- a/tests/specs/ioc/config/BinderTest.cfc +++ b/tests/specs/ioc/config/BinderTest.cfc @@ -282,7 +282,7 @@ function testWith(){ try { config.with( "Bogus" ); - } catch ( "Binder.InvalidMappingStateException" e ) { + } catch ( "InvalidMappingStateException" e ) { } catch ( Any e ) { fail( e ); } diff --git a/tests/tmp/scribble.cfm b/tests/tmp/scribble.cfm index 71dbf6bec..c68820f40 100755 --- a/tests/tmp/scribble.cfm +++ b/tests/tmp/scribble.cfm @@ -1,6 +1,10 @@ - - f = createObject("java","java.lang.ThreadLocal").init(); - f.set("test"); + - writeDump( f.get() ); - \ No newline at end of file + \ No newline at end of file From 88c3fcc78f3173862a9f47457fa35dd77ecf5416 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Wed, 16 Dec 2020 18:01:09 -0600 Subject: [PATCH 71/78] COLDBOX-956 #resolve Whoops loads multiple files into the DOM for the templates in the stacktrace causing major slowdowns --- server-adobe@2018.json | 4 ++-- system/exceptions/Whoops.cfm | 43 ++++++++++++++++++++++++---------- system/exceptions/js/whoops.js | 31 ++++++++++++++++++++---- 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/server-adobe@2018.json b/server-adobe@2018.json index e26ce2b5e..92aaade9f 100644 --- a/server-adobe@2018.json +++ b/server-adobe@2018.json @@ -13,8 +13,8 @@ }, "rewrites":{ "enable":true - }, - "aliases":{ + }, + "aliases":{ "/coldbox":"./" } }, diff --git a/system/exceptions/Whoops.cfm b/system/exceptions/Whoops.cfm index 51282c749..14eaa7906 100644 --- a/system/exceptions/Whoops.cfm +++ b/system/exceptions/Whoops.cfm @@ -141,7 +141,7 @@ SyntaxHighlighter.defaults[ 'gutter' ] = true; SyntaxHighlighter.defaults[ 'smart-tabs' ] = false; SyntaxHighlighter.defaults[ 'tab-size' ] = 4; - SyntaxHighlighter.all(); + //SyntaxHighlighter.all(); @@ -401,25 +401,43 @@
- + - - - - - + + + + + + + + + + + + + + + @@ -430,6 +448,7 @@ diff --git a/system/exceptions/js/whoops.js b/system/exceptions/js/whoops.js index a5e065bec..00691ce12 100644 --- a/system/exceptions/js/whoops.js +++ b/system/exceptions/js/whoops.js @@ -29,16 +29,35 @@ function toggleActiveClasses( id ) { } function changeCodePanel( id ) { + // activate the stackframe toggleActiveClasses( id ); - var code = document.getElementById( id + "-code" ); - var highlightLine = code.getAttribute( "data-highlight-line" ); + // get access to pre tag so we can populate and highlight it + var code = document.getElementById( id + "-code" ); + // get access to pre > script tag so we can populate it with template code when needed + var codeScript = document.getElementById( id + "-script" ); + // Get the template id for inejcting the source + var templateSource = document.getElementById( "stackframe-" + code.getAttribute( "data-template-id" ) ); + // Only assign sources if codeContainer exists, else ignore. if( codeContainer == null ){ return; } + // Activate highlighting only if template not rendered already + if( code.getAttribute( "data-template-rendered" ) == "false" ){ + // Inject template source into highlighter source + codeScript.innerHTML = templateSource.innerHTML; + codeScript.setAttribute( "type", "syntaxhighlighter" ); + // Activate it + SyntaxHighlighter.highlight( {}, id + "-script" ); + // Mark as rendered + code.setAttribute( "data-template-rendered", "true" ); + } + + // Inject the source to the code container for visualizations codeContainer.innerHTML = code.innerHTML; - scrollToLine( highlightLine ); + // Scroll to the highlighted line + scrollToLine( code.getAttribute( "data-highlight-line" ) ); } function reinitframework( usingPassword ){ @@ -103,6 +122,8 @@ Array.from( document.querySelectorAll( ".stacktrace" ) ) } ); document.addEventListener( "DOMContentLoaded", function() { - var initialStackTrace = document.querySelector( ".stacktrace__list .stacktrace" ); - setTimeout(function(){ changeCodePanel( initialStackTrace.id ); }, 500); + var initialStackTrace = document.querySelector( ".stacktrace__list .stacktrace" ); + setTimeout( function(){ + changeCodePanel( initialStackTrace.id ); + }, 500 ); } ); From 28b25bf85270e1c71b8983a28ecb835b46755ebf Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 17 Dec 2020 10:15:46 -0600 Subject: [PATCH 72/78] COLDBOX-939 #resolve Invalid event handler detection was overriding some event handler beans, in this commit it was from when an action was missing or invalid --- system/web/services/HandlerService.cfc | 14 ++++++++------ test-harness/models/ControllerDecorator.cfc | 10 ++++++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/system/web/services/HandlerService.cfc b/system/web/services/HandlerService.cfc index 60a5c91fd..01f7a3730 100644 --- a/system/web/services/HandlerService.cfc +++ b/system/web/services/HandlerService.cfc @@ -136,6 +136,8 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { * * @ehBean The event handler bean representation * @requestContext The request context object + * + * @return The event handler object represented by the ehBean */ function getHandler( required ehBean, required requestContext ){ var oRequestContext = arguments.requestContext; @@ -164,12 +166,12 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { return oEventHandler; } - // Invalid Event procedures - invalidEvent( arguments.ehBean.getFullEvent(), arguments.ehBean ); + // The handler exists but the action requested does not, let's go into invalid execution mode + var targetInvalidEvent = invalidEvent( arguments.ehBean.getFullEvent(), arguments.ehBean ); - // If we get here, then the invalid event kicked in and exists, else an exception is thrown + // If we get here, then the invalid event kicked in and exists, else an exception is thrown above // Go retrieve the handler that will handle the invalid event so it can execute. - return getHandler( getHandlerBean( arguments.ehBean.getFullEvent() ), oRequestContext ); + return getHandler( getHandlerBean( targetInvalidEvent ), oRequestContext ); } // method check finalized. @@ -478,9 +480,9 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { // Override Event With On invalid handler event return variables.invalidEventHandler; - } + } // end invalidEventHandler found - // If we got here, we have an invalid event and no override, throw a 404 header + // If we got here, we have an invalid event and no override, throw a 404 ERROR controller .getRequestService() .getContext() diff --git a/test-harness/models/ControllerDecorator.cfc b/test-harness/models/ControllerDecorator.cfc index b58776523..f043ee674 100644 --- a/test-harness/models/ControllerDecorator.cfc +++ b/test-harness/models/ControllerDecorator.cfc @@ -3,6 +3,8 @@ component extends="coldbox.system.web.ControllerDecorator" { this.decorator = "true"; function configure(){ + + variables.logger = getLogBox().getLogger( this ); } /** @@ -49,8 +51,12 @@ component extends="coldbox.system.web.ControllerDecorator" { throw( message = "Relocating via relocate: #arguments.toString()#", type = "TestController.relocate" ); } - function runEvent(){ - getLogBox().getLogger( this ).info( " Called decorator runEvent(#arguments.toString()#)" ); + function runEvent( event="" ){ + // useful debugging to pinpoint execution exceptions + logger.info( + "=>Called decorator runEvent(#arguments.toString()#)", + !len( arguments.event ) ? callStackGet() : "" + ); return getController().runEvent( argumentCollection = arguments ); } From fd1fe496d3e9dac5de6c59de9f95e9e9e94760e2 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 17 Dec 2020 11:07:11 -0600 Subject: [PATCH 73/78] COLDBOX-958 #resolve Store the test case metadata on `variables.metadata` so it can be reused by any helper within test operations COLDBOX-957 #resolve New autowire annotation or `variables.autowire` on integration tests so coldbox will autowire the test with dependencies via WireBox COLDBOX-959 #resolve New ColdBox CustomMatchers object found at coldbox.system.testing.CustomMatchers which is loaded on all tests --- system/testing/BaseTestCase.cfc | 75 +++++++++---- system/testing/CustomMatchers.cfc | 106 ++++++++++++++++++ tests/specs/integration/MainTests.cfc | 11 ++ .../integration/RestfulHandlersTests.cfc | 3 +- 4 files changed, 174 insertions(+), 21 deletions(-) create mode 100644 system/testing/CustomMatchers.cfc diff --git a/system/testing/BaseTestCase.cfc b/system/testing/BaseTestCase.cfc index 84a157926..28f9dc76e 100755 --- a/system/testing/BaseTestCase.cfc +++ b/system/testing/BaseTestCase.cfc @@ -22,6 +22,15 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { * The application key for the ColdBox applicatin this test links to */ property name="coldboxAppKey"; + /** + * If in integration mode, you can tag for your tests to be automatically autowired with dependencies + * by WireBox + */ + property name="autowire" type="boolean" default="false"; + /** + * The test case metadata + */ + property name="metadata" type="struct"; // Public Switch Properties // TODO: Remove by ColdBox 4.2+ and move to variables scope. @@ -33,6 +42,8 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { variables.configMapping = ""; variables.controller = ""; variables.coldboxAppKey = "cbController"; + variables.autowire = false; + variables.metadata = {}; /********************************************* LIFE-CYCLE METHODS *********************************************/ @@ -41,26 +52,30 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { * @return BaseTestCase */ function metadataInspection(){ - var md = new coldbox.system.core.util.Util().getInheritedMetadata( this ); + variables.metadata = new coldbox.system.core.util.Util().getInheritedMetadata( this ); // Inspect for appMapping annotation - if ( structKeyExists( md, "appMapping" ) ) { - variables.appMapping = md.appMapping; + if ( structKeyExists( variables.metadata, "appMapping" ) ) { + variables.appMapping = variables.metadata.appMapping; } // Configuration File mapping - if ( structKeyExists( md, "configMapping" ) ) { - variables.configMapping = md.configMapping; + if ( structKeyExists( variables.metadata, "configMapping" ) ) { + variables.configMapping = variables.metadata.configMapping; } // ColdBox App Key - if ( structKeyExists( md, "coldboxAppKey" ) ) { - variables.coldboxAppKey = md.coldboxAppKey; + if ( structKeyExists( variables.metadata, "coldboxAppKey" ) ) { + variables.coldboxAppKey = variables.metadata.coldboxAppKey; } // Load coldBox annotation - if ( structKeyExists( md, "loadColdbox" ) ) { - this.loadColdbox = md.loadColdbox; + if ( structKeyExists( variables.metadata, "loadColdbox" ) ) { + this.loadColdbox = variables.metadata.loadColdbox; } // unLoad coldBox annotation - if ( structKeyExists( md, "unLoadColdbox" ) ) { - this.unLoadColdbox = md.unLoadColdbox; + if ( structKeyExists( variables.metadata, "unLoadColdbox" ) ) { + this.unLoadColdbox = variables.metadata.unLoadColdbox; + } + // autowire + if ( structKeyExists( variables.metadata, "autowire" ) ) { + this.autowire = variables.metadata.autowire; } return this; } @@ -114,7 +129,17 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { variables.controller.getModuleService().loadMappings(); // Auto registration of test as interceptor variables.controller.getInterceptorService().registerInterceptor( interceptorObject = this ); + // Do we need to autowire this test? + if( variables.autowire ){ + variables.controller.getWireBox().autowire( + target : this, + targetId : variables.metadata.path + ); + } } + + // Let's add Custom Matchers + addMatchers( "coldbox.system.testing.CustomMatchers" ); } /** @@ -130,7 +155,7 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { variables.controller = application[ getColdBoxAppKey() ]; } // remove context + reset headers - getController().getRequestService().removeContext(); + variables.controller.getRequestService().removeContext(); getPageContextResponse().reset(); request._lastInvalidEvent = ""; } @@ -141,7 +166,7 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { */ function afterTests(){ if ( this.unLoadColdbox ) { - structDelete( application, getColdboxAppKey() ); + shutdownColdBox(); } } @@ -166,13 +191,9 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { } /** - * Reset the persistence of the unit test coldbox app, basically removes the controller from application scope - * - * @orm Reload ORM or not - * - * @return BaseTestCase + * Gracefully shutdown ColdBox */ - function reset( boolean orm = false ){ + function shutdownColdBox(){ // Graceful shutdown if ( structKeyExists( application, getColdboxAppKey() ) ) { application[ getColdboxAppKey() ].getLoaderService().processShutdown(); @@ -181,6 +202,19 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { // Wipe app scopes structDelete( application, getColdboxAppKey() ); structDelete( application, "wirebox" ); + } + + /** + * Reset the persistence of the unit test coldbox app, basically removes the controller from application scope + * + * @orm Reload ORM or not + * @wipeRequest Wipe the request scope + * + * @return BaseTestCase + */ + function reset( boolean orm = false, boolean wipeRequest = true ){ + // Shutdown gracefully ColdBox + shutdownColdBox(); // Lucee Cleanups if ( server.keyExists( "lucee" ) ) { @@ -193,7 +227,7 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { } // Wipe out request scope. - if ( !structIsEmpty( request ) ) { + if ( arguments.wipeRequest && !structIsEmpty( request ) ) { lock type="exclusive" scope="request" timeout=10 { if ( !structIsEmpty( request ) ) { structClear( request ); @@ -786,6 +820,7 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { /** * Get an interceptor reference + * * @interceptorName The name of the interceptor to retrieve * * @return Interceptor diff --git a/system/testing/CustomMatchers.cfc b/system/testing/CustomMatchers.cfc new file mode 100644 index 000000000..2ecf29ce9 --- /dev/null +++ b/system/testing/CustomMatchers.cfc @@ -0,0 +1,106 @@ +/** + * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * www.ortussolutions.com + * --- + * All the custom matchers ColdBox registers within TestBox for easier testing! + */ +component{ + + /** + * Checks if the ColdBox response obejct has a matched status code + *
+	 * expect( event.getResponse() ).toHaveStatus( statusCode );
+	 * 
+ */ + function toHaveStatus( expectation, args = {} ){ + // handle both positional and named arguments + param args.statusCode = ""; + if ( structKeyExists( args, 1 ) ) { + args.statusCode = args[ 1 ]; + } + param args.message = ""; + if ( structKeyExists( args, 2 ) ) { + args.message = args[ 2 ]; + } + if ( !len( args.statusCode ) ) { + expectation.message = "No status code provided."; + return false; + } + var statusCode = expectation.actual.getStatusCode(); + if ( statusCode != args.statusCode ) { + expectation.message = "#args.message#. Received incorrect status code. Expected [#args.statusCode#]. Received [#statusCode#]."; + debug( expectation.actual.getMemento() ); + return false; + } + return true; + } + + /** + * Expectation for testing again cbValidation invalid data fields returned in a Restful response + * by looking into the response object + * + *
+	 * expect( event.getResponse() ).toHaveInvalidData( "username", "is required" )
+	 * expect( event.getResponse() ).toHaveInvalidData( "role", "is required" )
+	 * expect( event.getResponse() ).toHaveInvalidData( "permission", "is not unique" )
+	 * expect( event.getResponse() ).toHaveInvalidData( "status", "does not match" )
+	 * 
+ */ + function toHaveInvalidData( expectation, args = {} ){ + param args.field = ""; + if ( structKeyExists( args, 1 ) ) { + args.field = args[ 1 ]; + } + param args.error = ""; + if ( structKeyExists( args, 2 ) ) { + args.error = args[ 2 ]; + } + param args.message = ""; + if ( structKeyExists( args, 3 ) ) { + args.message = args[ 3 ]; + } + + // If !400 then there is no invalid data + if ( expectation.actual.getStatusCode() != 400 ) { + expectation.message = "#args.message#. Received incorrect status code. Expected [400]. Received [#expectation.actual.getStatusCode()#]."; + debug( expectation.actual.getMemento() ); + return false; + } + // If no field passed, we just check that invalid data was found + if ( !len( args.field ) ) { + if ( expectation.actual.getData().isEmpty() ) { + expectation.message = "#args.message#. Received incorrect status code. Expected [400]. Received [#expectation.actual.getStatusCode()#]."; + debug( expectation.actual.getMemento() ); + return false; + } + return true; + } + // We have a field to check and it has data + if ( + !structKeyExists( + expectation.actual.getData(), + args.field + ) || expectation.actual.getData()[ args.field ].isEmpty() + ) { + expectation.message = "#args.message#. The requested field [#args.field#] does not have any invalid data."; + debug( expectation.actual.getMemento() ); + return false; + } + // Do we have any error messages to check? + if ( len( args.error ) ) { + try { + expect( + expectation.actual.getData()[ args.field ] + .map( ( item ) => item.message ) + .toList() + ).toInclude( args.error ); + } catch ( any e ) { + debug( expectation.actual.getMemento() ); + rethrow; + } + } + + // We checked and it's all good! + return true; + } +} \ No newline at end of file diff --git a/tests/specs/integration/MainTests.cfc b/tests/specs/integration/MainTests.cfc index a90cdefe4..d3a471311 100755 --- a/tests/specs/integration/MainTests.cfc +++ b/tests/specs/integration/MainTests.cfc @@ -1,7 +1,10 @@ component extends="tests.resources.BaseIntegrationTest" + autowire { + property name="logger" inject="logbox:logger:{this}"; + /*********************************** BDD SUITES ***********************************/ function run(){ @@ -13,6 +16,14 @@ structDelete( request, "_lastInvalidEvent" ); } ); + it( "can handle autowire annotations for tests", function(){ + expect( variables.logger ).toBeComponent(); + }); + + it( "reads metadata for the test and stores it", function(){ + expect( variables.metadata ).notToBeEmpty(); + }); + it( "can handle invalid events", function(){ var event = execute( event = "invalid:bogus.index", renderResults = true ); expect( event.getValue( "cbox_rendered_content" ) ).toInclude( "Invalid Page" ); diff --git a/tests/specs/integration/RestfulHandlersTests.cfc b/tests/specs/integration/RestfulHandlersTests.cfc index a72d2f9df..5e15ad12c 100644 --- a/tests/specs/integration/RestfulHandlersTests.cfc +++ b/tests/specs/integration/RestfulHandlersTests.cfc @@ -7,12 +7,13 @@ component extends="tests.resources.BaseIntegrationTest" { setup(); } ); - it( "can handle vanilla restful execution", function(){ + it( "can handle vanilla restful execution with custom matchers", function(){ var e = this.get( "/restfulHandler" ); var response = e.getResponse(); expect( response.getError() ).toBeFalse( response.getMessagesString() ); expect( response.getData() ).toBe( "hello" ); + expect( response ).toHaveStatus( 200 ); } ); it( "can handle handler return results", function(){ From 16881d89bc5327a7b370d903690ff9b96592c662 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 17 Dec 2020 11:20:20 -0600 Subject: [PATCH 74/78] ACF 2016 compat! --- system/testing/CustomMatchers.cfc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/testing/CustomMatchers.cfc b/system/testing/CustomMatchers.cfc index 2ecf29ce9..58976938f 100644 --- a/system/testing/CustomMatchers.cfc +++ b/system/testing/CustomMatchers.cfc @@ -91,7 +91,9 @@ component{ try { expect( expectation.actual.getData()[ args.field ] - .map( ( item ) => item.message ) + .map( function( item ){ + return item.message; + } ) .toList() ).toInclude( args.error ); } catch ( any e ) { From 2d0fb39767b4c00da9e907c14d6965883d468fed Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 17 Dec 2020 11:34:07 -0600 Subject: [PATCH 75/78] more updates for consistency on tests --- system/testing/BaseTestCase.cfc | 3 ++- tests/specs/integration/RestfulTests.cfc | 17 +++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/system/testing/BaseTestCase.cfc b/system/testing/BaseTestCase.cfc index 28f9dc76e..5d10a8d45 100755 --- a/system/testing/BaseTestCase.cfc +++ b/system/testing/BaseTestCase.cfc @@ -379,7 +379,8 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { */ function setupRequest( required event ){ // Setup the incoming event - URL[ getController().getSetting( "EventName" ) ] = arguments.event; + URL[ getController().getSetting( "EventName" ) ] = arguments.event; + FORM[ getController().getSetting( "EventName" ) ] = arguments.event; // Cleanup for invalid event handlers structDelete( request, "_lastInvalidEvent" ); // Capture the request diff --git a/tests/specs/integration/RestfulTests.cfc b/tests/specs/integration/RestfulTests.cfc index 96105ecd2..69b32973d 100644 --- a/tests/specs/integration/RestfulTests.cfc +++ b/tests/specs/integration/RestfulTests.cfc @@ -19,16 +19,13 @@ component expect( event.getValue( "cbox_rendered_content" ) ).toBe( "Yep, onInvalidHTTPMethod works!" ); } ); - var formats = [ "json" ]; - // var formats = [ "json", "xml", "pdf", "wddx", "html" ]; - it( "can do #formats.toString()# data renderings", function(){ - for ( var thisFormat in formats ) { - getRequestContext().setValue( "format", thisFormat ); - var event = execute( event = "rendering.index", renderResults = true ); - var prc = event.getCollection( private = true ); - expect( prc.cbox_renderData ).toBeStruct(); - expect( prc.cbox_renderData.contenttype ).toMatch( thisFormat ); - } + it( "can do json data renderings", function(){ + getRequestContext().setValue( "format", thisFormat ); + var event = execute( event = "rendering.index", renderResults = true ); + var prc = event.getPrivateCollection(); + + expect( prc.cbox_renderData ).toBeStruct(); + expect( prc.cbox_renderData.contenttype ).toMatch( thisFormat ); } ); it( "can redirect only for html formats with the `formatsRedirect` parameter", function(){ From bcad1ade28c8492852dbe525f16f38035988e3ed Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 17 Dec 2020 13:01:22 -0600 Subject: [PATCH 76/78] oops --- tests/specs/integration/RestfulTests.cfc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/specs/integration/RestfulTests.cfc b/tests/specs/integration/RestfulTests.cfc index 69b32973d..257005d16 100644 --- a/tests/specs/integration/RestfulTests.cfc +++ b/tests/specs/integration/RestfulTests.cfc @@ -20,12 +20,12 @@ component } ); it( "can do json data renderings", function(){ - getRequestContext().setValue( "format", thisFormat ); + getRequestContext().setValue( "format", "json" ); var event = execute( event = "rendering.index", renderResults = true ); var prc = event.getPrivateCollection(); expect( prc.cbox_renderData ).toBeStruct(); - expect( prc.cbox_renderData.contenttype ).toMatch( thisFormat ); + expect( prc.cbox_renderData.contenttype ).toMatch( "json" ); } ); it( "can redirect only for html formats with the `formatsRedirect` parameter", function(){ From 32021bcb707cefa8c6f88eaab8fd26b90e1133e7 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 17 Dec 2020 15:43:28 -0600 Subject: [PATCH 77/78] name refactorings --- .../async/performance-parallel-tests.cfm} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename tests/{tmp/test.cfm => suites/async/performance-parallel-tests.cfm} (96%) diff --git a/tests/tmp/test.cfm b/tests/suites/async/performance-parallel-tests.cfm similarity index 96% rename from tests/tmp/test.cfm rename to tests/suites/async/performance-parallel-tests.cfm index 460f5b98e..2fd2699b1 100755 --- a/tests/tmp/test.cfm +++ b/tests/suites/async/performance-parallel-tests.cfm @@ -112,7 +112,7 @@ ); writeDump( var={ - label: "State injection futures apply()", + label: "State injection futures allApply()", value : "#getTickCount() - sTime#ms" } ); @@ -156,7 +156,7 @@ ); writeDump( var={ - label: "State injections futures apply() with custom executor", + label: "State injections futures allApply() with custom executor", value : "#getTickCount() - sTime#ms" } ); executor.shutdown(); @@ -193,7 +193,7 @@ }, true ); writeDump( var={ - label: "State injection with Parallel Map", + label: "State injection with native Lucee Parallel Map", value : "#getTickCount() - sTime#ms" } ); \ No newline at end of file From 32e3f407bb4c6e4f43de072a0a0db6a54dc2d0a7 Mon Sep 17 00:00:00 2001 From: Luis Majano Date: Thu, 17 Dec 2020 15:52:27 -0600 Subject: [PATCH 78/78] readme updates --- tests/suites/async/performance-parallel-tests.cfm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/suites/async/performance-parallel-tests.cfm b/tests/suites/async/performance-parallel-tests.cfm index 2fd2699b1..956ff4b35 100755 --- a/tests/suites/async/performance-parallel-tests.cfm +++ b/tests/suites/async/performance-parallel-tests.cfm @@ -11,7 +11,7 @@ mockdata = new testbox.system.modules.mockdatacfc.models.MockData(); count = 1000; - // Traditional Approach + // Traditional Sync Approach of creation an state injection sTime = getTickCount(); data = mockData.mock( $num : count, @@ -40,7 +40,7 @@ /*****************************************************/ /*****************************************************/ /*****************************************************/ - // Using State Pattern Injection + // Using State Pattern Injection and still using sync processing sTime = getTickCount(); data = mockData.mock( @@ -78,7 +78,7 @@ /*****************************************************/ /*****************************************************/ /*****************************************************/ - // Using State Pattern Injection + Futures with default 20 thread bound executor + // Using State Pattern Injection + ColdBox Futures with a default 20 thread bound executor asyncManager = new coldbox.system.async.AsyncManager(); @@ -120,7 +120,7 @@ /*****************************************************/ /*****************************************************/ /*****************************************************/ - // Using State Pattern Injection + Futures with default 20 thread bound executor + // Using State Pattern Injection + ColdBox Futures and a Cached Unbounded Thread Pool Executor asyncManager = new coldbox.system.async.AsyncManager(); executor = asyncManager.$executors.newCachedThreadPool();
Bug Date:#dateformat(now(), "MM/DD/YYYY")# #timeformat(now(),"hh:MM:SS TT")##dateformat(now(), "mm/dd/yyyy")# #timeformat(now(),"hh:mm:ss tt")#