From c511fab6ce2fc5e01a9b6fea8e2ebd7b4467be26 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 14 Feb 2026 12:56:13 -0800 Subject: [PATCH 1/5] Allow registering multiple InvoicedItemsProcessingService impls --- .../labkey/wnprc_billing/WNPRC_BillingModule.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/wnprc_billing/src/org/labkey/wnprc_billing/WNPRC_BillingModule.java b/wnprc_billing/src/org/labkey/wnprc_billing/WNPRC_BillingModule.java index 4089c3ebc..ac1ad4461 100644 --- a/wnprc_billing/src/org/labkey/wnprc_billing/WNPRC_BillingModule.java +++ b/wnprc_billing/src/org/labkey/wnprc_billing/WNPRC_BillingModule.java @@ -31,7 +31,6 @@ import org.labkey.api.query.DefaultSchema; import org.labkey.api.query.QuerySchema; import org.labkey.api.security.roles.RoleManager; -import org.labkey.api.services.ServiceRegistry; import org.labkey.api.view.WebPartFactory; import org.labkey.wnprc_billing.dataentry.ChargesFormType; import org.labkey.wnprc_billing.dataentry.NonAnimalChargesFormType; @@ -39,9 +38,9 @@ import org.labkey.wnprc_billing.pipeline.BillingPipelineProvider; import org.labkey.wnprc_billing.pipeline.InvoicedItemsProcessingServiceImpl; import org.labkey.wnprc_billing.query.WNPRC_BillingUserSchema; -import org.labkey.wnprc_billing.table.WNPRC_BillingCustomizer; import org.labkey.wnprc_billing.security.permissions.EHRFinanceAdminPermission; import org.labkey.wnprc_billing.security.roles.EHRFinanceAdmin; +import org.labkey.wnprc_billing.table.WNPRC_BillingCustomizer; import java.util.Collection; import java.util.Collections; @@ -69,12 +68,6 @@ public String getName() return 22.000; } - @Override - public boolean hasScripts() - { - return true; - } - @Override @NotNull protected Collection createWebPartFactories() @@ -86,7 +79,7 @@ protected Collection createWebPartFactories() protected void init() { addController(WNPRC_BillingController.NAME, WNPRC_BillingController.class); - ServiceRegistry.get().registerService(InvoicedItemsProcessingService.class, new InvoicedItemsProcessingServiceImpl()); + InvoicedItemsProcessingService.register(this, new InvoicedItemsProcessingServiceImpl()); BillingNotificationService.get().registerBillingNotificationProvider(WNPRCBillingNotificationProvider.get()); registerRoles(); @@ -122,7 +115,7 @@ protected void registerSchemas() @Override public QuerySchema createSchema(final DefaultSchema schema, Module module) { - return new WNPRC_BillingUserSchema(WNPRC_BillingSchema.NAME, null, schema.getUser(), schema.getContainer(), WNPRC_BillingSchema.getInstance().getSchema()); + return new WNPRC_BillingUserSchema(WNPRC_BillingSchema.NAME, null, schema.getUser(), schema.getContainer(), WNPRC_BillingSchema.getSchema()); } }); } From 70a9e8b378e1d7db7125607dcd57322c28294e9b Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 14 Feb 2026 19:57:55 -0800 Subject: [PATCH 2/5] Fix module memory leaks caused by GoogleDriveModule --- .../labkey/googledrive/GoogleDriveModule.java | 20 ++++++++++--------- .../org/labkey/wnprc_r24/wnprc_r24Module.java | 1 + .../org/labkey/wnprc_u24/wnprc_u24Module.java | 1 + 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/GoogleDrive/src/org/labkey/googledrive/GoogleDriveModule.java b/GoogleDrive/src/org/labkey/googledrive/GoogleDriveModule.java index 4e1f33d76..ce8f1e72c 100644 --- a/GoogleDrive/src/org/labkey/googledrive/GoogleDriveModule.java +++ b/GoogleDrive/src/org/labkey/googledrive/GoogleDriveModule.java @@ -5,6 +5,7 @@ import org.labkey.api.data.ContainerManager; import org.labkey.api.ldk.ExtendedSimpleModule; import org.labkey.api.module.Module; +import org.labkey.api.module.ModuleContext; import org.labkey.api.module.ModuleLoader; import org.labkey.api.query.DefaultSchema; import org.labkey.api.query.QuerySchema; @@ -17,11 +18,6 @@ import java.util.Set; public class GoogleDriveModule extends ExtendedSimpleModule { - @Override - public boolean hasScripts() { - return true; - } - @Override @NotNull protected Collection createWebPartFactories() { @@ -33,17 +29,23 @@ protected void init() { addController(GoogleDriveController.NAME, GoogleDriveController.class); GoogleDriveService.set(new GoogleDriveServiceImpl()); + } - Module thisModule = ModuleLoader.getInstance().getModule(GoogleDriveModule.class); + @Override + protected void doStartupAfterSpringConfig(ModuleContext moduleContext) + { + // We moved this from init() to startup() because init() is too early to be referencing active modules, which + // indirectly loads and stashes web parts. Some referenced modules might be removed after this module's init() + // runs (e.g., if dependencies are missing), which would then lead to memory leaks. Container home = ContainerManager.getHomeContainer(); // Ensure that we're enabled in the home module, since we'll use that for our queries. if (ModuleLoader.getInstance().shouldInsertData()) { Set homeModules = new HashSet<>(home.getActiveModules()); - if (!homeModules.contains(thisModule)) + if (!homeModules.contains(this)) { - homeModules.add(thisModule); + homeModules.add(this); home.setActiveModules(homeModules); } } @@ -66,7 +68,7 @@ public void registerSchemas() { DefaultSchema.registerProvider(GoogleDriveSchema.NAME, new DefaultSchema.SchemaProvider(this) { @Override public QuerySchema createSchema(final DefaultSchema schema, Module module) { - return (QuerySchema) new GoogleDriveSchema(schema.getUser(), schema.getContainer()); + return new GoogleDriveSchema(schema.getUser(), schema.getContainer()); } }); } diff --git a/WNPRC_r24/src/org/labkey/wnprc_r24/wnprc_r24Module.java b/WNPRC_r24/src/org/labkey/wnprc_r24/wnprc_r24Module.java index 52a53edc0..c077d1094 100644 --- a/WNPRC_r24/src/org/labkey/wnprc_r24/wnprc_r24Module.java +++ b/WNPRC_r24/src/org/labkey/wnprc_r24/wnprc_r24Module.java @@ -79,6 +79,7 @@ public void doStartup(ModuleContext moduleContext) final DbSchema dbSchema = DbSchema.get(schemaName, DbSchemaType.Module); DefaultSchema.registerProvider(dbSchema.getQuerySchemaName(), new DefaultSchema.SchemaProvider(this) { + @Override public QuerySchema createSchema(final DefaultSchema schema, Module module) { DbSchema dbSchema = DbSchema.get(schemaName, DbSchemaType.Module); diff --git a/WNPRC_u24/src/org/labkey/wnprc_u24/wnprc_u24Module.java b/WNPRC_u24/src/org/labkey/wnprc_u24/wnprc_u24Module.java index b121c69a2..7fee16325 100644 --- a/WNPRC_u24/src/org/labkey/wnprc_u24/wnprc_u24Module.java +++ b/WNPRC_u24/src/org/labkey/wnprc_u24/wnprc_u24Module.java @@ -79,6 +79,7 @@ public void doStartup(ModuleContext moduleContext) final DbSchema dbSchema = DbSchema.get(schemaName, DbSchemaType.Module); DefaultSchema.registerProvider(dbSchema.getQuerySchemaName(), new DefaultSchema.SchemaProvider(this) { + @Override public QuerySchema createSchema(final DefaultSchema schema, Module module) { DbSchema dbSchema = DbSchema.get(schemaName, DbSchemaType.Module); From 57e9aedb8b9d2f5ef7a4a3058e1abcabddb36f09 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 14 Feb 2026 21:12:29 -0800 Subject: [PATCH 3/5] Avoid webpart conflict by deleting them both --- WNPRC_r24/resources/views/welcome.html | 1 - WNPRC_r24/resources/views/welcome.view.xml | 5 ----- WNPRC_r24/resources/views/welcome.webpart.xml | 3 --- 3 files changed, 9 deletions(-) delete mode 100644 WNPRC_r24/resources/views/welcome.html delete mode 100644 WNPRC_r24/resources/views/welcome.view.xml delete mode 100644 WNPRC_r24/resources/views/welcome.webpart.xml diff --git a/WNPRC_r24/resources/views/welcome.html b/WNPRC_r24/resources/views/welcome.html deleted file mode 100644 index a8052619a..000000000 --- a/WNPRC_r24/resources/views/welcome.html +++ /dev/null @@ -1 +0,0 @@ -

Welcome to the r24 module

\ No newline at end of file diff --git a/WNPRC_r24/resources/views/welcome.view.xml b/WNPRC_r24/resources/views/welcome.view.xml deleted file mode 100644 index 66231bee1..000000000 --- a/WNPRC_r24/resources/views/welcome.view.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/WNPRC_r24/resources/views/welcome.webpart.xml b/WNPRC_r24/resources/views/welcome.webpart.xml deleted file mode 100644 index 10d5ec1e2..000000000 --- a/WNPRC_r24/resources/views/welcome.webpart.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file From c9669c7ab9aacb3d2b432d745766dfdc313b76e4 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 16 Feb 2026 07:54:49 -0800 Subject: [PATCH 4/5] Address some warnings --- .../WNPRC_ComplianceController.java | 3 +++ .../WNPRC_ComplianceModule.java | 8 +------ .../org/labkey/wnprc_ehr/WNPRC_EHRModule.java | 24 ++++++++----------- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/WNPRC_Compliance/src/org/labkey/wnprc_compliance/WNPRC_ComplianceController.java b/WNPRC_Compliance/src/org/labkey/wnprc_compliance/WNPRC_ComplianceController.java index a9a447cc9..ffc9e4266 100644 --- a/WNPRC_Compliance/src/org/labkey/wnprc_compliance/WNPRC_ComplianceController.java +++ b/WNPRC_Compliance/src/org/labkey/wnprc_compliance/WNPRC_ComplianceController.java @@ -404,6 +404,7 @@ public Object execute(CardExemptForm form, BindException errors) throws Exceptio @RequiresPermission(ComplianceAdminPermission.class) @Marshal(Marshaller.Jackson) public class GetPersonFromCardAPI extends ReadOnlyApiAction { + @Override public Object execute(SearchPersonFromCardForm form, BindException errors) throws Exception { JSONObject json = new JSONObject(); Map> results = new HashMap<>(); @@ -443,6 +444,7 @@ public Object execute(SearchPersonFromCardForm form, BindException errors) throw @Marshal(Marshaller.Jackson) @CSRF(CSRF.Method.NONE) public class GetClearancesFromPerson extends ReadOnlyApiAction { + @Override public Object execute(SearchClearanceFromPersonForm form, BindException errors) { @@ -526,6 +528,7 @@ public Object execute(Clearances form, BindException errors) throws Exception { @Marshal(Marshaller.Jackson) @CSRF(CSRF.Method.NONE) public class GetMeaslesClearanceFromPerson extends ReadOnlyApiAction { + @Override public Object execute(SearchPersonFromCardForm form, BindException errors) { JSONObject json = new JSONObject(); diff --git a/WNPRC_Compliance/src/org/labkey/wnprc_compliance/WNPRC_ComplianceModule.java b/WNPRC_Compliance/src/org/labkey/wnprc_compliance/WNPRC_ComplianceModule.java index 15f82ec14..30f9b9ecb 100644 --- a/WNPRC_Compliance/src/org/labkey/wnprc_compliance/WNPRC_ComplianceModule.java +++ b/WNPRC_Compliance/src/org/labkey/wnprc_compliance/WNPRC_ComplianceModule.java @@ -4,7 +4,6 @@ import org.labkey.api.data.Container; import org.labkey.api.ldk.ExtendedSimpleModule; import org.labkey.api.module.Module; -import org.labkey.api.module.ModuleContext; import org.labkey.api.query.DefaultSchema; import org.labkey.api.query.QuerySchema; import org.labkey.api.security.roles.RoleManager; @@ -17,11 +16,6 @@ import java.util.Set; public class WNPRC_ComplianceModule extends ExtendedSimpleModule { - @Override - public boolean hasScripts() { - return true; - } - @Override @NotNull protected Collection createWebPartFactories() { @@ -52,7 +46,7 @@ public void registerSchemas() { DefaultSchema.registerProvider(WNPRC_ComplianceSchema.NAME, new DefaultSchema.SchemaProvider(this) { @Override public QuerySchema createSchema(final DefaultSchema schema, Module module) { - return (QuerySchema) new WNPRC_ComplianceSchema(schema.getUser(), schema.getContainer()); + return new WNPRC_ComplianceSchema(schema.getUser(), schema.getContainer()); } }); } diff --git a/WNPRC_EHR/src/org/labkey/wnprc_ehr/WNPRC_EHRModule.java b/WNPRC_EHR/src/org/labkey/wnprc_ehr/WNPRC_EHRModule.java index d6caf1205..a0b9b496f 100644 --- a/WNPRC_EHR/src/org/labkey/wnprc_ehr/WNPRC_EHRModule.java +++ b/WNPRC_EHR/src/org/labkey/wnprc_ehr/WNPRC_EHRModule.java @@ -175,6 +175,7 @@ public class WNPRC_EHRModule extends ExtendedSimpleModule */ private boolean loadOnStart = false; + @Override public String getName() { return NAME; @@ -185,11 +186,6 @@ public String getName() return forceUpdate ? Double.POSITIVE_INFINITY : 22.010; } - @Override - public boolean hasScripts() { - return true; - } - @Override protected void init() { addController(CONTROLLER_NAME, WNPRC_EHRController.class); @@ -202,16 +198,16 @@ protected void init() { registerPermissions(); } - @Override - @NotNull - protected Collection createWebPartFactories() - { - return new ArrayList<>(Arrays.asList(waterCalendarWebPart)); - } + @Override + @NotNull + protected Collection createWebPartFactories() + { + return new ArrayList<>(Arrays.asList(waterCalendarWebPart)); + } - @Override - protected void doStartupAfterSpringConfig(ModuleContext moduleContext) - { + @Override + protected void doStartupAfterSpringConfig(ModuleContext moduleContext) + { ModuleUpdate.onStartup(moduleContext, this); EHRService.get().registerModule(this); From 37222f36e90330d500a4453fa2ab4c01359c4d52 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Mon, 16 Feb 2026 08:08:26 -0800 Subject: [PATCH 5/5] Move back to init() --- .../src/org/labkey/googledrive/GoogleDriveModule.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/GoogleDrive/src/org/labkey/googledrive/GoogleDriveModule.java b/GoogleDrive/src/org/labkey/googledrive/GoogleDriveModule.java index ce8f1e72c..c3d8bf38e 100644 --- a/GoogleDrive/src/org/labkey/googledrive/GoogleDriveModule.java +++ b/GoogleDrive/src/org/labkey/googledrive/GoogleDriveModule.java @@ -5,7 +5,6 @@ import org.labkey.api.data.ContainerManager; import org.labkey.api.ldk.ExtendedSimpleModule; import org.labkey.api.module.Module; -import org.labkey.api.module.ModuleContext; import org.labkey.api.module.ModuleLoader; import org.labkey.api.query.DefaultSchema; import org.labkey.api.query.QuerySchema; @@ -29,14 +28,7 @@ protected void init() { addController(GoogleDriveController.NAME, GoogleDriveController.class); GoogleDriveService.set(new GoogleDriveServiceImpl()); - } - @Override - protected void doStartupAfterSpringConfig(ModuleContext moduleContext) - { - // We moved this from init() to startup() because init() is too early to be referencing active modules, which - // indirectly loads and stashes web parts. Some referenced modules might be removed after this module's init() - // runs (e.g., if dependencies are missing), which would then lead to memory leaks. Container home = ContainerManager.getHomeContainer(); // Ensure that we're enabled in the home module, since we'll use that for our queries.