Skip to content

Conversation

@botimer
Copy link
Member

@botimer botimer commented Dec 24, 2025

TL;DR -- the kernel can be booted standalone and deliver a standard application context to components and tools. This enables context hierarchy, annotation-based configuration, and acceptance testing that can use the entire real kernel and request filter stack without needing to mock anything.

This is an overhaul of the long-stale PR #10677, brought up to date with Sakai 25 and extended some. I won't repeat the lengthy description from that PR, but will mention the additions from 28dc436 and f2e2444 and a couple of notes:

  • The Ignite/Ehcache/Hibernate startup timing stuff is finally figured out
  • All of the kernel-using tests in the Announcements tool can now run under mvn or in an IDE without clobbering each other
  • There are certain to be other places that rely on static / caching covers, and those should be tracked down and eliminated
  • There is a bit more work to do to move the examples / testing integrity tests out of announcement and a bit of experimentation to see if we can now use the context hierarchy to separate and cache the kernel context from tools

I'm marking this PR as draft while I do that last couple of steps and consider what documentation, discussion, or roadmapping is appropriate.

Summary by CodeRabbit

Release Notes

  • Chores
    • Refactored internal service dependencies from locator-based to dependency-injection pattern across core modules.
    • Enhanced test infrastructure with new testing configuration and support classes for kernel and announcement modules.
    • Simplified cache manager shutdown process.

✏️ Tip: You can customize this high-level summary in your review settings.

This is not working, because the kernel has a bad dependency on impls,
by way of the MessagingService.
The only places that need to know about "Shared" are those creating the
context to begin with.
We need to add an initializer to be able to register the context once
it's created. We don't really have any other customization to do, but
the API doesn't quite let us catch it directly... so we have to
approximate the DelegatingSmartContextLoader. We lose Groovy support,
but we retain XML and Annotations, with a trivial implementation.

This still doesn't quite work, though, because the placeholder
properties are lost by switching to a GenericApplicationContext, which
is unfortunately required by the loaders with the real implementation.
But, registering those two beans up front should take care of it.
This is a test that the kernel boots once per file/suite and that you
can get reliable user journey testing if you want.

We can add some reset capability, or more likely: do unrelated stuff,
clean up, or use different files to isolate.
It turns out, things DO call the CM during refresh. Just set it first.
Getting the property overrides and placeholders loading in the normal
startup phase was the last piece to using a standard Spring context. At
this point, the SharedApplicationContext is unused, and probably not
necessary. I'm leaving it in place for the moment, because it extends
AbstractRefreshableApplicationContext, which could theoretically open
the chance for reloadability.

However, if we can fix up SakaiProperties and use regular beans and
environment inheritance/resolution, we could load the kernel in a
generic context and let each component build its own child context. That
would require an entrypoint that delivers the parent context and
initiates component load, but that seems like the logical progression.

There doesn't seem to be much of a use case for kernel reloading if
components could be redeployed, especially if there is parallel or lazy
loading of tool webapps. The BDD/TDD cycle for tools can now be
dramatically faster and easier. Reloadable components (and probably
some proxy/reload trigger for the tools) would shorten the cycle time
for interactive testing and changes.
The default SmartContextLoader behavior examines the configuration and
annotations on your test class. It selects one of four loaders, based on
whether or not you have @WebAppConfiguration and whether you have
classes or locations defined in the @ContextConfiguration. These are the
regular/web variations of annotation and XML loaders, respectively.

We want to preserve this behavior and not reimplement the details of
creating the context, but we have to plug in at exactly the right time:
we need to register context as global after it created, but before
refresh happens. Because loadContext is a template method, and it
delegates to the appropriate subclass for the load-refresh, we have to
extend all four variations to implement prepareContext, which is an
extension point for exactly this purpose -- preparing the context to be
refreshed.

The reason we need this timing is that the singletons are
"pre-instantiated" during refresh. Basically, any bean that gets an id
is a singleton, and all of our traditional services fall in this
category. Some of these call the ComponentManager cover during their
init method, so we need to have the global context registered already.
When it is, a ComponentManager.get translates to a context.getBean,
which is no problem; the container carries on creating any beans that
are requested, transitively.

