Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/src/main/java/lucee/runtime/config/ConfigAdmin.java
Original file line number Diff line number Diff line change
Expand Up @@ -5064,14 +5064,23 @@ public void updateRHExtension(Config config, RHExtension rhext, boolean reload,
if (!ArrayUtil.isEmpty(rhext.getMavens())) {
Iterator<Map<String, String>> itl = rhext.getMavens().iterator();
GAVSO gavso;
boolean mavenUpdated = false;
while (itl.hasNext()) {
gavso = MavenUtil.toGAVSO(itl.next());
if (gavso != null) {
_updateMaven(gavso);
reloadNecessary = true;
mavenUpdated = true;
}
logger.info("extension", "Update maven endpoint [" + gavso + "] from extension [" + rhext.getName() + ":" + rhext.getVersion() + "]");
}
// LDEV-6297: refresh cached JavaSettings so the new maven entries reach the RPC classloader without a server restart
if (mavenUpdated && config instanceof ConfigImpl) {
ConfigWebFactory._loadJavaSettings(null, (ConfigImpl) config, root, logger);
// also invalidate the JVM-level default classloader cached in ModernApplicationContext,
// otherwise CFML createObject calls keep getting the stale classloader from before the refresh
lucee.runtime.listener.ModernApplicationContext.resetDefaultClassLoader();
}
}

// update Resource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4388,7 +4388,7 @@ else if (hasCS) {
}
}

