Skip to content

Commit 8f651f5

Browse files
committed
add reflection hot-reinstall test via cfadmin updateRHExtension
1 parent 4b75fc7 commit 8f651f5

2 files changed

Lines changed: 113 additions & 4 deletions

File tree

.github/workflows/main.yml

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,13 +391,14 @@ jobs:
391391
lucee-express/lucee-server/context/logs/
392392
393393
test-config-override:
394-
name: Test - Config override (LUCEE_WEBSOCKET_CONFIG + alt directory)
394+
name: Test - Config override + reflection hot-reinstall
395395
runs-on: ubuntu-latest
396396
needs: [build-extension]
397397
env:
398398
LUCEE_LOGGING_FORCE_APPENDER: console
399399
LUCEE_LOGGING_FORCE_LEVEL: info
400400
LUCEE_WEBSOCKET_CONFIG: /tmp/ws-alt-config.json
401+
LUCEE_ADMIN_PASSWORD: testadmin
401402
steps:
402403
- name: Checkout
403404
uses: actions/checkout@v6
@@ -455,7 +456,13 @@ jobs:
455456
- name: Configure Tomcat port
456457
run: sed -i 's/port="8080"/port="8888"/g' lucee-express/conf/server.xml
457458

458-
- name: Start Lucee Express (with LUCEE_WEBSOCKET_CONFIG in env)
459+
- name: Resolve built .lex absolute path
460+
run: |
461+
LEX_FILE=$(ls $GITHUB_WORKSPACE/extension/*.lex | head -1)
462+
echo "WS_EXT_LEX_PATH=$LEX_FILE" >> $GITHUB_ENV
463+
echo "Resolved .lex to: $LEX_FILE"
464+
465+
- name: Start Lucee Express (with LUCEE_WEBSOCKET_CONFIG + LUCEE_ADMIN_PASSWORD in env)
459466
run: cd lucee-express && ./bin/catalina.sh start
460467

461468
- name: Wait for server
@@ -479,6 +486,33 @@ jobs:
479486
echo "CONFIG_OVERRIDE_FAILED=true" >> $GITHUB_OUTPUT
480487
fi
481488
489+
- name: Run reflection hot-reinstall test
490+
id: reflection-test
491+
continue-on-error: true
492+
run: |
493+
RESPONSE=$(curl -s -w "\n%{http_code}" http://localhost:8888/tests/integration/test-reflection-reinstall.cfm)
494+
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
495+
BODY=$(echo "$RESPONSE" | sed '$d')
496+
echo "$BODY"
497+
if [ "$HTTP_CODE" != "200" ] || echo "$BODY" | grep -q "FAILED"; then
498+
echo "REFLECTION_FAILED=true" >> $GITHUB_OUTPUT
499+
fi
500+
501+
- name: Verify reflection warning in catalina.out
502+
id: reflection-log-check
503+
continue-on-error: true
504+
run: |
505+
# The reflection fallback path logs "calling [onOpen] via reflection, servlet engine restart needed"
506+
# (see BaseWebSocketEndpoint and LDEV-6221). If the hot re-install actually exercised this path,
507+
# the warning will appear in catalina.out. If not, the round-trip test is lying.
508+
if grep -q "calling \[.*\] via reflection" lucee-express/logs/catalina.out; then
509+
echo "Reflection warning found — hot re-install exercised the fallback path"
510+
grep "calling \[.*\] via reflection" lucee-express/logs/catalina.out
511+
else
512+
echo "REFLECTION_LOG_MISSING=true" >> $GITHUB_OUTPUT
513+
echo "Reflection warning NOT found in catalina.out — hot re-install did not hit the fallback path"
514+
fi
515+
482516
- name: Dump WebSocket event logs
483517
if: always()
484518
run: |
@@ -504,8 +538,8 @@ jobs:
504538
lucee-express/logs/
505539
lucee-express/lucee-server/context/logs/
506540
507-
- name: Fail if test failed
508-
if: steps.config-override-test.outputs.CONFIG_OVERRIDE_FAILED == 'true'
541+
- name: Fail if any sub-test failed
542+
if: steps.config-override-test.outputs.CONFIG_OVERRIDE_FAILED == 'true' || steps.reflection-test.outputs.REFLECTION_FAILED == 'true' || steps.reflection-log-check.outputs.REFLECTION_LOG_MISSING == 'true'
509543
run: exit 1
510544

511545
deploy:
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<cfscript>
2+
// Triggers a hot re-install of the websocket extension via cfadmin and verifies
3+
// the round-trip still works afterwards. The CI job then greps catalina.out for
4+
// the "calling [onOpen] via reflection" warning to prove the reflection fallback
5+
// (LDEV-6221) was actually exercised.
6+
//
7+
// Preconditions set by the surrounding CI job:
8+
// - LUCEE_ADMIN_PASSWORD env var is set and matches Lucee's server admin password
9+
// - WS_EXT_LEX_PATH env var points at the built .lex on disk
10+
// - A listener (TestListener) is reachable at ws://localhost:8888/ws/TestListener
11+
12+
writeOutput( "=== Reflection Hot-Reinstall Test ===" & chr( 10 ) );
13+
14+
try {
15+
if ( !structKeyExists( getFunctionList(), "CreateWebSocketClient" ) )
16+
throw( message="CreateWebSocketClient not available", type="TestSetupError" );
17+
18+
adminPassword = server.system.environment.LUCEE_ADMIN_PASSWORD ?: "";
19+
lexPath = server.system.environment.WS_EXT_LEX_PATH ?: "";
20+
21+
if ( adminPassword == "" )
22+
throw( message="LUCEE_ADMIN_PASSWORD env var not set", type="TestSetupError" );
23+
if ( lexPath == "" || !fileExists( lexPath ) )
24+
throw( message="WS_EXT_LEX_PATH does not resolve to a file: [#lexPath#]", type="TestSetupError" );
25+
26+
writeOutput( "Re-installing extension from: " & lexPath & chr( 10 ) );
27+
28+
// Trigger re-install via admin API. This is synchronous — when it returns,
29+
// the new extension's startup hook has already run and either:
30+
// - re-registered endpoints normally (if Tomcat allowed it), OR
31+
// - fallen back to inject() via reflection (the LDEV-6221 path)
32+
cfadmin(
33+
action = "updateRHExtension",
34+
type = "server",
35+
password = adminPassword,
36+
source = lexPath
37+
);
38+
39+
writeOutput( "Re-install complete. Running round-trip test..." & chr( 10 ) );
40+
41+
// Round-trip — must still work post-reinstall
42+
wsUrl = "ws://localhost:8888/ws/TestListener";
43+
listener = new tests.integration.ClientListener();
44+
ws = CreateWebSocketClient( wsUrl, listener );
45+
sleep( 500 );
46+
47+
ws.sendText( "reflectionCheck" );
48+
sleep( 500 );
49+
50+
ws.disconnect();
51+
sleep( 500 );
52+
53+
received = listener.getMessages();
54+
writeOutput( "Received: " & received.toJSON() & chr( 10 ) );
55+
56+
errors = [];
57+
if ( !received.find( "ECHO:reflectionCheck" ) )
58+
arrayAppend( errors, "expected ECHO:reflectionCheck after re-install; got: " & received.toJSON() );
59+
60+
if ( arrayLen( errors ) ) {
61+
writeOutput( chr( 10 ) & "FAILED:" & chr( 10 ) );
62+
for ( err in errors )
63+
writeOutput( " - #err#" & chr( 10 ) );
64+
cfheader( statuscode=500, statustext="Test Failed" );
65+
}
66+
else {
67+
writeOutput( chr( 10 ) & "SUCCESS: round-trip works after hot re-install — CI will verify the reflection warning in catalina.out" & chr( 10 ) );
68+
}
69+
}
70+
catch ( any e ) {
71+
writeOutput( "FAILED with exception:" & chr( 10 ) );
72+
writeOutput( e.stacktrace );
73+
cfheader( statuscode=500, statustext="Test Failed" );
74+
}
75+
</cfscript>

0 commit comments

Comments
 (0)