All put together, this means that a test can inherit from the regular
test or the web test base class, depending on whether it needs a web
application context (and MockMvc / WebClient) facilities, and it will
have a full kernel and context set up, with all services injectable.
This adds some jars to the test helper and upgrades reactor-core
(because WebTestClient uses Sinks, which is new in 3.4). With the web
test working (though not passing or testing anything, it's time to move
the test helpers/base classes to the kernel.
This isn't necessarily the right strategy, but it allows the portal to
continue to function the same and let us instantiate the
ToolPortalServlet in a test context. Combined with the RequestFilter,
which is already in kernel-api, we can drive very realistic requests to a
tool easily and see what else we need.
- Stop scanning announcements packages from the kernel
- Add a component scan to announcement-impl for the bullhorn handler
  (and making way for anything else)
- Set up context hierarchy so test components can be loaded alongside
  the kernel or in a child context
- Add a portal level to the hierarchy that scans for the
  ToolPortalServlet, making it available to inject easily
- Expand the test helper to include edu-services and portal-util
- Build out the WebContextTest into a real proof of concept, making
  requests through the request filter, tool portal, and announcements
Because a different kind of Spring application context is now mentioned
in the ComponentManager cover, PowerMock has to deal with that. In these
few cases, it could not during tests, because spring-context wasn't
loaded. It's always provided in shared, anyway, so we just add it.

The other issue was that portal-tool now needs to know about the @nAmed
annotation, added so the ToolPortalServlet can be detected automatically
as a component/bean. A simple addition of javax.inject fixes that.
The recently added kernel-test and test-helper modules will probably
merge eventually, but for now we put all of the test support code in
kernel-test and pack it up in the helper jar like everything else. The
former gets a few new provided dependencies identified for compile.
This also adds test-helper to the kernel pom, so it is now within the
normal compile tree/cycle.

For now, anyway, the classes all have a Modi prefix. That's probably
unnecessary now that they're in a reasonable package.

This also turns down the log noise. If you want to turn logs up, put a
sakai.properties file in target/sakai-home of the module you are
testing with log.config entries.
- json-simple
- javax-mail
- ehcache-core

We add json-simple because HtmlPageFilter gained this dependency (which
is widely used outside of the kernel) in support of configuring MathJax.
These should probably be converted across the board to Jackson or an
alternative implementation like jolokia-json.

We add com.sun.mail:jakarta-mail to support changes in the Message
Service implementation. Whether we stay with that jar or switch to
something like Angus, the references should all be updated to jakarta.

The ehcache-core dependency is dubious, with both Ignite and Hazelcast
in place.
The tests aren't to what I consider a fully running state because they
cannot start and stop cleanly when running a group of test classes.
Getting the startup and shutdown isolation working properly will be
quite a bit of work.

- Tidy up the test hierarchy slightly
- Add the LTI API for test scope
- Stub out the LTIService since all it does for announcement is adjust
  links embedded in messages
This large commit adjusts a number of problems with dependencies and
initialization. They are mostly related to either caching references or
sequencing and a bit of circular dependency, especially around
Hibernate, Ignite, the ServerConfigurationService, and the
MemoryService. Now, it's possible to run multiple tests that each get
their own kernel and clean application context.

The main problem around Ignite/Hibernate:

There has been a long-standing dependency cycle between the caching
provider and Hibernate. The issue is that Ignite needs startup
properties, which our setup step was reading from
ServerConfigurationService, which can read properties from the database,
so it needs Hibernate, which needs Ignite for its caching. The
IgniteSpringBean works out of the box to start the grid at the right
time, provided you do not have a dependency cycle.

The Fix:

- Add a non-dynamic ConfiguredContext to stand in for these
  fundamental properties that cannot be sourced dynamically (at least
  not through the global Hibernate).
- Inject this into the IgniteConfiguration instead of SCS.
- Eliminate the caching of the IgniteConfiguration that was in place
  because of reentrant setup -- AbstractFactoryBean already manages this
  and will create one configuration per context.
- Adjust the lifecycle interaction so that our EntityManagerFactory is
  injected with the configured Ignite grid instance, rather than the
  configuration; ensure that it is started in time for Hibernate, but
  only once.
- Adjust the SakaiPersistenceUnitManager and AssignableUUIDGenerator so
  they also only depend on this ConfiguredContext at startup.
- Allow for multiple Ignite instances to be started by appending an
  index to the name -- this is primarily to avoid any timing issues with
  test runners.

The problem and fix around Ehcache:

- The CacheManager class is rather strange at our version. The clearAll
  and removalAll methods do not behave consistently and throw exceptions
  on what would be normal shutdown, so we simply call shutdown.

General reference caching:

- The GlobalApplicationContext now ensures that the ComponentManager
  cover does not hold a stale reference to a closed context.
- The SiteService uses BaseDbFlatStorage, which was calling the caching
  MemoryServiceLocator. MemoryAction was also using this locator.
- It's unlikely that outside code was using this locator, but it is now
  marked deprecated for removal in 27. It also has its caching removed,
  so it will reliably retrieve from the correct context.
- The ResourceLoader was caching components statically, meaning that
  almost any tool would throw NPEs on a reloaded kernel.
- This ResourceLoader caching is completely removed, but could be added
  back at the instance level if we find performance impact.

Miscellaneous items:

- This is the first time that the modi SysProp is used outside the
  package, so it needed some methods marked public.
- Add a few NumberUtil methods to better handle Optionals.
- Adjust the mocking in one assignment test that relies on advising the
  Hibernate ID generation (switch it to use ConfiguredContext)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 24, 2025

Walkthrough

This pull request introduces a configuration abstraction layer (ConfiguredContext) to replace ServerConfigurationService, refactors the component manager to use Spring's ConfigurableApplicationContext instead of SharedApplicationContext, establishes a new Modi test framework, and updates memory service usage to leverage ComponentManager dependency injection.

Changes

Cohort / File(s) Summary
Memory Service Refactoring
admin-tools/src/java/org/sakaiproject/memory/tool/MemoryAction.java, kernel/kernel-storage-util/src/main/java/org/sakaiproject/util/BaseDbFlatStorage.java, kernel/kernel-impl/src/main/java/org/sakaiproject/memory/impl/EhcacheMemoryService.java, kernel/api/src/main/java/org/sakaiproject/memory/cover/MemoryServiceLocator.java
Transitions from MemoryServiceLocator to ComponentManager.get(MemoryService.class) injection pattern. MemoryServiceLocator deprecated (forRemoval=true, since=26) with simplified getInstance() implementation. Memory service shutdown logic simplified in EhcacheMemoryService.
ConfiguredContext API & Implementation
kernel/component-manager/src/main/java/org/sakaiproject/component/api/ConfiguredContext.java, kernel/kernel-impl/src/main/java/org/sakaiproject/component/impl/ConfiguredContextImpl.java, kernel/kernel-impl/src/main/webapp/WEB-INF/component-components.xml
Introduces new public interface ConfiguredContext with typed getters (String, Integer, Long, Boolean, Object) supporting Optional and default values. ConfiguredContextImpl provides Properties-backed implementation with CSV/enumerated multi-value parsing. Bean wiring added to kernel configuration.
Modi Framework Core
kernel/component-manager/src/main/java/org/sakaiproject/modi/Configuration.java, kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java, kernel/component-manager/src/main/java/org/sakaiproject/modi/Launcher.java, kernel/component-manager/src/main/java/org/sakaiproject/modi/ComponentsDirectory.java, kernel/component-manager/src/main/java/org/sakaiproject/modi/ModiSakaiProperties.java
Transitions from SharedApplicationContext to ConfigurableApplicationContext/GenericApplicationContext. GlobalApplicationContext gains setContext() public API with exception handling. Configuration made public; base configuration path changed to modi-configuration.xml. Launcher integrates context with GlobalApplicationContext. ModiSakaiProperties implements BeanFactoryPostProcessor with priority ordering.
Modi Configuration & Properties
kernel/component-manager/src/main/java/org/sakaiproject/modi/SysProp.java, kernel/component-manager/src/main/bundle/org/sakaiproject/config/modi-configuration.xml
SysProp methods transitioned from package-private to public API. New modi-configuration.xml defines PropertySourcesPlaceholderConfigurer and DefaultSakaiProperties beans with DynamicDefaultSakaiProperties and specific kernel.properties location.
Component Manager Wiring Updates
kernel/component-manager/src/main/java/org/sakaiproject/component/impl/ComponentManagerShim.java
Constructor and getApplicationContext() return type changed from SharedApplicationContext to ConfigurableApplicationContext; field updated accordingly.
ServerConfigurationService to ConfiguredContext Migration
kernel/api/src/main/java/org/sakaiproject/hibernate/AssignableUUIDGenerator.java, kernel/api/src/main/java/org/sakaiproject/springframework/orm/hibernate/SakaiPersistenceUnitManager.java, kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/IgniteConfigurationAdapter.java, kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/IgniteEntityManagerFactoryBean.java, kernel/kernel-impl/src/main/webapp/WEB-INF/db-components.xml, kernel/kernel-impl/src/main/webapp/WEB-INF/ignite-components.xml
Replaces ServerConfigurationService with ConfiguredContext across UUID generation, persistence unit management, and Ignite configuration. AssignableUUIDGenerator simplifies array-based entity checks. IgniteConfigurationAdapter adds instanceIndex property, path-based home directory configuration, and error handling. IgniteEntityManagerFactoryBean parameter type changed to EagerIgniteSpringBean with ensureStarted() invocation.
Ignite Spring Integration
kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/EagerIgniteSpringBean.java
Removes InitializingBean and SmartInitializingSingleton implementations. Introduces started flag and ensureStarted() method for guarded singleton startup. afterSingletonsInstantiated() delegates to ensureStarted().
SakaiProperties Protected Field Access
kernel/component-manager/src/main/java/org/sakaiproject/util/SakaiProperties.java
propertyOverrideConfigurer and propertyPlaceholderConfigurer fields visibility changed from private to protected for subclass access.
Test Infrastructure - Modi Framework
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiContextLoader.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiAnnotationLoader.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiAnnotationWebLoader.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlLoader.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlWebLoader.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebContextLoader.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiTest.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebTest.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiInitializer.java, kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiPortalConfig.java
Introduces comprehensive Modi test framework with custom context loaders (XML, annotation, web variants), base test classes, application context initializer, and portal configuration. GlobalApplicationContext registration integrated at context loading. Dynamic ignite instance index generation via DynamicPropertySource. Hierarchical context configuration for kernel and portal layers.
Test Helper Module
kernel/test-helper/pom.xml, kernel/test-helper/src/assembly/helper-jar.xml, kernel/test-helper/src/main/resources/log4j.properties, kernel/test-helper/src/main/resources/org/sakaiproject/config/test-configuration.xml, kernel/test-helper/src/main/resources/org/sakaiproject/config/test.properties, kernel/pom.xml
New test-helper Maven module added as kernel submodule with comprehensive test dependencies (kernel, spring, hibernate, ignite, commons libraries). Assembly descriptor unpacks transitive dependencies into single JAR. Test configuration extends DefaultSakaiProperties with test.properties. HSQLDB database defaults configured for in-memory testing.
Kernel Test Dependencies
kernel/kernel-test/pom.xml
Added spring-context-support, spring-web, spring-webmvc, and sakai-portal-util dependencies for test framework support.
ResourceLoader Simplification
kernel/kernel-util/src/main/java/org/sakaiproject/util/ResourceLoader.java
Removed cached lazy initialization and static locking for SessionManager, PreferencesService, MessageBundleService, and ThreadLocalManager. Methods now directly invoke ComponentManager.get(). ThreadLocalManager changed from static to non-static.
NumberUtil Parsing Helpers
kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java
Added public static methods toInteger(String) returning Optional\<Integer\>, toInteger(String, Integer) with default, toLong(String) returning Optional\<Long\>, and toLong(String, Long) with default.
Announcement Tool - Configuration & Testing
announcement/announcement-impl/impl/pom.xml, announcement/announcement-impl/impl/src/webapp/WEB-INF/components.xml, announcement/announcement-tool/tool/pom.xml, announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/AnncTestBeanConfig.java, announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/ContextSetupTest.java, announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/KernelIntegrityTest.java, announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/MockFactory.java, announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/WebContextSetupTest.java, announcement/announcement-tool/tool/src/test/resources/mocks.xml
announcement-impl adds build-helper-maven-plugin to attach WAR as JAR artifact; resource duplication for WEB-INF migration. Components.xml enables context annotation-config and component scanning for org.sakaiproject.announcement.api. Announcement-tool adds test dependencies (assertj-core, javax.inject, scheduler-api, lti-api, sakai-test-helper, sakai-announcement-impl). Test configuration class, context setup tests, kernel integrity tests, mock factory, web context integration tests, and mock beans XML added for comprehensive test coverage. ⚠️ Review focus: WebContextSetupTest complexity with MockMvc, servlet filters, and tool routing.
Assignment Conversion & Configuration
assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentConversionTest.java, assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentTestConfiguration.java
Replaces ServerConfigurationService with ConfiguredContext in test setup. AssignableUUIDGenerator initialization uses setConfiguredContext(). New ConfiguredContext mock bean added to test configuration.
Portal Tool Refactoring
portal/portal-tool/tool/pom.xml, portal/portal-tool/tool/src/java/org/sakaiproject/portal/tool/ToolPortal.java, portal/portal-util/util/src/java/org/sakaiproject/portal/util/ToolPortalServlet.java
ToolPortal simplified to extend ToolPortalServlet with constructor-based ComponentManager injection. New ToolPortalServlet base class handles tool routing, site placement resolution, access control checks, tool session management, and request forwarding. Supports dynamic tool path resolution and last-destination caching. javax.inject dependency added to portal-tool. ⚠️ Review focus: Tool routing security checks and session handling logic.
Kernel Components Configuration
kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml
Removed org.sakaiproject.announcement.api from component-scan base-packages. Maintains other scanned packages for messaging, profile2, commons, and lessonbuildertool.
Test Suite Updates
kernel/component-manager/src/test/java/org/sakaiproject/modi/EnvironmentTest.java, kernel/component-manager/src/test/java/org/sakaiproject/modi/LauncherTest.java, kernel/component-manager/src/test/java/org/sakaiproject/modi/SakaiStartListenerTest.java, kernel/kernel-impl/src/test/java/org/sakaiproject/messaging/impl/RuntimeRegistrationTest.java
EnvironmentTest reverses read-only validation logic for critical test setup. LauncherTest refactored to use globalContext() helper with ConfigurableApplicationContext. SakaiStartListenerTest updated to use ConfigurableApplicationContext. RuntimeRegistrationTest adds comment and formatting adjustments (minor).
Samigo QTI
samigo/samigo-qti/pom.xml
Added org.springframework:spring-context dependency to samigo-qti module.

Suggested reviewers

  • ern
  • kunaljaykam
  • i-a-m-s-k

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'SAK-43201 - Better Acceptance Tests' is specific and directly relates to the main objective: enabling better acceptance tests by overhauling the kernel to support standalone booting with standard Spring context, context hierarchy, and tests that exercise the real kernel without mocks.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (13)
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/AnncTestBeanConfig.java-10-12 (1)

10-12: Missing @Configuration annotation.

For Spring to properly recognize this as a configuration class and process the @ComponentScan, you typically need to add @Configuration. Without it, the component scanning may not be activated when this class is used as a configuration source.

🔎 Proposed fix
+import org.springframework.context.annotation.Configuration;
+
 /**
  * This is here only to set up component scanning...
  */
+@Configuration
 @ComponentScan(basePackages = "org.sakaiproject.announcement.tool")
 public class AnncTestBeanConfig {
 }
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlWebLoader.java-5-5 (1)

5-5: Remove unused import.

The import org.springframework.context.support.GenericApplicationContext is not used in this class.

🔎 Proposed fix
 import org.sakaiproject.modi.GlobalApplicationContext;
 import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.support.GenericApplicationContext;
 import org.springframework.test.context.MergedContextConfiguration;
 import org.springframework.test.context.web.GenericXmlWebContextLoader;
 import org.springframework.web.context.WebApplicationContext;
kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java-153-159 (1)

153-159: Handle null input to avoid NullPointerException.

Integer.valueOf(value) throws NullPointerException when value is null, which isn't caught by the NumberFormatException handler. Consider returning Optional.empty() for null input to match the expected behavior of a safe parsing method.

🔎 Proposed fix
 public static Optional<Integer> toInteger(final String value) {
+    if (value == null) {
+        return Optional.empty();
+    }
     try {
         return Optional.of(Integer.valueOf(value));
     } catch (final NumberFormatException nfe) {
         return Optional.empty();
     }
 }
kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java-201-207 (1)

201-207: Handle null input for toLong default-value variant.

Same null-safety issue as the other methods.

🔎 Proposed fix
 public static Long toLong(final String value, final Long defaultValue) {
+    if (value == null) {
+        return defaultValue;
+    }
     try {
         return Long.valueOf(value);
     } catch (final NumberFormatException e) {
         return defaultValue;
     }
 }
kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java-185-191 (1)

185-191: Handle null input for toLong Optional variant.

Same null-safety issue as toInteger.

🔎 Proposed fix
 public static Optional<Long> toLong(final String value) {
+    if (value == null) {
+        return Optional.empty();
+    }
     try {
         return Optional.of(Long.valueOf(value));
     } catch (final NumberFormatException nfe) {
         return Optional.empty();
     }
 }
kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java-169-175 (1)

169-175: Handle null input for the default-value variant.

Similar to the Optional variant, null input causes NullPointerException. Return defaultValue when input is null.

🔎 Proposed fix
 public static Integer toInteger(final String value, final Integer defaultValue) {
+    if (value == null) {
+        return defaultValue;
+    }
     try {
         return Integer.valueOf(value);
     } catch (final NumberFormatException nfe) {
         return defaultValue;
     }
 }
kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml-13-14 (1)

13-14: Remove org.sakaiproject.messaging.api.testhandler from production component scan.

The testhandler package is located in src/test and contains test-only components (e.g., TestUserNotificationHandler). These should not be scanned in the production components.xml. Either move this configuration to a test-specific Spring context or remove the package from the scan list.

Note: The removal of org.sakaiproject.announcement.api is not problematic—AnnouncementsUserNotificationHandler is properly discovered through the announcement module's own component-scan configuration in announcement/announcement-impl/impl/src/webapp/WEB-INF/components.xml.

kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebTest.java-12-16 (1)

12-16: Include initializers = ModiInitializer.class in the kernel context configuration.

Unlike locations (which subclasses are designed to provide), the initializers attribute should be carried forward. The parent class ModiTest specifies initializers = ModiInitializer.class, which sets critical system properties (sakai.home and sakai.modi.enabled). Since ModiWebTest uses @ContextHierarchy to replace the parent's configuration rather than extend it, these initializers are lost. Add initializers = ModiInitializer.class to the kernel context in ModiWebTest to match the parent's behavior.

kernel/component-manager/src/main/java/org/sakaiproject/modi/ModiSakaiProperties.java-55-64 (1)

55-64: Fix Javadoc @link syntax error.

The @link reference on line 57 is missing the closing brace, which will cause Javadoc generation warnings.

🔎 Proposed fix
     /**
      * The base class yields its override and placeholder processors for registration. We simply delegate to them in
-     * {@link #postProcessBeanFactory(ConfigurableListableBeanFactory).
+     * {@link #postProcessBeanFactory(ConfigurableListableBeanFactory)}.
      *
      * @return an empty list, always
      */
kernel/api/src/main/java/org/sakaiproject/hibernate/AssignableUUIDGenerator.java-47-54 (1)

47-54: Replace var with explicit type declaration.

Line 48 uses local variable type inference (var), which violates the project's coding guidelines. As per the guidelines, Java code should always declare explicit types.

🔎 Proposed fix
     @Override
     public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
         if (configuredContext != null) {
-            var classes = configuredContext.getStrings(HIBERNATE_ASSIGNABLE_ID_CLASSES);
+            Collection<String> classes = configuredContext.getStrings(HIBERNATE_ASSIGNABLE_ID_CLASSES);
             String entityName = object.getClass().getName();
             if (classes.contains(entityName)) {
                 final Serializable id = session.getEntityPersister(entityName, object).getIdentifier(object, session);
                 if (id != null) return id;
             }
         }
         return super.generate(session, object);
     }