private static void _loadJavaSettings(ConfigServerImpl configServer, ConfigImpl config, Struct root, Log log) {
static void _loadJavaSettings(ConfigServerImpl configServer, ConfigImpl config, Struct root, Log log) {
try {
if (config instanceof ConfigServerImpl) {

Expand Down
48 changes: 47 additions & 1 deletion core/src/main/java/lucee/runtime/extension/RHExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import lucee.runtime.functions.conversion.DeserializeJSON;
import lucee.runtime.interpreter.JSONExpressionInterpreter;
import lucee.runtime.listener.SerializationSettings;
import lucee.runtime.mvn.MavenUtil;
import lucee.runtime.op.Caster;
import lucee.runtime.op.Decision;
import lucee.runtime.osgi.BundleFile;
Expand Down Expand Up @@ -723,7 +724,7 @@ private void readSearch(String label, String str, Log logger) {

private void readMaven(String label, String str, Log logger) {
if (!StringUtil.isEmpty(str, true)) {
mavens = toSettings(logger, str);
mavens = toMavenSettings(logger, str);
mavensJson = str;
}
if (mavens == null) mavens = new ArrayList<Map<String, String>>();
Expand Down Expand Up @@ -1337,6 +1338,51 @@ private static void _toSettings(List list, Log log, String str, boolean valueAsS
return;
}

// LDEV-6297: accepts JSON or gradle GAV-comma; JSON tried first to preserve in-the-wild 6.2 manifests
private static List<Map<String, String>> toMavenSettings(Log log, String str) {
List<Map<String, String>> list = new ArrayList<>();

boolean parsedJson = false;
try {
Object res = DeserializeJSON.call(null, str);
if (Decision.isStruct(res)) {
parsedJson = true;
_toSetting(list, Caster.toMap(res), true);
}
else if (Decision.isArray(res)) {
parsedJson = true;
Iterator it = Caster.toList(res).iterator();
while (it.hasNext()) {
_toSetting(list, Caster.toMap(it.next()), true);
}
}
}
catch (Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
// not JSON — fall through to gradle GAV-comma
}
if (parsedJson) return list;

List<MavenUtil.GAVSO> gavsos = MavenUtil.toGAVSOs(str, null);
if (!gavsos.isEmpty()) {
for (MavenUtil.GAVSO gavso: gavsos) {
Map<String, String> m = new HashMap<>();
m.put("groupId", gavso.g);
m.put("artifactId", gavso.a);
if (gavso.v != null) m.put("version", gavso.v);
if (gavso.s != null) m.put("scope", gavso.s);
if (gavso.o != null) m.put("optional", gavso.o);
if (gavso.c != null) m.put("checksum", gavso.c);
list.add(m);
}
return list;
}

log.error("Extension Installation",
"Could not parse maven manifest field — expected JSON or 'group:artifact:version' form: " + str);
return list;
}

private static void _toSetting(List list, Map src, boolean valueAsString) throws PageException {
Entry e;
Iterator<Entry> it = src.entrySet().iterator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,13 @@ public static ClassLoader getDefaultClassLoader(ConfigWeb config) throws IOExcep
return defaultClassLoader;
}

// LDEV-6297: invalidate the cached default classloader so the next request rebuilds it from the (possibly just-refreshed) ConfigServer JavaSettings
public static void resetDefaultClassLoader() {
synchronized (token) {
defaultClassLoader = null;
}
}

@Override
public Map<Collection.Key, Object> getTagAttributeDefaultValues(PageContext pc, String tagClassName) {
if (!initDefaultAttributeValues) {
Expand Down
98 changes: 98 additions & 0 deletions test/tickets/LDEV6297.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
component extends="org.lucee.cfml.test.LuceeTestCase" {

function run( testResults, testBox ) {
describe( "LDEV-6297: extension manifest 'maven:' field accepts JSON and gradle GAV-comma forms", function() {

it( title="single GAV gradle string", body=function() {
var result = parseManifest( "org.postgresql:postgresql:42.7.1" );
expect( result.size() ).toBe( 1 );
expect( result.get( 0 ).get( "groupId" ) ).toBe( "org.postgresql" );
expect( result.get( 0 ).get( "artifactId" ) ).toBe( "postgresql" );
expect( result.get( 0 ).get( "version" ) ).toBe( "42.7.1" );
});

it( title="multiple GAVs comma-separated", body=function() {
var result = parseManifest( "org.postgresql:postgresql:42.7.1,org.apache.poi:poi:5.5.1" );
expect( result.size() ).toBe( 2 );
expect( result.get( 0 ).get( "groupId" ) ).toBe( "org.postgresql" );
expect( result.get( 0 ).get( "version" ) ).toBe( "42.7.1" );
expect( result.get( 1 ).get( "groupId" ) ).toBe( "org.apache.poi" );
expect( result.get( 1 ).get( "artifactId" ) ).toBe( "poi" );
expect( result.get( 1 ).get( "version" ) ).toBe( "5.5.1" );
});

it( title="JSON struct (single entry)", body=function() {
var result = parseManifest( "{'groupId':'org.postgresql','artifactId':'postgresql','version':'42.7.1'}" );
expect( result.size() ).toBe( 1 );
expect( result.get( 0 ).get( "groupId" ) ).toBe( "org.postgresql" );
expect( result.get( 0 ).get( "artifactId" ) ).toBe( "postgresql" );
expect( result.get( 0 ).get( "version" ) ).toBe( "42.7.1" );
});

it( title="JSON array of structs", body=function() {
var result = parseManifest( "[{'groupId':'org.postgresql','artifactId':'postgresql','version':'42.7.1'},{'groupId':'org.apache.poi','artifactId':'poi','version':'5.5.1'}]" );
expect( result.size() ).toBe( 2 );
expect( result.get( 0 ).get( "groupId" ) ).toBe( "org.postgresql" );
expect( result.get( 1 ).get( "groupId" ) ).toBe( "org.apache.poi" );
expect( result.get( 1 ).get( "version" ) ).toBe( "5.5.1" );
});

it( title="JSON array with short-key aliases (g/a/v)", body=function() {
// short keys are passed through as-is; downstream MavenUtil.toGAVSO( map ) reads both forms
var result = parseManifest( "[{'g':'org.postgresql','a':'postgresql','v':'42.7.1'}]" );
expect( result.size() ).toBe( 1 );
expect( result.get( 0 ).get( "g" ) ).toBe( "org.postgresql" );
expect( result.get( 0 ).get( "a" ) ).toBe( "postgresql" );
expect( result.get( 0 ).get( "v" ) ).toBe( "42.7.1" );
});

it( title="whitespace-padded GAV-comma form", body=function() {
var result = parseManifest( " org.postgresql:postgresql:42.7.1 , org.apache.poi:poi:5.5.1 " );
expect( result.size() ).toBe( 2 );
expect( result.get( 0 ).get( "groupId" ) ).toBe( "org.postgresql" );
expect( result.get( 0 ).get( "version" ) ).toBe( "42.7.1" );
expect( result.get( 1 ).get( "artifactId" ) ).toBe( "poi" );
});

it( title="GAV with scope token", body=function() {
var result = parseManifest( "org.postgresql:postgresql:42.7.1:runtime" );
expect( result.size() ).toBe( 1 );
expect( result.get( 0 ).get( "groupId" ) ).toBe( "org.postgresql" );
expect( result.get( 0 ).get( "version" ) ).toBe( "42.7.1" );
expect( result.get( 0 ).get( "scope" ) ).toBe( "runtime" );
});

it( title="garbage value parses as neither, returns empty list", body=function() {
// a single 'Could not parse maven manifest field' error is logged; mavens stays empty
var result = parseManifest( "this is not a parseable manifest value" );
expect( result.size() ).toBe( 0 );
});

});
}

private any function parseManifest( required string str ) {
var classCls = createObject( "java", "java.lang.Class" );
var rhExtClass = classCls.forName( "lucee.runtime.extension.RHExtension" );
var logClass = classCls.forName( "lucee.commons.io.log.Log" );
var stringClass = classCls.forName( "java.lang.String" );

var method = rhExtClass.getDeclaredMethod( "toMavenSettings", classArray( [ logClass, stringClass ] ) );
method.setAccessible( true );

var log = getPageContext().getConfig().getLog( "application" );
return method.invoke( nullValue(), [ log, arguments.str ] );
}

// build a real java.lang.Class[] array for getDeclaredMethod's param-types argument
private any function classArray( required array classes ) {
var classCls = createObject( "java", "java.lang.Class" );
var arrayType = createObject( "java", "java.lang.reflect.Array" );
var arr = arrayType.newInstance( classCls.getClass(), arrayLen( arguments.classes ) );
for ( var i = 1; i <= arrayLen( arguments.classes ); i++ ) {
arrayType.set( arr, i - 1, arguments.classes[ i ] );
}
return arr;
}

}
Loading