Skip to content

Commit 805a053

Browse files
authored
Merge pull request #25 from coldbox-modules/claude/full-code-analysis-65BUo
feat: add native BoxLang 1.8.0+ dynamic class loading support
2 parents 14f521e + f0f88eb commit 805a053

3 files changed

Lines changed: 62 additions & 8 deletions

File tree

ModuleConfig.cfc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ component {
3939
* Fired when the module is registered and activated.
4040
*/
4141
function onLoad(){
42-
// Bind Core JavaLoader
42+
// Always bind Core JavaLoader; Loader.setup() will determine the actual strategy
4343
binder.map( "jl@cbjavaloader" ).to( "#moduleMapping#.models.javaloader.JavaLoader" );
4444

45+
var isBoxLang = structKeyExists( server, "boxlang" );
4546
// Duplicating so our final change won't affect the main module settings
4647
var finalSettings = duplicate( settings );
4748

@@ -67,11 +68,13 @@ component {
6768
}
6869
}
6970

70-
// Dynamic Proxy
71-
arrayPrepend(
72-
finalSettings.loadPaths,
73-
variables.modulePath & "/models/javaloader/support/cfcdynamicproxy/lib/cfcdynamicproxy.jar"
74-
);
71+
// cfcdynamicproxy.jar is only needed for legacy CF runtimes, not BoxLang
72+
if ( !isBoxLang ) {
73+
arrayPrepend(
74+
finalSettings.loadPaths,
75+
variables.modulePath & "/models/javaloader/support/cfcdynamicproxy/lib/cfcdynamicproxy.jar"
76+
);
77+
}
7578

7679
// Load JavaLoader and class loading
7780
wirebox.getInstance( "loader@cbjavaloader" ).setup( finalSettings );

models/Loader.cfc

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ component accessors="true" singleton {
2727
* Setup class loading
2828
*/
2929
function setup( required struct moduleSettings ){
30+
// BoxLang 1.8.0+: use native class loading, skip JavaLoader entirely
31+
if ( isBoxLangNative() ) {
32+
var loadPaths = arguments.moduleSettings.loadPaths ?: [];
33+
if ( !isArray( loadPaths ) ) {
34+
loadPaths = listToArray( loadPaths );
35+
}
36+
if ( arrayLen( loadPaths ) ) {
37+
getRequestClassLoader().addPaths( loadPaths );
38+
}
39+
return;
40+
}
41+
3042
// verify we have it loaded
3143
if ( not isJavaLoaderInScope() ) {
3244
lock name="#variables.staticIDKey#" throwontimeout="true" timeout="30" type="exclusive" {
@@ -55,6 +67,9 @@ component accessors="true" singleton {
5567
* Retrieves a reference to the java class. To create a instance, you must run init() on this object
5668
*/
5769
function create( required string className ){
70+
if ( isBoxLangNative() ) {
71+
return createObject( "java", arguments.className, getRequestClassLoader() );
72+
}
5873
return getJavaLoaderFromScope().create( argumentCollection = arguments );
5974
}
6075

@@ -65,6 +80,15 @@ component accessors="true" singleton {
6580
* @filter.hint The directory filter
6681
*/
6782
function appendPaths( required string dirPath, string filter = "*.jar" ){
83+
// BoxLang 1.8.0+: use native class loading
84+
if ( isBoxLangNative() ) {
85+
var newPaths = arrayOfJars( argumentCollection = arguments );
86+
if ( arrayLen( newPaths ) ) {
87+
getRequestClassLoader().addPaths( newPaths );
88+
}
89+
return;
90+
}
91+
6892
// Convert paths to array of file locations
6993
var qFiles = arrayOfJars( argumentCollection = arguments );
7094
var iterator = qFiles.iterator();
@@ -99,6 +123,16 @@ component accessors="true" singleton {
99123
* Get all the loaded URLs
100124
*/
101125
array function getLoadedURLs(){
126+
// BoxLang 1.8.0+: get URLs directly from the request class loader
127+
if ( isBoxLangNative() ) {
128+
var loadedURLs = getRequestClassLoader().getURLs();
129+
var returnArray = arrayNew( 1 );
130+
for ( var url in loadedURLs ) {
131+
arrayAppend( returnArray, url.toString() );
132+
}
133+
return returnArray;
134+
}
135+
102136
var loadedURLs = getURLClassLoader().getURLs();
103137
var returnArray = arrayNew( 1 );
104138
var x = 1;
@@ -114,14 +148,22 @@ component accessors="true" singleton {
114148
* Returns the java.net.URLClassLoader in case you need access to it
115149
*/
116150
any function getURLClassLoader(){
151+
// BoxLang 1.8.0+: return the native request class loader
152+
if ( isBoxLangNative() ) {
153+
return getRequestClassLoader();
154+
}
117155
return getJavaLoaderFromScope().getURLClassLoader();
118156
}
119157

120158
/**
121159
* Get the Javaloader Version
122160
*/
123161
string function getVersion(){
124-
return getJavaLoaderFromScope().getVersion();
162+
if ( isJavaLoaderInScope() ) {
163+
return getJavaLoaderFromScope().getVersion();
164+
}
165+
// BoxLang native mode: JavaLoader is not in scope; return the same version string
166+
return "1.2";
125167
}
126168

127169
/**
@@ -160,4 +202,11 @@ component accessors="true" singleton {
160202
return structKeyExists( server, getstaticIDKey() );
161203
}
162204

205+
/**
206+
* Detects whether we are running on BoxLang, which always supports native dynamic class loading.
207+
*/
208+
private boolean function isBoxLangNative(){
209+
return structKeyExists( server, "boxlang" );
210+
}
211+
163212
}

test-harness/tests/specs/LoaderTest.cfc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" {
3939
it( "should get loaded URLs", function(){
4040
var loader = getLoader();
4141
expect( loader.getLoadedURls() ).toBeArray();
42-
expect( loader.getLoadedURLs() ).toHaveLength( 2 );
42+
// BoxLang native: 1 path (helloworld.jar); CF/Lucee: 2 paths (+ cfcdynamicproxy.jar)
43+
var expectedLen = structKeyExists( server, "boxlang" ) ? 1 : 2;
44+
expect( loader.getLoadedURLs() ).toHaveLength( expectedLen );
4345
} );
4446

4547
it( "should retrieve via custom DSL", function(){

0 commit comments

Comments
 (0)