You'll also need to add the import:

import java.util.Collection;
kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/IgniteConfigurationAdapter.java-286-297 (1)

286-297: Replace var with explicit types per coding guidelines.

Lines 287-288 use local variable type inference (var), which violates the project's coding guidelines. The guidelines explicitly state: "Never use local variable type inference (var). Always declare explicit types."

🔎 Proposed fix
     private void configureHome() {
-        var base = Path.of(home);
-        var igniteHome = StringUtils.isBlank(node) ? base : base.resolve(node);
+        Path base = Path.of(home);
+        Path igniteHome = StringUtils.isBlank(node) ? base : base.resolve(node);
 
         try {
             Files.createDirectories(igniteHome);
             if (!Files.isWritable(igniteHome)) throw cannotWriteToIgniteDirectory(igniteHome);
             home = igniteHome.toAbsolutePath().toString();
         } catch (IOException e) {
             throw cannotCreateIgniteDirectory(igniteHome);
         }
     }

As per coding guidelines, var usage is non-compliant and should be replaced with explicit types before merge.

kernel/kernel-impl/src/main/java/org/sakaiproject/component/impl/ConfiguredContextImpl.java-101-107 (1)

101-107: Replace var with explicit types per coding guidelines.

Lines 102 and 105 use local variable type inference (var), which violates the project's coding guidelines.

🔎 Proposed fix
     Optional<List<String>> enumeratedStrings(String name) {
-        var count = getInt(name + ".count").filter(n -> n > 0);
+        Optional<Integer> count = getInt(name + ".count").filter(n -> n > 0);
         if (count.isEmpty()) return Optional.empty();
 
-        var keys = IntStream.rangeClosed(1, count.get()).mapToObj(i -> name + "." + i);
+        Stream<String> keys = IntStream.rangeClosed(1, count.get()).mapToObj(i -> name + "." + i);
         return Optional.of(keys.map(key -> getString(key, "")).toList());
     }

Note: You'll need to add import java.util.stream.Stream;

As per coding guidelines, var usage is non-compliant and should be replaced with explicit types before merge.

Committable suggestion skipped: line range outside the PR's diff.

kernel/test-helper/pom.xml-112-122 (1)

112-122: Hardcoded versions diverging from parent BOM; missing scope on reactor-core.

Both reactive-streams and reactor-core have hardcoded versions that differ from the parent BOM (master/pom.xml). Additionally, reactor-core is missing an explicit <scope> declaration. Removing these versions will inherit the managed versions from the parent (reactive-streams 1.0.4 and reactor-core 3.5.19 with scope provided).

     <dependency>
       <groupId>org.reactivestreams</groupId>
       <artifactId>reactive-streams</artifactId>
-      <version>1.0.3</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>io.projectreactor</groupId>
       <artifactId>reactor-core</artifactId>
-      <version>3.4.18</version>
+      <scope>compile</scope>
     </dependency>
🧹 Nitpick comments (19)
kernel/kernel-impl/src/main/java/org/sakaiproject/memory/impl/EhcacheMemoryService.java (1)

92-96: Consider adding a null-guard before shutdown.

If destroy() is called before init() completes (or if called twice), cacheManager could be null, causing an NPE. The previous implementation likely had defensive checks.

🔎 Proposed fix
 public void destroy() {
+    if (cacheManager == null) {
+        log.warn("CacheManager already null during destroy");
+        return;
+    }
     cacheManager.shutdown();
     cacheManager = null; // release
     log.info("SHUTDOWN");
 }
kernel/component-manager/src/main/java/org/sakaiproject/modi/Launcher.java (2)

70-71: Update outdated Javadoc reference.

The Javadoc mentions SharedApplicationContext but the implementation now uses GenericApplicationContext. Consider updating for consistency.

🔎 Proposed fix
-     * The {@link SharedApplicationContext} is both refreshed (loaded) and started (beans created/wired). We make no
+     * The {@link GenericApplicationContext} is both refreshed (loaded) and started (beans created/wired). We make no

98-105: Consider null-guard for context in stop().

If stop() is called before start() completes (e.g., during startup failure), context could be null, causing an NPE. A null check similar to the one for components would improve robustness.

🔎 Proposed fix
 public void stop() {
     log.info("Stopping Sakai components");
     if (components != null) {
         components.stopping();
     }
-    context.stop();
-    context.close();
+    if (context != null) {
+        context.stop();
+        context.close();
+    }
 }
portal/portal-tool/tool/src/java/org/sakaiproject/portal/tool/ToolPortal.java (1)

31-31: Consider qualifying the static import for clarity.

The static import of get makes call sites like get(ActiveToolManager.class) less immediately readable. While this works, fully qualifying as ComponentManager.get(...) would make the source clearer.

That said, given this is draft PR with transitional wiring and the description notes removing static covers as remaining work, this is acceptable for now.

portal/portal-util/util/src/java/org/sakaiproject/portal/util/ToolPortalServlet.java (1)

150-160: Consider enhancing error handling for permission exceptions.

When a PermissionException occurs during site visit, the method returns false (likely resulting in a 404), but the TODO comment suggests a login redirect might be more appropriate for user experience. This would guide unauthorized users to authenticate rather than showing a not-found error.

announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/MockFactory.java (1)

7-9: Make the factory method public for clarity.

Since this method is invoked via Spring's factory-method mechanism (as seen in mocks.xml), it should be explicitly declared public to clearly express its intended usage, even though package-private access works when the Spring context is in the same package.

