Skip to content

Feature/1004 update javalin drop tomcat - #1985

Open
MikeNeilson wants to merge 62 commits into
developfrom
feature/1004-update-javalin-drop-tomcat
Open

MikeNeilson wants to merge 62 commits into
developfrom
feature/1004-update-javalin-drop-tomcat

Conversation

@MikeNeilson

@MikeNeilson MikeNeilson commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Move to latest javalin (7.2.3)

Remove Use of Tomcat, no Tomcat 11+ in our local environment so will just use Javalin's default jetty and package it up.

NOTE: Due to Javalin updating the annotation plugin (this happened in javalin 5) this is going to be a large change as it touches every file and will need to alter how the OpenAPI static test works.

TODO:

  • - Make a decision on examples (thinking a follow up given the current size)
  • - Security operations behaving correctly. (I may give up for now and just put a note on the Swagger UI about the lock icons.)
  • - Get the version behaving correctly.

Related Issue

Validation

Existing integration tests

Checklist

  • AI tools used

@MikeNeilson

Copy link
Copy Markdown
Contributor Author

NOTE: at best I will have the annotations updated today. Getting the API running again in it's standalone mode will likely take until the end of the week if not longer.

@MikeNeilson

Copy link
Copy Markdown
Contributor Author

For the record. I have considered keeping with tomcat and just using like embedded tomcat for the packaging, but Javalin was made for Jetty so since that would be a fair amount of work anyway, better to use a tool that was actually built for this given style of packaging an app.

@MikeNeilson

Copy link
Copy Markdown
Contributor Author

FYI, I am aware this PR is rather huge.

The OpenAPI changes basically have to be all or nothing. I think we can all agree on that.

It also requires the migration to Jakarta EE (javalin update) which would may or may not (java.security.Principal so probably not) affect the CwmsAaaIdentityProvider.

So fair amount of changes to Tomcat anyway.

So is this likely to be a pain... yes, but I'm arguing it's worth it.

I could pivot back and do the move to standalone Javalin (e.g. Jetty whatever version that was) first, but then without updating javalin frankly it wouldn't be in a releasable state (security concerns, that's a really old Jetty)

So. for now I'm going to barrel through. I'm open to addressing concerns and pivoting if they're strong enough.

We do have a lot of plans. and the bulk are passing. What's failing is most likely various validation I've intentionally disabled due to the location of initialization changing.

@MikeNeilson
MikeNeilson force-pushed the feature/1004-update-javalin-drop-tomcat branch from 15a7212 to 6829e73 Compare October 7, 2026 17:56



//TODO: The rest

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these gonna get added back or dropped. Some of this commented out stuff seems important

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still figuring that out. I did get the scheme processor working,

but the returned OpenAPI object is now a builder from javalin-openapi that doesn't behave the same way... plus most of what we were doing here can no be moved to the controllers, like examples.

so I might decide to let those go for this PR, and then come back and do a follow up on them. As much as I want it all to work. at a minimum if this gets merged into main it needs to work for existing usage. And there's already new work that not going to be super trivial to rebase into this.

Comment thread cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesController.java Outdated
Comment thread cwms-data-api/build.gradle Outdated
}
classpath += configurations.tomcatLibs
classpath += configurations.baseLibs
jvmArgs += "-Dlogback.configurationFile=${projectDir}/logback-tests.xml"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be logback-test.xml ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.... that would explain some issues with what was visible.

Comment thread cwms-data-api/src/main/webapp/WEB-INF/web.xml Outdated
Comment thread cwms-data-api/build.gradle Outdated
Comment thread cwms-data-api/src/main/java/cwms/cda/data/dao/JooqDao.java
Comment thread gradle/libs.versions.toml Outdated
Comment thread cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java Outdated
Comment thread cwms-data-api/src/main/java/cwms/cda/validation/ValidationSetup.java Outdated
Comment thread buildSrc/src/main/groovy/cda.java-conventions.gradle
Comment thread cwms-data-api/src/test/java/fixtures/TestHttpServletResponse.java Outdated
Comment thread cwms-data-api/src/test/java/fixtures/TestHttpServletResponse.java Outdated
Comment thread cwms-data-api/src/test/java/fixtures/TestHttpServletResponse.java Outdated
Comment thread cwms-data-api/src/main/java/cwms/cda/security/Authenticator.java Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not because of your change but this class looks wonky, ApplicationException already has a details map. We should be able to build a map with our extra key and pass it to super and we're done. We don't have to hold our own "details" map and modify it after the super call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point... which means the QueryParameters is likely just as wonky, I copy pasted it from that.

});
configureRoutes(config.routes, metrics, cdaAccessManager);
});
QueueManager.ensureRssSubscribers(ds);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its kinda gross that ensureRssSubscribers(ds); gets called from the constructor. That means you can't build the object without it touching the database. Maybe it could move to the first line of start() ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, seams reasonable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though then there's no reference to the DataSource (easily). good idea though, will think about best place. Maybe just save a simple reference to the datasource.

