I’m having problems adding maven dependencies to .CFConfig.json. Here’s what I was trying to add:
"javasettings": {
"maven": [
"com.rabbitmq:amqp-client:5.25.0",
"software.amazon.awssdk:cloudfront:2.20.0",
"software.amazon.awssdk:secretsmanager:2.20.0",
"software.amazon.awssdk:url-connection-client:2.20.0"
]
}
I looked in the deploy.log file and didn’t see any issues. The only way I was able to find the issue was by slowly removing parts of my .CFConfig.json file and then trying to load the Lucee Administrator, which would not open when these dependencies were present.
What’s the best practice for adding these dependencies? Are there problems or limitations with this methodology that I should be aware of? How can I make this work, or am I forced to build fat jars that include the entire dependency graph for each one?
I ended up writing my own java bundle builder that pulls the entire dependency tree for a dependency and builds a shaded bundle jar for it. Not sure if this is the best way to go, but it seems to have solved my problem for the time being.
G’day — chasing this down right now, reproduced your symptoms on the latest 6.2 and 7.0 snapshots with your exact .CFConfig.json block. Quick update on what’s going on and a workaround you can ship today.
What’s actually failing
The “no error in deploy.log, just silent failure” you’re seeing is HTTP 429 (Too Many Requests) from Maven Central, swallowed by Lucee’s resolver. Once I patched the resolver locally to surface the real cause, the message becomes:
Failed to download [groupID:io.dropwizard.metrics;artifactId:metrics-core;version:4.2.30:jar]
from 1 repositories - Maven Central: 429 (Retry-After: 222)
Maven Central has been tightening 429 enforcement since 2024 (Sonatype FAQ). A coordinate like com.rabbitmq:amqp-client:5.25.0 pulls 30+ transitive deps (dropwizard metrics is one of them); on 6.2 those are resolved in parallel, which makes the per-IP threshold trivial to trip. The opaque error you see is MavenUtil.download() discarding the HTTP status code and the per-repo exception in three separate places — there’s literally no path for the truth to reach you. That’s why the deploy.log was empty.
We’ve got this in flight
This whole problem space is being worked under an epic — see LDEV-6299. Sub-tickets:
- LDEV-6316 — surface the actual HTTP status, Retry-After header and per-repo exception in the wrapping IOException. Patch is written and tested, will land shortly on 6.2 and cherry-pick to 7.0. After that you’ll see exactly which repo returned what.
- LDEV-6317 — the second “fallback” repo (
oss.sonatype.org) is deprecated and just redirects back to repo1.maven.org — same Fastly stack, same 429 bucket. Being replaced with the Google Cloud Maven Central mirror (independent infrastructure, actual fallback).
- LDEV-6318 — treat 429+Retry-After as a transient signal with proper backoff rather than burning a 15-minute negative-cache slot.
Also note: 7.0 already resolves transitive deps sequentially (the parallel walker was removed) — same artifacts, same machine, no 429 in our repro. So 7.0 is materially less prone to this than 6.2.
Workaround you can use today
Lucee’s resolver checks ${user.home}/.m2/repository before any HTTP. If the artifact is sitting there, it’s copied straight in — zero network, zero 429 risk, zero mavenLoad() flakiness.
Pipeline recipe:
-
Drop a tiny pom.xml somewhere in your repo (not the Lucee app itself, just a build-time resolver):
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>local</groupId>
<artifactId>lucee-runtime-deps</artifactId>
<version>0</version>
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.25.0</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>cloudfront</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>secretsmanager</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
<version>2.20.0</version>
</dependency>
</dependencies>
</project>
-
In your CI/build step:
mvn dependency:resolve -f path/to/that-pom.xml
That pulls the full transitive tree into ~/.m2/repository once, deterministically.
-
Cache ~/.m2/repository between CI runs. Every CI system has built-in support for this — GitHub Actions setup-java does it automatically, GitLab/Jenkins/Bitbucket all have Maven cache patterns.
-
When you deploy, restore the cache so it lands at the runtime user’s ~/.m2/repository. This is the one footgun: if Lucee runs as tomcat but you cached under root HOME, the lookup won’t find anything. In Docker, either:
- mount the cache volume at
/root/.m2 if you run as root, or
chown -R tomcat:tomcat /home/tomcat/.m2 after restore, or
- set the JVM
-Duser.home=/path/to/cache-parent
-
Boot Lucee → first request → resolver checks ~/.m2, copies files in, done. No HTTP, no rate-limit dependency, no opaque failures.
This is basically the same pattern every Java CI build has used for a decade — Lucee just needs the standard Maven layout to be present at the standard path.
TL;DR
- Your symptom is silent 429 from Maven Central, made worse by 6.2’s parallel resolver.
- We’re aware (LDEV-6299 epic) and the diagnostic-visibility patch will be in the next 6.2 snapshot. Once you can see the cause you can react to it.
- Until then, pre-resolve into
~/.m2/repository in your pipeline and cache it. Lucee picks it up automatically — bypasses the entire HTTP path.
- On 7.0 the parallel-resolution trigger is already gone, so if you can move to 7.0 stable you’ll see this less.
Sorry about the silent failure — that’s the bit that’s making this miserable to debug, and that’s the first thing we’re fixing.
1 Like
Very helpful info. The lack of log information was problematic so I’m glad you guys are addressing that.
The one thing that I’m curious about are conflicting jars, where one jar is loaded but a dependency requires the same jar with a different version. How is this handled? The code I wrote to pull the dependency graph builds a shaded bundled jar that afaik avoids this problem.
With modern cfc using javasettings - maven, that explicit set is loaded into it’s own classloader?
But as you are still using the old 6.2 LTS, please keep in mind, there’s quite a lot of maven java gotchas which have all been properly resolved in 7
1 Like
Can you try the latest 6.2 snapshot?
I have made some changes to both surface 429 retry after errors during maven resolution and also switched the maven resolver to be single threaded, which should avoid triggering all the 429s
https://luceeserver.atlassian.net/browse/LDEV-6320?focusedCommentId=84025
@Zackster - I could give it a shot, but my understanding is that Maven failures when deploying .CFConfig.json are still possible, leaving Lucee in a bad state. I need something that works 100% of the time, which is why I’m currently bundling shaded fat jars with maven relocations (to avoid classpath conflicts) prior to deploying the instance, and then installing them after Lucee is installed. Any Maven failures can be detected early in the deployment process and before the instance is created.
Doesn’t this seem like a better way to do it?
Pre population of the users .m2 cache is how to avoid any potential network issues
Are you still seeing problems with the latest snapshot, or haven’t you tried it?
I haven’t tried it because I worked around the issue and I don’t have time to go back and test at the moment. I have some research to do on this, and thank you pointing me in the right direction.
Currently, I have a python script that builds my dependencies (and transitives) as fat jars through Maven. Then, I copy those dependencies into my app at deploy time.
When I create the java objects, I specify the bundle like:
createObject("java", "com.something.java.MyClass", "/path/to/dependency.jar")
This works, but do you think it’s worth going back and using Lucee’s built in maven features? My biggest issue with it is that I don’t see an easy way to detect a failure, and if it does fail, I need to rebuild the entire instance, or write a complicated script to retry.