🔎 Proposed change
-    public LTIService ltiService() {
+    public LTIService ltiService() {

Wait, it's already public. Let me re-check the annotated code...

Actually, looking at the code again:

public class MockFactory {
    public LTIService ltiService() {

The method IS already public. My mistake. Let me approve this instead.

announcement/announcement-tool/tool/pom.xml (1)

79-84: Consider using dependency management for assertj-core.

The assertj-core dependency has a hardcoded version (3.23.1) while other test dependencies rely on the parent POM's dependency management. For consistency and easier maintenance, consider managing this version centrally in the parent POM's <dependencyManagement> section.

🔎 Proposed refactor
         <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
-            <version>3.23.1</version>
             <scope>test</scope>
         </dependency>

Then add to the parent POM's dependencyManagement section:

<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.23.1</version>
</dependency>
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiInitializer.java (1)

12-12: Use proper path separator handling.

The manual string concatenation + "/" for adding a trailing slash is platform-dependent and fragile. Consider using File.separator or Path's built-in methods for better cross-platform compatibility.

🔎 Proposed fix using Path resolution
-        System.setProperty("sakai.home", Path.of("target/sakai-home").toAbsolutePath().toString() + "/");
+        System.setProperty("sakai.home", Path.of("target", "sakai-home", "").toAbsolutePath().toString());

Or alternatively, ensure trailing separator explicitly:

-        System.setProperty("sakai.home", Path.of("target/sakai-home").toAbsolutePath().toString() + "/");
+        System.setProperty("sakai.home", Path.of("target/sakai-home").toAbsolutePath().toString() + File.separator);
admin-tools/src/java/org/sakaiproject/memory/tool/MemoryAction.java (1)

49-131: LGTM! Improved dependency management.

The refactoring from MemoryServiceLocator.getInstance() to injected memoryService field improves testability and aligns with the PR's goal of reducing problematic static/cached resource usage. All four call sites have been updated correctly.

As a future enhancement, consider replacing ComponentManager.get() calls in the constructor with proper Spring constructor injection (though this would require broader refactoring of the Velocity tool infrastructure).

kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiContextLoader.java (2)

14-21: Minor naming inconsistency for clarity.

The field annotationLoader could be renamed to annotationConfigLoader for consistency with the method name getAnnotationConfigLoader() and the naming in the parent class.

🔎 Proposed fix
 public class ModiContextLoader extends DelegatingSmartContextLoader {
     private final SmartContextLoader xmlLoader;
-    private final SmartContextLoader annotationLoader;
+    private final SmartContextLoader annotationConfigLoader;

     public ModiContextLoader() {
         xmlLoader = new ModiXmlLoader();
-        annotationLoader = new ModiAnnotationLoader();
+        annotationConfigLoader = new ModiAnnotationLoader();
     }

35-38: Update the return expression to match the renamed field.

If you apply the naming consistency fix above, update this method accordingly.

🔎 Proposed fix (if field is renamed)
     @Override
     protected SmartContextLoader getAnnotationConfigLoader() {
-        return annotationLoader;
+        return annotationConfigLoader;
     }
kernel/component-manager/src/main/bundle/org/sakaiproject/config/modi-configuration.xml (1)

2-5: Add util schema location for XML validation.

The util namespace is declared but its schema location is missing. This may cause XML validation warnings in IDEs and build tools.

🔎 Proposed fix
 <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:util="http://www.springframework.org/schema/util"
-    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+                        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

Alternatively, if the util namespace is not actually used in this file, consider removing the declaration from line 4.

kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebContextLoader.java (1)

11-18: Same naming inconsistency as ModiContextLoader.

For consistency with the method name getAnnotationConfigLoader(), consider renaming annotationLoader to annotationConfigLoader.

🔎 Proposed fix
 public class ModiWebContextLoader extends WebDelegatingSmartContextLoader {
     private final SmartContextLoader xmlLoader;
-    private final SmartContextLoader annotationLoader;
+    private final SmartContextLoader annotationConfigLoader;

     public ModiWebContextLoader() {
         xmlLoader = new ModiXmlWebLoader();
-        annotationLoader = new ModiAnnotationWebLoader();
+        annotationConfigLoader = new ModiAnnotationWebLoader();
     }
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/KernelIntegrityTest.java (1)

40-99: Consider extracting the repeated SecurityAdvisor pattern.

The same anonymous SecurityAdvisor implementation is duplicated in all four test methods. This could be extracted to a field or helper method for better maintainability.

I notice that tests _3_ and _4_ are intentionally interdependent (demonstrating that state persists across tests). If this is meant to illustrate the problem with shared state (as the test name suggests), consider adding a class-level comment documenting this intent for future maintainers.

🔎 Proposed refactor to reduce duplication
 public class KernelIntegrityTest extends ModiTest {
+    private final SecurityAdvisor allowAllAdvisor = (userId, function, reference) -> SecurityAdvice.ALLOWED;
+
     @Inject
     public SiteService siteService;
 
     @Inject
     public SecurityService securityService;
 
     @Inject
     public ApplicationContext context;
 
     @Test
     public void _1_givenAModiKernel_thenWeCanRunRealIntegrationTests() throws IdUnusedException {
-        SecurityAdvisor advisor = new SecurityAdvisor() {
-            public SecurityAdvice isAllowed(String userId, String function, String reference) {
-                return SecurityAdvice.ALLOWED;
-            }
-        };
-
-        securityService.pushAdvisor(advisor);
+        securityService.pushAdvisor(allowAllAdvisor);
         String siteTitle = siteService.getSite("!admin").getTitle();
-        securityService.popAdvisor(advisor);
+        securityService.popAdvisor(allowAllAdvisor);
 
         assertThat(siteTitle).isEqualTo("Administration Workspace");
     }
assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentConversionTest.java (1)

32-32: Wildcard import detected.

Using java.util.* is generally discouraged in favor of explicit imports for better readability and to avoid potential naming conflicts. Consider expanding to explicit imports.

announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/WebContextSetupTest.java (1)

87-103: Consider extracting session setup to reduce duplication.

The session setup pattern (start session, set user, set current session, create cookie) is repeated identically in all three test methods. Consider extracting this to a helper method or a @Before setup to improve maintainability.

🔎 Example helper method
private Cookie setupAdminSession() {
    Session session = sessionManager.startSession();
    session.setUserEid("admin");
    session.setUserId("admin");
    sessionManager.setCurrentSession(session);
    String cookieValue = session.getId() + "." + System.getProperty(RequestFilter.SAKAI_SERVERID, "sakai");
    return new Cookie("JSESSIONID", cookieValue);
}
kernel/test-helper/pom.xml (3)

30-35: Inconsistent version specification across Sakai dependencies.

sakai-kernel-impl and scoring-service-api explicitly specify ${project.version} while other Sakai dependencies inherit versions from the parent BOM. For consistency, consider removing the explicit version declarations if the parent BOM already manages these versions.

🔎 Suggested fix
     <dependency>
       <groupId>org.sakaiproject.kernel</groupId>
       <artifactId>sakai-kernel-impl</artifactId>
-      <version>${project.version}</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.sakaiproject.edu-services.scoringservice</groupId>
       <artifactId>scoring-service-api</artifactId>
-      <version>${project.version}</version>
       <scope>compile</scope>
     </dependency>

Also applies to: 56-61


181-185: Consider upgrade path for deprecated ehcache-core.

net.sf.ehcache:ehcache-core is in maintenance mode and superseded by org.ehcache:ehcache (3.x). If this dependency is required for Hibernate 2nd-level cache compatibility with the existing Sakai stack, this is acceptable. Otherwise, consider migrating to Ehcache 3.x or the Ignite-based caching that appears to be used elsewhere.


134-143: Remove deprecated commons-lang dependency.

commons-lang (Apache Commons Lang 2.x) is deprecated and superseded by commons-lang3. The deprecated package is not used anywhere in the kernel codebase, making it safe to remove.

🔎 Suggested fix
-    <dependency>
-      <groupId>commons-lang</groupId>
-      <artifactId>commons-lang</artifactId>
-      <scope>compile</scope>
-    </dependency>
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
       <scope>compile</scope>
     </dependency>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69a94ff and f2e2444.

📒 Files selected for processing (62)
  • admin-tools/src/java/org/sakaiproject/memory/tool/MemoryAction.java
  • announcement/announcement-impl/impl/pom.xml
  • announcement/announcement-impl/impl/src/webapp/WEB-INF/components.xml
  • announcement/announcement-tool/tool/pom.xml
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/AnncTestBeanConfig.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/ContextSetupTest.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/KernelIntegrityTest.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/MockFactory.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/WebContextSetupTest.java
  • announcement/announcement-tool/tool/src/test/resources/mocks.xml
  • assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentConversionTest.java
  • assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentTestConfiguration.java
  • kernel/api/src/main/java/org/sakaiproject/hibernate/AssignableUUIDGenerator.java
  • kernel/api/src/main/java/org/sakaiproject/memory/cover/MemoryServiceLocator.java
  • kernel/api/src/main/java/org/sakaiproject/springframework/orm/hibernate/SakaiPersistenceUnitManager.java
  • kernel/component-manager/src/main/bundle/org/sakaiproject/config/modi-configuration.xml
  • kernel/component-manager/src/main/java/org/sakaiproject/component/api/ConfiguredContext.java
  • kernel/component-manager/src/main/java/org/sakaiproject/component/impl/ComponentManagerShim.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/ComponentsDirectory.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/Configuration.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/Launcher.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/ModiSakaiProperties.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/SysProp.java
  • kernel/component-manager/src/main/java/org/sakaiproject/util/SakaiProperties.java
  • kernel/component-manager/src/test/java/org/sakaiproject/modi/EnvironmentTest.java
  • kernel/component-manager/src/test/java/org/sakaiproject/modi/LauncherTest.java
  • kernel/component-manager/src/test/java/org/sakaiproject/modi/SakaiStartListenerTest.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/component/impl/ConfiguredContextImpl.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/EagerIgniteSpringBean.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/IgniteConfigurationAdapter.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/IgniteEntityManagerFactoryBean.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/memory/impl/EhcacheMemoryService.java
  • kernel/kernel-impl/src/main/webapp/WEB-INF/component-components.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/db-components.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/ignite-components.xml
  • kernel/kernel-impl/src/test/java/org/sakaiproject/messaging/impl/RuntimeRegistrationTest.java
  • kernel/kernel-storage-util/src/main/java/org/sakaiproject/util/BaseDbFlatStorage.java
  • kernel/kernel-test/pom.xml
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiAnnotationLoader.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiAnnotationWebLoader.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiContextLoader.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiInitializer.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiPortalConfig.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiTest.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebContextLoader.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebTest.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlLoader.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlWebLoader.java
  • kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java
  • kernel/kernel-util/src/main/java/org/sakaiproject/util/ResourceLoader.java
  • kernel/pom.xml
  • kernel/test-helper/pom.xml
  • kernel/test-helper/src/assembly/helper-jar.xml
  • kernel/test-helper/src/main/resources/log4j.properties
  • kernel/test-helper/src/main/resources/org/sakaiproject/config/test-configuration.xml
  • kernel/test-helper/src/main/resources/org/sakaiproject/config/test.properties
  • portal/portal-tool/tool/pom.xml
  • portal/portal-tool/tool/src/java/org/sakaiproject/portal/tool/ToolPortal.java
  • portal/portal-util/util/src/java/org/sakaiproject/portal/util/ToolPortalServlet.java
  • samigo/samigo-qti/pom.xml
🧰 Additional context used
📓 Path-based instructions (5)
**/*.java

📄 CodeRabbit inference engine (.cursor/rules/logging-rule.mdc)

**/*.java: Use SLF4J parameterized logging (logger.info("Value is: {}", value)) instead of string concatenation (logger.info("Value is: " + value))
Log messages and code comments should be in English. Log messages should never be translated.

**/*.java: Java: Never use local variable type inference (var). Always declare explicit types. Yes: Map<String, Integer> counts = new HashMap<>(); No: var counts = new HashMap<String, Integer>();
When proposing Java code, spell out full types in local variable declarations, for loops, and try-with-resources
When editing Java, prefer clarity over brevity; avoid introducing language features that aren't widely used in the repo
Treat any PR or suggestion containing Java var as non-compliant. Recommend replacing with explicit types before merge

**/*.java: Use Java 17 for trunk development (Java 11 was used for Sakai 22 and Sakai 23)
Do not use local variable type inference (var) in Java code. Always declare explicit types (e.g., List<String> names = new ArrayList<>(); not var names = new ArrayList<String>();). Enforced by Checkstyle rule during mvn validate

Files:

  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/MockFactory.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebTest.java
  • kernel/component-manager/src/main/java/org/sakaiproject/util/SakaiProperties.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiInitializer.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebContextLoader.java
  • kernel/component-manager/src/test/java/org/sakaiproject/modi/EnvironmentTest.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiContextLoader.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/WebContextSetupTest.java
  • kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/AnncTestBeanConfig.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/Launcher.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/ModiSakaiProperties.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiAnnotationLoader.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiPortalConfig.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/KernelIntegrityTest.java
  • kernel/component-manager/src/main/java/org/sakaiproject/component/api/ConfiguredContext.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlWebLoader.java
  • kernel/kernel-impl/src/test/java/org/sakaiproject/messaging/impl/RuntimeRegistrationTest.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/ComponentsDirectory.java
  • admin-tools/src/java/org/sakaiproject/memory/tool/MemoryAction.java
  • kernel/kernel-storage-util/src/main/java/org/sakaiproject/util/BaseDbFlatStorage.java
  • kernel/api/src/main/java/org/sakaiproject/hibernate/AssignableUUIDGenerator.java
  • kernel/component-manager/src/main/java/org/sakaiproject/component/impl/ComponentManagerShim.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiAnnotationWebLoader.java
  • kernel/api/src/main/java/org/sakaiproject/springframework/orm/hibernate/SakaiPersistenceUnitManager.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiTest.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/SysProp.java
  • assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentConversionTest.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/memory/impl/EhcacheMemoryService.java
  • kernel/component-manager/src/test/java/org/sakaiproject/modi/LauncherTest.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/IgniteConfigurationAdapter.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java
  • kernel/component-manager/src/test/java/org/sakaiproject/modi/SakaiStartListenerTest.java
  • portal/portal-util/util/src/java/org/sakaiproject/portal/util/ToolPortalServlet.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/IgniteEntityManagerFactoryBean.java
  • portal/portal-tool/tool/src/java/org/sakaiproject/portal/tool/ToolPortal.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlLoader.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/ContextSetupTest.java
  • kernel/component-manager/src/main/java/org/sakaiproject/modi/Configuration.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/component/impl/ConfiguredContextImpl.java
  • kernel/kernel-util/src/main/java/org/sakaiproject/util/ResourceLoader.java
  • kernel/api/src/main/java/org/sakaiproject/memory/cover/MemoryServiceLocator.java
  • kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/EagerIgniteSpringBean.java
  • assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentTestConfiguration.java
**/*.{html,jsp,vm,xhtml,xml}

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

**/*.{html,jsp,vm,xhtml,xml}: Use Bootstrap 5.2 as the preferred UI framework for styling in Sakai frontends
Leverage Bootstrap 5 components for consistent UI/UX

Files:

  • kernel/kernel-impl/src/main/webapp/WEB-INF/component-components.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/ignite-components.xml
  • kernel/pom.xml
  • portal/portal-tool/tool/pom.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/db-components.xml
  • announcement/announcement-tool/tool/src/test/resources/mocks.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml
  • samigo/samigo-qti/pom.xml
  • announcement/announcement-impl/impl/src/webapp/WEB-INF/components.xml
  • kernel/kernel-test/pom.xml
  • kernel/test-helper/src/main/resources/org/sakaiproject/config/test-configuration.xml
  • kernel/component-manager/src/main/bundle/org/sakaiproject/config/modi-configuration.xml
  • announcement/announcement-impl/impl/pom.xml
  • kernel/test-helper/src/assembly/helper-jar.xml
  • announcement/announcement-tool/tool/pom.xml
  • kernel/test-helper/pom.xml
**/*.{html,jsp,vm,xhtml,xml,css,scss}

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

Ensure all UI components work across different screen sizes (Responsive Design)

Files:

  • kernel/kernel-impl/src/main/webapp/WEB-INF/component-components.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/ignite-components.xml
  • kernel/pom.xml
  • portal/portal-tool/tool/pom.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/db-components.xml
  • announcement/announcement-tool/tool/src/test/resources/mocks.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml
  • samigo/samigo-qti/pom.xml
  • announcement/announcement-impl/impl/src/webapp/WEB-INF/components.xml
  • kernel/kernel-test/pom.xml
  • kernel/test-helper/src/main/resources/org/sakaiproject/config/test-configuration.xml
  • kernel/component-manager/src/main/bundle/org/sakaiproject/config/modi-configuration.xml
  • announcement/announcement-impl/impl/pom.xml
  • kernel/test-helper/src/assembly/helper-jar.xml
  • announcement/announcement-tool/tool/pom.xml
  • kernel/test-helper/pom.xml
**/*.{js,html,jsp,vm,xhtml,xml}

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

Use the web components in the webcomponents/ directory when possible in Sakai frontends

Files:

  • kernel/kernel-impl/src/main/webapp/WEB-INF/component-components.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/ignite-components.xml
  • kernel/pom.xml
  • portal/portal-tool/tool/pom.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/db-components.xml
  • announcement/announcement-tool/tool/src/test/resources/mocks.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml
  • samigo/samigo-qti/pom.xml
  • announcement/announcement-impl/impl/src/webapp/WEB-INF/components.xml
  • kernel/kernel-test/pom.xml
  • kernel/test-helper/src/main/resources/org/sakaiproject/config/test-configuration.xml
  • kernel/component-manager/src/main/bundle/org/sakaiproject/config/modi-configuration.xml
  • announcement/announcement-impl/impl/pom.xml
  • kernel/test-helper/src/assembly/helper-jar.xml
  • announcement/announcement-tool/tool/pom.xml
  • kernel/test-helper/pom.xml
**/*.{html,jsp,jspx,xml,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer kebab-case for values of HTML class and id attributes

Files:

  • kernel/kernel-impl/src/main/webapp/WEB-INF/component-components.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/ignite-components.xml
  • kernel/pom.xml
  • portal/portal-tool/tool/pom.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/db-components.xml
  • announcement/announcement-tool/tool/src/test/resources/mocks.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml
  • samigo/samigo-qti/pom.xml
  • announcement/announcement-impl/impl/src/webapp/WEB-INF/components.xml
  • kernel/kernel-test/pom.xml
  • kernel/test-helper/src/main/resources/org/sakaiproject/config/test-configuration.xml
  • kernel/component-manager/src/main/bundle/org/sakaiproject/config/modi-configuration.xml
  • announcement/announcement-impl/impl/pom.xml
  • kernel/test-helper/src/assembly/helper-jar.xml
  • announcement/announcement-tool/tool/pom.xml
  • kernel/test-helper/pom.xml
🧠 Learnings (11)
📚 Learning: 2025-10-10T15:13:08.361Z
Learnt from: ern
Repo: sakaiproject/sakai PR: 14152
File: reset-pass/account-validator-impl/src/test/java/org/sakaiproject/accountvalidator/test/AccountValidationTestConfiguration.java:69-72
Timestamp: 2025-10-10T15:13:08.361Z
Learning: In the Sakai project, test-specific mock configurations should remain in the test class itself as part of the test logic. Only mock configurations that are common across multiple test classes should be centralized in test configuration classes.

Applied to files:

  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/MockFactory.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebTest.java
  • announcement/announcement-tool/tool/src/test/resources/mocks.xml
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiPortalConfig.java
  • kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiTest.java
  • kernel/test-helper/src/main/resources/org/sakaiproject/config/test-configuration.xml
  • kernel/component-manager/src/main/bundle/org/sakaiproject/config/modi-configuration.xml
  • assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentConversionTest.java
  • announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/ContextSetupTest.java
  • announcement/announcement-tool/tool/pom.xml
  • assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentTestConfiguration.java
📚 Learning: 2025-11-24T19:11:31.821Z
Learnt from: CR
Repo: sakaiproject/sakai PR: 0
File: .cursor/rules/frontend.mdc:0-0
Timestamp: 2025-11-24T19:11:31.821Z
Learning: Applies to **/*.{js,html,jsp,vm,xhtml,xml} : Use the web components in the webcomponents/ directory when possible in Sakai frontends

Applied to files:

  • kernel/pom.xml
  • announcement/announcement-impl/impl/pom.xml
📚 Learning: 2025-11-24T19:12:20.537Z
Learnt from: CR
Repo: sakaiproject/sakai PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:12:20.537Z
Learning: Applies to **/*.java : Use Spring for dependency injection, MVC, and services in Java development

Applied to files:

  • portal/portal-tool/tool/pom.xml
  • kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml
  • samigo/samigo-qti/pom.xml
  • kernel/kernel-test/pom.xml
  • announcement/announcement-tool/tool/pom.xml
📚 Learning: 2025-10-06T22:15:40.541Z
Learnt from: ottenhoff
Repo: sakaiproject/sakai PR: 14152
File: reset-pass/account-validator-api/src/main/java/org/sakaiproject/accountvalidator/model/ValidationAccount.java:24-33
Timestamp: 2025-10-06T22:15:40.541Z
Learning: The Sakai project (as of this PR) uses Hibernate 5.6.15.Final and Spring 5.3.39, which requires javax.persistence imports, not jakarta.persistence. The jakarta namespace migration will happen when the project upgrades to JDK 21, Spring 6, and Hibernate 6+.

Applied to files:

  • kernel/kernel-impl/src/main/webapp/WEB-INF/db-components.xml
  • samigo/samigo-qti/pom.xml
  • kernel/kernel-test/pom.xml
  • kernel/api/src/main/java/org/sakaiproject/hibernate/AssignableUUIDGenerator.java
  • kernel/api/src/main/java/org/sakaiproject/springframework/orm/hibernate/SakaiPersistenceUnitManager.java
  • announcement/announcement-tool/tool/pom.xml
📚 Learning: 2025-12-10T10:41:09.339Z
Learnt from: st-manu
Repo: sakaiproject/sakai PR: 14256
File: kernel/kernel-impl/src/main/sql/mysql/sakai_realm.sql:734-734
Timestamp: 2025-12-10T10:41:09.339Z
Learning: In Sakai, DB upgrade/backfill for new realm functions and grants belongs in the sakai-reference repo under docs/conversion (e.g., sakai_25_1-25_2_*_conversion.sql). For SAK-51713, assessment.all.groups is granted to maintain and Instructor and backfilled across existing realms via conversion scripts.

Applied to files:

  • kernel/kernel-impl/src/main/webapp/WEB-INF/db-components.xml
📚 Learning: 2025-11-24T19:12:20.537Z
Learnt from: CR
Repo: sakaiproject/sakai PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:12:20.537Z
Learning: Applies to **/*.java : Use Hibernate as the ORM framework for database interactions in Java

Applied to files:

  • kernel/test-helper/src/main/resources/org/sakaiproject/config/test.properties
📚 Learning: 2025-11-24T19:12:20.537Z
Learnt from: CR
Repo: sakaiproject/sakai PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:12:20.537Z
Learning: Applies to **/*.{java,properties} : Reconcile push notification subscriptions proactively and keep localization strings in `sakai-notifications.properties`

Applied to files:

  • kernel/kernel-impl/src/main/webapp/WEB-INF/components.xml
  • announcement/announcement-impl/impl/pom.xml
📚 Learning: 2025-11-24T19:12:27.663Z
Learnt from: CR
Repo: sakaiproject/sakai PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:12:27.663Z
Learning: Spring is crucial for dependency injection, MVC, and services; use Spring for new development

Applied to files:

  • samigo/samigo-qti/pom.xml
  • kernel/kernel-test/pom.xml
📚 Learning: 2025-10-07T16:35:33.991Z
Learnt from: ern
Repo: sakaiproject/sakai PR: 14152
File: reset-pass/account-validator-impl/pom.xml:37-42
Timestamp: 2025-10-07T16:35:33.991Z
Learning: The Sakai project cannot upgrade to Spring Framework 6 yet and must remain on Spring 5.x for the time being.

Applied to files:

  • samigo/samigo-qti/pom.xml
📚 Learning: 2025-09-25T13:34:06.742Z
Learnt from: susowrc
Repo: sakaiproject/sakai PR: 14105
File: assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java:1736-1745
Timestamp: 2025-09-25T13:34:06.742Z
Learning: In Sakai, each site can only have one tool placement for a given common tool ID (e.g., "sakai.assignment.grades" for the Assignment tool). The method getToolForCommonId() returns a single ToolConfiguration object, not a collection, confirming this one-to-one relationship between tools and sites.

Applied to files:

  • portal/portal-util/util/src/java/org/sakaiproject/portal/util/ToolPortalServlet.java
  • portal/portal-tool/tool/src/java/org/sakaiproject/portal/tool/ToolPortal.java
📚 Learning: 2025-11-24T19:12:27.663Z
Learnt from: CR
Repo: sakaiproject/sakai PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T19:12:27.663Z
Learning: Build the project using `mvn install`

Applied to files:

  • announcement/announcement-impl/impl/pom.xml
🧬 Code graph analysis (15)
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebTest.java (1)
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiTest.java (1)
  • ContextHierarchy (15-39)
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/WebContextSetupTest.java (3)
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/ContextSetupTest.java (1)
  • RunWith (22-53)
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiTest.java (1)
  • ContextHierarchy (15-39)
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiWebTest.java (1)
  • WebAppConfiguration (11-17)
kernel/component-manager/src/main/java/org/sakaiproject/modi/Launcher.java (1)
kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java (1)
  • GlobalApplicationContext (30-82)
kernel/component-manager/src/main/java/org/sakaiproject/modi/ModiSakaiProperties.java (1)
kernel/component-manager/src/main/java/org/sakaiproject/util/SakaiProperties.java (1)
  • Slf4j (59-409)
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiAnnotationLoader.java (1)
kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java (1)
  • GlobalApplicationContext (30-82)
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/KernelIntegrityTest.java (1)
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/ContextSetupTest.java (1)
  • RunWith (22-53)
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlWebLoader.java (1)
kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java (1)
  • GlobalApplicationContext (30-82)
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiAnnotationWebLoader.java (1)
kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java (1)
  • GlobalApplicationContext (30-82)
kernel/api/src/main/java/org/sakaiproject/springframework/orm/hibernate/SakaiPersistenceUnitManager.java (1)
kernel/api/src/main/java/org/sakaiproject/hibernate/AssignableUUIDGenerator.java (1)
  • AssignableUUIDGenerator (33-57)
assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentConversionTest.java (1)
kernel/api/src/main/java/org/sakaiproject/hibernate/AssignableUUIDGenerator.java (1)
  • AssignableUUIDGenerator (33-57)
kernel/component-manager/src/test/java/org/sakaiproject/modi/SakaiStartListenerTest.java (1)
kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java (1)
  • GlobalApplicationContext (30-82)
portal/portal-tool/tool/src/java/org/sakaiproject/portal/tool/ToolPortal.java (1)
portal/portal-util/util/src/java/org/sakaiproject/portal/util/ToolPortalServlet.java (1)
  • Slf4j (22-217)
kernel/kernel-test/src/main/java/org/sakaiproject/test/ModiXmlLoader.java (1)
kernel/component-manager/src/main/java/org/sakaiproject/modi/GlobalApplicationContext.java (1)
  • GlobalApplicationContext (30-82)
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/ContextSetupTest.java (3)
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/KernelIntegrityTest.java (1)
  • RunWith (24-100)
announcement/announcement-tool/tool/src/test/org/sakaiproject/announcement/tool/WebContextSetupTest.java (1)
  • RunWith (45-184)
samigo/samigo-app/src/webapp/js/finInputValidator.js (1)
  • context (30-30)
kernel/kernel-impl/src/main/java/org/sakaiproject/component/impl/ConfiguredContextImpl.java (1)
kernel/kernel-util/src/main/java/org/sakaiproject/util/NumberUtil.java (1)
  • NumberUtil (36-210)
🪛 GitHub Actions: Windows Build
kernel/component-manager/src/test/java/org/sakaiproject/modi/EnvironmentTest.java

[error] 116-116: IO Critical error -- could not set test directory read-only: C:\Users\RUNNER~1\AppData\Local\Temp\junit12627441752225610901\readonly


[error] 132-132: IO Critical error -- could not set test directory read-only: C:\Users\RUNNER~1\AppData\Local\Temp\junit13493837206627215482\readonly\sakai

🪛 OSV Scanner (2.3.1)
kernel/test-helper/pom.xml

[HIGH] 1-1: org.springframework:spring-core 5.3.39: Spring Framework annotation detection mechanism may result in improper authorization

(GHSA-jmp9-x22r-554x)


[CRITICAL] 1-1: org.springframework:spring-web 5.3.39: Spring Framework DataBinder Case Sensitive Match Exception

(GHSA-4gc7-5j7h-4qph)


[CRITICAL] 1-1: org.springframework:spring-web 5.3.39: Pivotal Spring Framework contains unsafe Java deserialization methods

(GHSA-4wrc-f8pq-fpqp)


[HIGH] 1-1: org.springframework:spring-webmvc 5.3.39: Path traversal vulnerability in functional web frameworks

(GHSA-cx7f-g6mp-7hqm)


[HIGH] 1-1: org.springframework:spring-webmvc 5.3.39: Spring Framework Path Traversal vulnerability

(GHSA-g5vr-rgqm-vf78)


[HIGH] 1-1: org.springframework:spring-webmvc 5.3.39: Spring Framework MVC Applications Path Traversal Vulnerability

(GHSA-r936-gwx5-v52f)


[HIGH] 1-1: org.springframework:spring-webmvc 5.3.39: Spring MVC controller vulnerable to a DoS attack

(GHSA-w3c8-7r8f-9jp8)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: sakai-deploy
  • GitHub Check: maven-build

Comment on lines +68 to +90
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>
${project.build.directory}/${project.build.finalName}/WEB-INF/lib/${project.build.finalName}.jar
</file>
<type>jar</type>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find and read the actual pom.xml file
find . -path "*announcement/announcement-impl/impl/pom.xml" -type f | head -5

Repository: sakaiproject/sakai

Length of output: 107


🏁 Script executed:

# List the directory structure to understand context
ls -la announcement/announcement-impl/impl/ 2>/dev/null | head -20

Repository: sakaiproject/sakai

Length of output: 282


🏁 Script executed:

# Check if parent pom.xml exists and read relevant sections
fd "pom.xml" | grep -E "(pom.xml$|parent|announcement)" | head -10

Repository: sakaiproject/sakai

Length of output: 301


🏁 Script executed:

# Read the pom.xml file with line numbers
cat -n ./announcement/announcement-impl/impl/pom.xml | head -120

Repository: sakaiproject/sakai

Length of output: 5391


🏁 Script executed:

# Search for parent POM reference
rg "parent\|pluginManagement" ./announcement/announcement-impl/impl/pom.xml -A 3

Repository: sakaiproject/sakai

Length of output: 44


🏁 Script executed:

# Check the parent POM to see if there's pluginManagement
cat -n ./announcement/pom.xml | head -80

Repository: sakaiproject/sakai

Length of output: 1888


🏁 Script executed:

# Search for build-helper-maven-plugin in other parts of the codebase
rg "build-helper-maven-plugin" -A 2 | head -40

Repository: sakaiproject/sakai

Length of output: 944


🏁 Script executed:

# Check the master pom.xml for pluginManagement section
cat -n ./master/pom.xml | grep -A 5 -B 5 "build-helper-maven-plugin"

Repository: sakaiproject/sakai

Length of output: 565


🏁 Script executed:

# Get more context around the pluginManagement in master/pom.xml
cat -n ./master/pom.xml | grep -A 15 "pluginManagement"

Repository: sakaiproject/sakai

Length of output: 1478


🏁 Script executed:

# Check how kernel/kernel-impl/pom.xml handles build-helper-maven-plugin
rg -A 10 "build-helper-maven-plugin" kernel/kernel-impl/pom.xml

Repository: sakaiproject/sakai

Length of output: 524


🏁 Script executed:

# Verify current version of build-helper-maven-plugin in the wild
web_search "build-helper-maven-plugin latest version 2024 2025"

Repository: sakaiproject/sakai

Length of output: 112


🌐 Web query:

What is the latest version of build-helper-maven-plugin from org.codehaus.mojo?

💡 Result:

Latest version: 3.6.1. [1][2]

Sources:
[1] mojohaus plugin info (build-helper-maven-plugin) — shows version 3.6.1.
[2] Maven Central listing for org.codehaus.mojo:build-helper-maven-plugin:3.6.1.


🏁 Script executed:

# Check if kernel/kernel-impl/pom.xml specifies version for build-helper-maven-plugin
cat -n kernel/kernel-impl/pom.xml | grep -A 15 "build-helper-maven-plugin"

Repository: sakaiproject/sakai

Length of output: 955


Remove the hardcoded version and inherit from parent POM's pluginManagement.

The build-helper-maven-plugin version 3.0.0 is hardcoded here, but the master POM already defines version 3.4.0 in its <pluginManagement> section. Remove the <version> tag from this plugin configuration to inherit the centrally managed version, consistent with how kernel/kernel-impl/pom.xml handles the same plugin.

🤖 Prompt for AI Agents
In announcement/announcement-impl/impl/pom.xml around lines 68 to 90, the
build-helper-maven-plugin has a hardcoded <version>3.0.0</version>; remove that
<version> element so the plugin inherits the version defined in the parent POM's
<pluginManagement> (3.4.0). Keep the rest of the plugin configuration intact
(groupId, artifactId, executions, configuration) and ensure
formatting/indentation remains valid XML after removing the version tag.

Comment on lines +83 to 90
public String getRaw() {
return System.getProperty(key);
}

/** Get the unchecked value of this property, with a default value; currently implemented over System properties. */
String getRaw(String def) {
public String getRaw(String def) {
return System.getProperty(key, def);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Document null-returning behavior for public API.

Now that getRaw() is public, the JavaDoc should explicitly document that it returns null when the property is not set. This is inconsistent with the Optional-based accessors above and could lead to NPEs if callers aren't aware.

Consider adding @return JavaDoc: "the property value, or null if not set".

💡 Suggested JavaDoc improvement
-    /** Get the unchecked value of this property; currently implemented over System properties. */
+    /** 
+     * Get the unchecked value of this property; currently implemented over System properties.
+     * @return the property value, or null if not set
+     */
     public String getRaw() {
         return System.getProperty(key);
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In kernel/component-manager/src/main/java/org/sakaiproject/modi/SysProp.java
around lines 83 to 90, the public getRaw() method does not document that it can
return null when the system property is not set; update its JavaDoc to include
an @return tag reading: "the property value, or null if not set". Also
add/update JavaDoc for getRaw(String def) to include an @return tag like: "the
property value, or the provided default if not set" and, optionally, a short
note directing callers to the Optional-based accessors for null-safe usage.

Comment on lines +93 to 95
public Path getRawPath() {
return get().map(Path::of).orElse(null);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Document or refactor null-returning Path method.

getRawPath() returns null when the property is not set (line 94), which is risky for a public API returning Path. Callers may assume a non-null return and encounter NPEs. This is inconsistent with the safer getPath() method that returns Optional<Path>.

Recommendations:

  1. Add explicit JavaDoc documenting the null return
  2. Consider deprecating this in favor of getPath()
  3. Or refactor to throw an exception instead of returning null
💡 Suggested JavaDoc improvement
-    /** Get the unchecked value of this property as a Path; currently implemented over System properties. */
+    /** 
+     * Get the unchecked value of this property as a Path; currently implemented over System properties.
+     * @return the property value as a Path, or null if not set
+     */
     public Path getRawPath() {
         return get().map(Path::of).orElse(null);
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In kernel/component-manager/src/main/java/org/sakaiproject/modi/SysProp.java
around lines 93-95, the public getRawPath() returns null when the property is
absent which risks NPEs; mark this method as @Deprecated, add clear JavaDoc
stating it may return null and instruct callers to use getPath() which returns
Optional<Path>, update any internal call sites to prefer getPath() (or handle
null) and add a short migration note in the Javadoc pointing to getPath(); if
you prefer a stricter change instead, replace the body to throw an unchecked
exception (IllegalStateException) rather than returning null and document that
behavior in the Javadoc.

Comment on lines +102 to 104
public Path getRawPathPlus(String other) {
return getPath().map(p -> p.resolve(other)).orElse(null);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Document or refactor null-returning Path method.

Similar to getRawPath(), this method returns null when the property is not set (line 103), which creates NPE risk in public API. The presence of the other parameter may mislead callers into expecting a non-null result.

Recommendations:

  1. Add explicit JavaDoc documenting the null return
  2. Consider deprecating this in favor of getPathPlus(String other)
  3. Or refactor to throw an exception instead of returning null
💡 Suggested JavaDoc improvement
     /**
      * Get this property as a Path, with a resolved extension; currently implemented over System properties.
      * <p>
      * The extension is applied if the property is set. This returns the combined path if so, null otherwise.
+     * @param other the path segment to resolve against the property value
+     * @return the resolved path, or null if the property is not set
      */
     public Path getRawPathPlus(String other) {
         return getPath().map(p -> p.resolve(other)).orElse(null);
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In kernel/component-manager/src/main/java/org/sakaiproject/modi/SysProp.java
around lines 102 to 104, the public method getRawPathPlus(String other) returns
null when the property is not set, which risks NPEs and is misleading to
callers; update this API by either (A) adding clear JavaDoc on the method
stating that it may return null when the property is unset (and document callers
must null-check), or (B) mark this method as @Deprecated and create a safer
replacement getPathPlus(String other) that returns Optional<Path>, or (C) change
behavior to throw a well-documented unchecked exception (e.g.,
IllegalStateException) when the property is absent; implement one of these fixes
consistently (JavaDoc + deprecation is preferred), update callers/tests
accordingly, and run project build to ensure no regressions.

Comment on lines 112 to 125
public void givenUnwritableParentOfSakaiHome_whenInitialized_thenInitializationFails() throws IOException {
tmpDir.newFolder("components");
File file = tmpDir.newFolder("readonly");
if (file.setReadOnly()) {
System.setProperty("catalina.base", tmp.toString());
System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());

assertThatExceptionOfType(InitializationException.class)
.isThrownBy(Environment::initialize)
.withMessageContaining("could not create sakai.home");
if (!file.setReadOnly()) {
throw new IOException("Critical error -- could not set test directory read-only: " + file.getAbsolutePath());
}

System.setProperty("catalina.base", tmp.toString());
System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());

assertThatExceptionOfType(InitializationException.class)
.isThrownBy(Environment::initialize)
.withMessageContaining("could not create sakai.home");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Test fails on Windows due to unreliable File.setReadOnly() behavior on directories.

The pipeline failures confirm that File.setReadOnly() returns false on Windows when called on directories, causing these tests to throw IOException and fail. File.setReadOnly() has known platform-specific limitations—it doesn't work reliably on directories in Windows.

Consider one of these approaches:

🔎 Solution 1: Skip test on Windows using JUnit Assumptions
 @Test
 public void givenUnwritableParentOfSakaiHome_whenInitialized_thenInitializationFails() throws IOException {
+    assumeFalse(System.getProperty("os.name").toLowerCase().contains("win"), 
+                "setReadOnly() is unreliable on Windows directories");
     tmpDir.newFolder("components");
     File file = tmpDir.newFolder("readonly");
     if (!file.setReadOnly()) {
         throw new IOException("Critical error -- could not set test directory read-only: " + file.getAbsolutePath());
     }

     System.setProperty("catalina.base", tmp.toString());
     System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());

     assertThatExceptionOfType(InitializationException.class)
             .isThrownBy(Environment::initialize)
             .withMessageContaining("could not create sakai.home");
 }

Add import: import static org.junit.Assume.assumeFalse;

🔎 Solution 2: Use Java NIO with platform detection
 @Test
 public void givenUnwritableParentOfSakaiHome_whenInitialized_thenInitializationFails() throws IOException {
     tmpDir.newFolder("components");
     File file = tmpDir.newFolder("readonly");
-    if (!file.setReadOnly()) {
-        throw new IOException("Critical error -- could not set test directory read-only: " + file.getAbsolutePath());
-    }
+    
+    try {
+        Set<PosixFilePermission> perms = PosixFilePermissions.fromString("r-xr-xr-x");
+        Files.setPosixFilePermissions(file.toPath(), perms);
+    } catch (UnsupportedOperationException e) {
+        assumeTrue(file.setReadOnly(), "Cannot make directory read-only on this platform");
+    }

     System.setProperty("catalina.base", tmp.toString());
     System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());

     assertThatExceptionOfType(InitializationException.class)
             .isThrownBy(Environment::initialize)
             .withMessageContaining("could not create sakai.home");
 }

Add imports: import java.nio.file.*;, import java.nio.file.attribute.*;, import static org.junit.Assume.assumeTrue;

🧰 Tools
🪛 GitHub Actions: Windows Build

[error] 116-116: IO Critical error -- could not set test directory read-only: C:\Users\RUNNER~1\AppData\Local\Temp\junit12627441752225610901\readonly

🤖 Prompt for AI Agents
In
kernel/component-manager/src/test/java/org/sakaiproject/modi/EnvironmentTest.java
around lines 112 to 125, the test relies on File.setReadOnly() which is
unreliable on Windows and causes CI failures; modify the test to skip on Windows
using a JUnit assumption: add import static org.junit.Assume.assumeFalse; detect
Windows (e.g. String os = System.getProperty("os.name").toLowerCase(); boolean
isWin = os.contains("win");) and call assumeFalse("Skip test on Windows where
setReadOnly on directories is unreliable", isWin) before attempting to set the
directory read-only, so the rest of the test can proceed unchanged on supported
platforms.

Comment on lines 127 to 141
@Test
public void givenUnwritableSakaiHome_whenInitialized_thenInitializationFails() throws IOException {
tmpDir.newFolder("components");
File file = tmpDir.newFolder("readonly/sakai");
if (file.setReadOnly()) {
System.setProperty("catalina.base", tmp.toString());
System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());

assertThatExceptionOfType(InitializationException.class)
.isThrownBy(Environment::initialize)
.withMessageContaining("sakai.home is missing or unreadable");
if (!file.setReadOnly()) {
throw new IOException("Critical error -- could not set test directory read-only: " + file.getAbsolutePath());
}

System.setProperty("catalina.base", tmp.toString());
System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());

assertThatExceptionOfType(InitializationException.class)
.isThrownBy(Environment::initialize)
.withMessageContaining("sakai.home is missing or unreadable");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Test fails on Windows due to unreliable File.setReadOnly() behavior on directories.

This test has the same issue as givenUnwritableParentOfSakaiHome_whenInitialized_thenInitializationFails. Apply the same fix (either skip on Windows or use Java NIO with platform detection).

🔎 Solution 1: Skip test on Windows using JUnit Assumptions
 @Test
 public void givenUnwritableSakaiHome_whenInitialized_thenInitializationFails() throws IOException {
+    assumeFalse(System.getProperty("os.name").toLowerCase().contains("win"), 
+                "setReadOnly() is unreliable on Windows directories");
     tmpDir.newFolder("components");
     File file = tmpDir.newFolder("readonly/sakai");
     if (!file.setReadOnly()) {
         throw new IOException("Critical error -- could not set test directory read-only: " + file.getAbsolutePath());
     }

     System.setProperty("catalina.base", tmp.toString());
     System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());

     assertThatExceptionOfType(InitializationException.class)
             .isThrownBy(Environment::initialize)
             .withMessageContaining("sakai.home is missing or unreadable");
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test
public void givenUnwritableSakaiHome_whenInitialized_thenInitializationFails() throws IOException {
tmpDir.newFolder("components");
File file = tmpDir.newFolder("readonly/sakai");
if (file.setReadOnly()) {
System.setProperty("catalina.base", tmp.toString());
System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());
assertThatExceptionOfType(InitializationException.class)
.isThrownBy(Environment::initialize)
.withMessageContaining("sakai.home is missing or unreadable");
if (!file.setReadOnly()) {
throw new IOException("Critical error -- could not set test directory read-only: " + file.getAbsolutePath());
}
System.setProperty("catalina.base", tmp.toString());
System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());
assertThatExceptionOfType(InitializationException.class)
.isThrownBy(Environment::initialize)
.withMessageContaining("sakai.home is missing or unreadable");
}
@Test
public void givenUnwritableSakaiHome_whenInitialized_thenInitializationFails() throws IOException {
assumeFalse(System.getProperty("os.name").toLowerCase().contains("win"),
"setReadOnly() is unreliable on Windows directories");
tmpDir.newFolder("components");
File file = tmpDir.newFolder("readonly/sakai");
if (!file.setReadOnly()) {
throw new IOException("Critical error -- could not set test directory read-only: " + file.getAbsolutePath());
}
System.setProperty("catalina.base", tmp.toString());
System.setProperty("sakai.home", tmp.resolve("readonly/sakai").toString());
assertThatExceptionOfType(InitializationException.class)
.isThrownBy(Environment::initialize)
.withMessageContaining("sakai.home is missing or unreadable");
}
🧰 Tools
🪛 GitHub Actions: Windows Build

[error] 132-132: IO Critical error -- could not set test directory read-only: C:\Users\RUNNER~1\AppData\Local\Temp\junit13493837206627215482\readonly\sakai

🤖 Prompt for AI Agents
In
kernel/component-manager/src/test/java/org/sakaiproject/modi/EnvironmentTest.java
around lines 127-141, the test attempts to make a directory read-only using
File.setReadOnly(), which is unreliable on Windows and causes intermittent
failures; update the test to match the fix used in the similar test: guard the
read-only logic with a platform check and skip the test on Windows (use JUnit
Assumptions.assumeFalse(System.getProperty("os.name").toLowerCase().contains("win"))),
or alternatively use Java NIO to set POSIX permissions only when the OS supports
POSIX (check Files.getFileAttributeView and apply Files.setPosixFilePermissions
on non-Windows); ensure the test cleanly aborts/skips on Windows instead of
attempting File.setReadOnly().

Comment on lines +22 to 38
private boolean started = false;

/**
* Ensure that the Ignite grid instance has been started.
* <p>
* IgniteSpringBean conducts an unsafe (once-only) startup of the instance in its normal
* afterSingletonsInstantiated lifecycle hook, but it does so after some components like
* the Hibernate entity manager need the grid started. Here, we safely bridge our needed
* lifecycle with the base bean behavior by checking and conditionally starting.
* {@link IgniteEntityManagerFactoryBean}
*/
public void ensureStarted() {
if (started) return;

super.afterSingletonsInstantiated();
started = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Race condition: ensureStarted() is not thread-safe.

The started flag check-and-set pattern is not atomic. Multiple threads could see started == false simultaneously and both call super.afterSingletonsInstantiated(), potentially starting the Ignite grid twice. The Javadoc mentions "safely bridge" but the current implementation doesn't provide thread safety.

🔎 Proposed fix using synchronized
-    private boolean started = false;
+    private volatile boolean started = false;

     /**
      * Ensure that the Ignite grid instance has been started.
      * <p>
      * IgniteSpringBean conducts an unsafe (once-only) startup of the instance in its normal
      * afterSingletonsInstantiated lifecycle hook, but it does so after some components like
      * the Hibernate entity manager need the grid started. Here, we safely bridge our needed
      * lifecycle with the base bean behavior by checking and conditionally starting.
      * {@link IgniteEntityManagerFactoryBean}
      */
-    public void ensureStarted() {
+    public synchronized void ensureStarted() {
         if (started) return;

         super.afterSingletonsInstantiated();
         started = true;
     }

Alternatively, for better performance with the double-checked locking pattern:

🔎 Alternative: Double-checked locking
-    private boolean started = false;
+    private volatile boolean started = false;
+    private final Object startLock = new Object();

     public void ensureStarted() {
         if (started) return;
-
-        super.afterSingletonsInstantiated();
-        started = true;
+        synchronized (startLock) {
+            if (started) return;
+            super.afterSingletonsInstantiated();
+            started = true;
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private boolean started = false;
/**
* Ensure that the Ignite grid instance has been started.
* <p>
* IgniteSpringBean conducts an unsafe (once-only) startup of the instance in its normal
* afterSingletonsInstantiated lifecycle hook, but it does so after some components like
* the Hibernate entity manager need the grid started. Here, we safely bridge our needed
* lifecycle with the base bean behavior by checking and conditionally starting.
* {@link IgniteEntityManagerFactoryBean}
*/
public void ensureStarted() {
if (started) return;
super.afterSingletonsInstantiated();
started = true;
}
private volatile boolean started = false;
/**
* Ensure that the Ignite grid instance has been started.
* <p>
* IgniteSpringBean conducts an unsafe (once-only) startup of the instance in its normal
* afterSingletonsInstantiated lifecycle hook, but it does so after some components like
* the Hibernate entity manager need the grid started. Here, we safely bridge our needed
* lifecycle with the base bean behavior by checking and conditionally starting.
* {@link IgniteEntityManagerFactoryBean}
*/
public synchronized void ensureStarted() {
if (started) return;
super.afterSingletonsInstantiated();
started = true;
}
Suggested change
private boolean started = false;
/**
* Ensure that the Ignite grid instance has been started.
* <p>
* IgniteSpringBean conducts an unsafe (once-only) startup of the instance in its normal
* afterSingletonsInstantiated lifecycle hook, but it does so after some components like
* the Hibernate entity manager need the grid started. Here, we safely bridge our needed
* lifecycle with the base bean behavior by checking and conditionally starting.
* {@link IgniteEntityManagerFactoryBean}
*/
public void ensureStarted() {
if (started) return;
super.afterSingletonsInstantiated();
started = true;
}
private volatile boolean started = false;
private final Object startLock = new Object();
/**
* Ensure that the Ignite grid instance has been started.
* <p>
* IgniteSpringBean conducts an unsafe (once-only) startup of the instance in its normal
* afterSingletonsInstantiated lifecycle hook, but it does so after some components like
* the Hibernate entity manager need the grid started. Here, we safely bridge our needed
* lifecycle with the base bean behavior by checking and conditionally starting.
* {@link IgniteEntityManagerFactoryBean}
*/
public void ensureStarted() {
if (started) return;
synchronized (startLock) {
if (started) return;
super.afterSingletonsInstantiated();
started = true;
}
}
🤖 Prompt for AI Agents
In
kernel/kernel-impl/src/main/java/org/sakaiproject/ignite/EagerIgniteSpringBean.java
around lines 22 to 38, the ensureStarted() method performs a non-atomic
check-then-set on the started flag which can lead to multiple threads invoking
super.afterSingletonsInstantiated() concurrently; make this method thread-safe
by using either a synchronized block or double-checked locking: declare started
as volatile, first check started and return if true, then synchronize on this
and re-check started inside the synchronized block before calling
super.afterSingletonsInstantiated() and setting started = true so the base
startup runs exactly once.

Comment on lines +72 to +111
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>compile</scope>
</dependency>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's find and read the pom.xml file in question
find . -path "*/kernel/test-helper/pom.xml" -type f | head -1

Repository: sakaiproject/sakai

Length of output: 90


🏁 Script executed:

# Read the pom.xml to check the Spring version
cat -n kernel/test-helper/pom.xml | head -100

Repository: sakaiproject/sakai

Length of output: 4097


🌐 Web query:

CVE GHSA-4gc7-5j7h-4qph Spring Framework DataBinder

💡 Result:

Summary — CVE-2024-38820 / GHSA-4gc7-5j7h-4qph

  • What: A DataBinder "disallowedFields" case-insensitivity issue: the patch for CVE-2022-22968 used String.toLowerCase(), which has locale-dependent exceptions and can let some protected fields bypass DataBinder protection. [1][2]
  • Severity/IDs: Moderate; CVE-2024-38820, GitHub GHSA-4gc7-5j7h-4qph. [2][3]
  • Affected Spring Framework versions:
    • 5.3.0 — 5.3.40
    • 6.0.0 — 6.0.24
    • 6.1.0 — 6.1.13
    • (older unsupported versions may also be affected). [1][2]
  • Fixed versions / recommended action:
    • Upgrade to at least 5.3.41, 6.0.25, or 6.1.14 (depending on your line). Spring recommends upgrading; no other mitigations are listed. [1][2]
  • References:
    1. Spring security advisory (CVE-2024-38820). [1]
    2. GitHub Advisory / GHSA-4gc7-5j7h-4qph. [2]
    3. OSV / NVD summary (CVE-2024-38820). [3]

🌐 Web query:

CVE GHSA-4wrc-f8pq-fpqp Spring Framework unsafe deserialization

💡 Result:

Summary: GHSA-4wrc-f8pq-fpqp (alias CVE-2016-1000027) is a critical insecure-deserialization / potential RCE issue in the Spring Framework: org.springframework:spring-web versions before 6.0.0 may perform unsafe Java deserialization of untrusted data, allowing remote code execution depending on how Spring is used. [1][2][3]

Affected versions:

  • spring-web / Spring Framework < 6.0.0 (older 5.x, 4.x, 3.x lines as noted). [1][2]

Mitigation / recommended actions:

  • Upgrade to Spring Framework 6.0.0 or later (6.0.0 removes the impacted classes). [1][2]
  • If you cannot upgrade immediately:
    • Avoid deserializing untrusted data; ensure only trusted sources can provide serialized Java payloads.
    • Use allowlists / blocklists for deserialization (or switch to safer serializers like JSON) where applicable.
    • Note: Spring 5.3.x deprecated the affected classes; 4.2.6 and 3.2.17 include enhanced documentation recommending precautions. [1][2]

References:


🌐 Web query:

Spring Framework 5.3.40 release date security fixes

💡 Result:

  • Spring Framework 5.3.40 was released as a commercial (support-customer) hotfix on September 12, 2024 to address a path‑traversal issue (CVE‑2024‑38816). [1][2]

  • That CVE (CVE‑2024‑38816) affects 5.3.0–5.3.39 and was fixed in 5.3.40 (commercial). Spring later published a related path‑traversal report (CVE‑2024‑38819) which still affected 5.3.0–5.3.40 and was fixed in 5.3.41. If you run 5.3.40 you should upgrade to 5.3.41 (or a supported OSS 6.1.x) to address the later report. [1][3]

Sources:

  1. Spring blog — "Spring Framework 5.3.40 and 6.0.24 available now" (Sept 12, 2024). [4]
  2. Spring Security advisory — CVE‑2024‑38816 (Sept 12, 2024). [1]
  3. Spring Security advisory / blog — CVE‑2024‑38819 and guidance to upgrade to 5.3.41 (Oct 2024). [3]

(If you want, I can fetch the exact release notes/changelog for 5.3.40 and 5.3.41.)


🏁 Script executed:

# Read the parent pom.xml
cat -n kernel/pom.xml | head -150

Repository: sakaiproject/sakai

Length of output: 6076


🌐 Web query:

spring-webmvc path traversal vulnerabilities 5.3

💡 Result:

Short summary — Spring WebMVC (5.3) path-traversal issues and what to do:

  • CVE-2024-38816: path traversal in functional web frameworks (WebMvc.fn/WebFlux.fn). Affects Spring Framework 5.3.0 — 5.3.39; fixed in 5.3.40 (commercial release). Mitigations: upgrade to fixed release, or (if you cannot upgrade) enable Spring Security HTTP Firewall or run on Tomcat/Jetty which reject the malicious requests. [1][2]

  • CVE-2025-41242 (reported Aug 2025): path traversal affecting spring-webmvc when deployed on non‑compliant servlet containers. Affects 5.3.0 — 5.3.43 (and certain 6.x ranges); advisories recommend upgrading to a patched release (upstream advisories list 6.2.10 as a patched baseline and instruct upgrading 5.3.x to a patched branch). If you run on a servlet container that properly canonicalizes/rejects suspicious paths, your exposure is reduced, but upgrade is recommended. [3][4]

Action items (urgent):

  1. Identify projects using org.springframework:spring-webmvc 5.3.x and exactly which sub-version(s).
  2. Upgrade to a patched Spring release (for CVE-2024-38816: 5.3.40+ where available; for the 2025 advisory follow the patched versions in your advisory feed — see links). If you rely on open-source 5.3.x and a patch isn’t available in OSS for your exact micro version, plan migration to a supported branch or apply vendor/commercial fixes. [1][2][3][4]
  3. As short-term mitigations: enable Spring Security HTTP Firewall and/or run on Tomcat or Jetty (if acceptable), and review any use of RouterFunctions serving static resources or FileSystemResource locations. [1][2]

Sources:
[1] Spring Security advisory — CVE-2024-38816 (path traversal in functional web frameworks). (spring.io)
[2] Spring blog post: fixes and release notes for CVE-2024-38816. (spring.io)
[3] GitHub Advisory (GHSA / CVE-2025-41242) — spring-webmvc path traversal advisory and affected/patched ranges. (github.com)
[4] GitLab advisory / package database entry for CVE-2025-41242. (advisories.gitlab.com)

If you want, tell me the exact spring-webmvc versions you have and I’ll say whether they’re vulnerable and which specific upgrade target to use.

Citations:


🌐 Web query:

Spring Framework 5.3.41 release available

💡 Result:

Yes. Spring Framework 5.3.41 was released as an enterprise (commercial) hotfix on October 17, 2024 (contains fixes for CVE‑2024‑38819 and CVE‑2024‑38820) and is available to Spring Enterprise / Tanzu customers — it was not released as an open‑source public 5.3.x update. [1][2][3]

Sources: Spring blog announcement (Oct 17, 2024). [1] Spring Enterprise project page for 5.3.41. [2] 5.3.41 docs (updated Oct 17, 2024). [3]


🏁 Script executed:

# Find and read the master pom.xml to determine actual Spring version
find . -path "*/master/pom.xml" -type f

Repository: sakaiproject/sakai

Length of output: 78


🏁 Script executed:

# Look for Spring version declaration in master pom
cat -n master/pom.xml | grep -A 5 -B 5 "spring" | head -100

Repository: sakaiproject/sakai

Length of output: 4929


Spring Framework 5.3.39 contains multiple known vulnerabilities requiring mitigation strategy.

Spring Framework 5.3.39 is affected by several CVEs:

CVE Severity Issue Mitigation
CVE-2024-38820 Moderate DataBinder case-insensitive field bypass Requires 5.3.40+ (commercial-only release)
CVE-2024-38816 High Path traversal in functional web frameworks Requires 5.3.40+ (commercial-only release)
CVE-2025-41242 High Path traversal in spring-webmvc Ongoing patch releases; 5.3.43+ affected
CVE-2016-1000027 Critical Unsafe Java deserialization Only fixed in Spring 6.0.0+

Given that the Sakai project cannot upgrade to Spring 6 at this time, CVE-2016-1000027 cannot be fully resolved on the 5.3.x line. For the path traversal issues (CVE-2024-38816, CVE-2025-41242), consider enabling Spring Security HTTP Firewall as a compensating control and ensure this test-helper module's runtime exposure is isolated. For commercial customers, upgrading to Spring 5.3.40 or later resolves some vulnerabilities, but plan migration to Spring 6.x when feasible to address the deserialization issue.

Comment on lines +9 to +11
<context:annotation-config />
<!-- We scan to pick up the bullhorn handler; it will be created and self-register with the MessagingService -->
<context:component-scan base-package="org.sakaiproject.announcement.api" />
Copy link
Member Author

Choose a reason for hiding this comment

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

This may or may not be relevant now with everything using the base class that auto-registers.

Comment on lines +88 to +91
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
Copy link
Member Author

Choose a reason for hiding this comment

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

Switch to jakarta.inject -- and consider whether the CDI API really helps anything, considering we are quite Springy.

* specific and clear, not as a pattern to follow.
*/
@ComponentScan(basePackages = "org.sakaiproject.announcement.tool")
public class AnncTestBeanConfig {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is effectively an example. Either use it for real announcement tests or extract to example area.

import static org.assertj.core.api.Assertions.*;

@RunWith(SpringJUnit4ClassRunner.class)
public class ContextSetupTest extends ModiTest {
Copy link
Member Author

Choose a reason for hiding this comment

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

This whole test is an example now -- relocate.

"file:../../announcement-impl/impl/src/webapp/WEB-INF/components.xml"
})
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class KernelIntegrityTest extends ModiTest {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is not an example, but should be moved into the kernel (without the announcement components).

* The default Ignite base directory is ${sakai.home}/ignite/
*/
protected String fallbackBaseDirectory() {
return sakai_home.getPathPlus("ignite")
Copy link
Member Author

Choose a reason for hiding this comment

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

Note: we use the SysProp here because of the super special status of sakai.home and the tricky dependency graph around SakaiProperties. This is definitely better than System.getProperty, though.

<context:annotation-config />
<!-- We need to scan these to pick up the bullhorn handlers for MessagingServiceImpl -->
<context:component-scan base-package="org.sakaiproject.messaging.api.testhandler,org.sakaiproject.announcement.api, org.sakaiproject.profile2.model, org.sakaiproject.commons.api, org.sakaiproject.lessonbuildertool.api" />
<context:component-scan base-package="org.sakaiproject.messaging.api.testhandler, org.sakaiproject.profile2.model, org.sakaiproject.commons.api, org.sakaiproject.lessonbuildertool.api" />
Copy link
Member Author

Choose a reason for hiding this comment

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

With the registration now incumbent upon the handlers themselves, I think this component-scan is unnecessary (and inappropriate). If we get to a point where tool-owned components are in their own tier (context), this kind of scan would always come up empty. It only kind-of works now because everything goes into one context.

Comment on lines +13 to +17
<modelVersion>4.0.0</modelVersion>
<groupId>org.sakaiproject.kernel</groupId>
<artifactId>sakai-test-helper</artifactId>
<packaging>jar</packaging>
<name>Sakai Kernel Test Helper</name>
Copy link
Member Author

Choose a reason for hiding this comment

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

There may be a way to make this jar without replicating all of the stuff from kernel or deploy that makes it all work, but there are a bunch of places where we change scope from provided to compile so they get unrolled and flattened. This is the painstakingly created minimal set needed to boot the kernel.

Comment on lines +14 to +22
### Spring and Hibernate ###
#log4j.logger.org.springframework=debug
#log4j.logger.net.sf.hibernate=debug
#log4j.logger.org.hibernate=debug

#log4j.logger.org.hibernate.SQL=debug
#log4j.logger.org.hibernate.type=debug
#log4j.logger.org.hibernate.tool.hbm2ddl=info
#log4j.logger.org.hibernate.cache=debug
Copy link
Member Author

Choose a reason for hiding this comment

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

Definitely want to these down by default for tests. They are extremely noisy with Ignite and Hibernate, in addition to all of the standard Spring stuff. It's easy enough to turn them up within a test context if you need.

Comment on lines +25 to +28
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
Copy link
Member Author

Choose a reason for hiding this comment

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

This may not be necessary -- seems spurious.

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.

1 participant