Co-authored-by: Ryan Ripken <89810919+rma-rripken@users.noreply.github.com>

@MikeNeilson MikeNeilson left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Definitely more to do than what you've already pointed out. But that'll be some good cleanup before starting it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point... which means the QueryParameters is likely just as wonky, I copy pasted it from that.

if (path.length() > 1 && path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
return SPA_ROUTES.contains(path) || path.matches("/user-roles/[A-Za-z0-9-]+");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to open an issue. You're correct, but there's already enough change in this PR, even if this is small.

Comment thread cwms-data-api/src/main/java/cwms/cda/api/CatalogController.java Outdated
Comment thread cwms-data-api/src/main/java/cwms/cda/api/MeasurementTimeExtentsGetController.java Outdated
+ " implemented")},
description = "Returns Projects Data",
tags = {TAG})
tags = {TAG}, path = "/project")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep.

Comment thread gradle/libs.versions.toml Outdated



//TODO: The rest

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still figuring that out. I did get the scheme processor working,

but the returned OpenAPI object is now a builder from javalin-openapi that doesn't behave the same way... plus most of what we were doing here can no be moved to the controllers, like examples.

so I might decide to let those go for this PR, and then come back and do a follow up on them. As much as I want it all to work. at a minimum if this gets merged into main it needs to work for existing usage. And there's already new work that not going to be super trivial to rebase into this.

dsConfig.setJdbcUrl(ConfigVariables.getConfigString("CDA_JDBC_URL"));
dsConfig.setUsername(ConfigVariables.getConfigString("CDA_JDBC_USERNAME"));
dsConfig.setPassword(ConfigVariables.getConfigString("CDA_JDBC_PASSWORD"));
dsConfig.setMaximumPoolSize(ConfigVariables.getConfigInt("CDA_POOL_MAX_ACTIVE", 1));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm kinda assuming if those aren't set it's a test environment but that should likely be a different value with the integration tests specifying the 1.

I'll go with 10. I suspect most devs that would run CDA directly that will be testing something won't necessarily be testing pool issues but trying to do some bulk work.

public CwmsDataApi build()
{
if (sessionManager == null) {
this.sessionManager = new SessionHandler();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a good idea.

Comment thread buildSrc/src/main/groovy/cda.java-conventions.gradle
@MikeNeilson

Copy link
Copy Markdown
Contributor Author

Will attempt rebase from main tomorrow. The conflicts shouldn't be difficult to fix though.

tags = UserListController.TAG
methods = HttpMethod.DELETE,
tags = UserListController.TAG,
path = "/users/list/{" + USER_LIST_ID + "}/members/{" + USER_ID + "}"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not saying this is wrong its just curious that we have 3 controllers, one at /users/list/ and /user/list/{blah} and /user/list

tags = TAG
methods = HttpMethod.POST,
tags = TAG,
path = "/forecast-spec"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class has a variety of paths /forecast-spec /forecasts-spec/{} /forecasts-spec and /forecasts/spec we have a number of other controllers with inconsistencies.

ContentType contentType = Formats.parseHeader(formatHeader, WaterUser.class);
ctx.contentType(contentType.toString());
WaterUser user = Formats.parseContent(contentType, ctx.body(), WaterUser.class);
if (!projectId.equals(user.getProjectId().getName())) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if case-sensitive matters?

method = HttpMethod.GET,
path = "/locations/with-kind",
methods = HttpMethod.GET,
path = "/locations/with-kinds",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this was supposed to change from with-kind to with-kinds . If so, fine.

int lastSlash = path.lastIndexOf('/');
String pathWithOffice = path.substring(0, lastSlash) + format("/{%s}", OFFICE) + path.substring(lastSlash);
return format("/v2/" + pathWithOffice, args);
return format("/v2" + pathWithOffice, args);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change intentional? /v2 vs /v2/ ?

@rma-rripken

Copy link
Copy Markdown
Collaborator

I made it one time thru all the changed files and made comments on all the obvious things that I saw. There are a number of inconsistencies with how controllers are registered. I didn't check whether the controllers were already like that or if they are new changes but if they are new than I expect the integration tests won't be passing. Once the integration tests pass I think this will be ready

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants