Startup improvements#3928
Conversation
… checked/ugpraded yet
WalkthroughThis PR removes organization-scoped group and user management APIs, introduces per-site bootstrap state tracking with annotation-based operation gating, refactors the startup event flow using semaphore-based coordination between system and site upgrades, removes deprecated WebDAV and ElasticTranscoder features, and updates the database schema to version 5.0.0.18. ChangesOrganization Removal & Site Bootstrap State Gating
🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/org/craftercms/studio/impl/v2/service/site/SitesServiceImpl.java`:
- Around line 129-132: The enablePublishing method is annotated with
`@RequireSiteBootstrapComplete` but its siteId parameter lacks the `@SiteId`
annotation so the bootstrap aspect cannot reliably resolve the target site;
update the method signature for enablePublishing to annotate the siteId
parameter with `@SiteId` in addition to the existing
`@ProtectedResourceId`(SITE_ID_RESOURCE_ID) so the bootstrap gating aspect can
correctly resolve and validate the site before calling
sitesServiceInternal.enablePublishing.
In
`@src/main/java/org/craftercms/studio/impl/v2/upgrade/StudioUpgradeManagerImpl.java`:
- Around line 139-141: The call to siteBootstrapStateProvider.markSiteAsReady is
executed unconditionally even though upgradeSiteConfiguration catches and
swallows errors; change the flow so the site is only marked ready when the
config upgrade actually succeeded by having upgradeSiteConfiguration report
success (e.g., return boolean or throw on failure) and update
StudioUpgradeManagerImpl to check that result before calling
siteBootstrapStateProvider.markSiteAsReady(context.getTarget()); alternatively
catch specific exceptions around upgradeSiteConfiguration here and skip marking
ready on failure, ensuring upgradeSiteConfiguration and/or the caller use clear
success/failure signaling rather than logging-only.
In
`@src/main/java/org/craftercms/studio/impl/v2/utils/spring/context/BootstrapManagerImpl.java`:
- Around line 87-94: The catch for InterruptedException in BootstrapManagerImpl
currently logs and restores the interrupt but then lets execution continue and
returns a StartSitesUpgradeEvent; change the catch to stop the flow by either
rethrowing a runtime exception or returning a no-op (do not produce
StartSitesUpgradeEvent). Concretely, inside the catch where
upgradeSemaphore.acquire() is handled, after Thread.currentThread().interrupt()
either throw a new IllegalStateException("Interrupted waiting for system
upgrade", e) (so callers won't start site upgrades) or return null/a dedicated
abort event instead of falling through to return new
StartSitesUpgradeEvent(this); update any callers if you choose to return null to
handle the abort case.
In `@src/main/resources/org/craftercms/studio/api/v2/dal/GroupDAO.xml`:
- Around line 53-71: The mapper IDs in GroupDAO.xml don't match the DAO method
names; rename the <select> id "getAllGroups" to "getAllGroupsForOrganization"
and "getAllGroupsTotal" to "getAllGroupsForOrganizationTotal" so MyBatis can
bind to the GroupDAO methods; keep the existing resultMap/resultType, parameter
bindings (keyword -> pattern bind), and SQL clauses unchanged, only update the
id attributes to exactly match the GroupDAO method names.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5a58e55f-7793-42e2-afec-b0517e741d2c
📒 Files selected for processing (54)
src/main/api/studio-api.yamlsrc/main/java/org/craftercms/studio/api/v1/aws/elastictranscoder/ElasticTranscoder.javasrc/main/java/org/craftercms/studio/api/v1/constant/StudioConstants.javasrc/main/java/org/craftercms/studio/api/v1/service/webdav/WebDavService.javasrc/main/java/org/craftercms/studio/api/v2/annotation/RequireSiteBootstrapComplete.javasrc/main/java/org/craftercms/studio/api/v2/annotation/RequireSiteBootstrapCompleteAnnotationHandler.javasrc/main/java/org/craftercms/studio/api/v2/dal/AuditLog.javasrc/main/java/org/craftercms/studio/api/v2/dal/Group.javasrc/main/java/org/craftercms/studio/api/v2/dal/GroupDAO.javasrc/main/java/org/craftercms/studio/api/v2/dal/Organization.javasrc/main/java/org/craftercms/studio/api/v2/dal/QueryParameterNames.javasrc/main/java/org/craftercms/studio/api/v2/dal/Site.javasrc/main/java/org/craftercms/studio/api/v2/exception/OrganizationNotFoundException.javasrc/main/java/org/craftercms/studio/api/v2/exception/SiteBootstrapNotCompleteException.javasrc/main/java/org/craftercms/studio/api/v2/service/security/GroupService.javasrc/main/java/org/craftercms/studio/api/v2/service/security/UserService.javasrc/main/java/org/craftercms/studio/api/v2/utils/spring/context/BootstrapManager.javasrc/main/java/org/craftercms/studio/api/v2/utils/spring/context/SiteBootstrapStateProvider.javasrc/main/java/org/craftercms/studio/controller/rest/v2/ExceptionHandlers.javasrc/main/java/org/craftercms/studio/controller/rest/v2/GroupsController.javasrc/main/java/org/craftercms/studio/controller/rest/v2/UsersController.javasrc/main/java/org/craftercms/studio/impl/v1/aws/elastictranscoder/TranscoderProfileMapper.javasrc/main/java/org/craftercms/studio/impl/v1/service/content/DmPageNavigationOrderServiceImpl.javasrc/main/java/org/craftercms/studio/impl/v1/service/webdav/WebDavServiceImpl.javasrc/main/java/org/craftercms/studio/impl/v2/repository/GitStartupConfig.javasrc/main/java/org/craftercms/studio/impl/v2/repository/RepositoryStartupCleanup.javasrc/main/java/org/craftercms/studio/impl/v2/service/audit/internal/AuditServiceInternalImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/content/ContentServiceImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/security/GroupServiceImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/security/UserServiceImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/security/internal/GroupServiceInternalImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/security/internal/UserServiceInternalImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/site/SitesServiceImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/site/internal/SitesServiceInternalImpl.javasrc/main/java/org/craftercms/studio/impl/v2/upgrade/StudioUpgradeManagerImpl.javasrc/main/java/org/craftercms/studio/impl/v2/upgrade/operations/plugin/DescriptorV2UpgradeOperation.javasrc/main/java/org/craftercms/studio/impl/v2/upgrade/operations/site/AddSiteUuidOperation.javasrc/main/java/org/craftercms/studio/impl/v2/utils/spring/context/BootstrapManagerImpl.javasrc/main/java/org/craftercms/studio/impl/v2/utils/spring/context/SiteBootstrapStateProviderImpl.javasrc/main/java/org/craftercms/studio/impl/v2/utils/spring/event/StartSitesBootstrapEvent.javasrc/main/java/org/craftercms/studio/impl/v2/utils/spring/event/StartSitesUpgradeEvent.javasrc/main/java/org/craftercms/studio/impl/v2/utils/spring/event/StartSystemUpgradeEvent.javasrc/main/java/org/craftercms/studio/model/rest/ApiResponse.javasrc/main/resources/crafter/studio/database/createDDL.sqlsrc/main/resources/crafter/studio/database/upgrade/5.0.x/5.0.0.17-to-5.0.0.18.sqlsrc/main/resources/crafter/studio/security/common.xmlsrc/main/resources/crafter/studio/studio-config.yamlsrc/main/resources/crafter/studio/studio-services-context.xmlsrc/main/resources/crafter/studio/studio-upgrade-context.xmlsrc/main/resources/crafter/studio/studio-websocket-context.xmlsrc/main/resources/crafter/studio/upgrade/pipelines.yamlsrc/main/resources/org/craftercms/studio/api/v2/dal/AuditDAO.xmlsrc/main/resources/org/craftercms/studio/api/v2/dal/GroupDAO.xmlsrc/test/java/org/craftercms/studio/impl/v2/service/content/ContentServiceImplTest.java
💤 Files with no reviewable changes (11)
- src/main/java/org/craftercms/studio/api/v1/constant/StudioConstants.java
- src/main/java/org/craftercms/studio/api/v1/aws/elastictranscoder/ElasticTranscoder.java
- src/main/java/org/craftercms/studio/controller/rest/v2/ExceptionHandlers.java
- src/main/java/org/craftercms/studio/impl/v1/aws/elastictranscoder/TranscoderProfileMapper.java
- src/main/java/org/craftercms/studio/api/v1/service/webdav/WebDavService.java
- src/main/java/org/craftercms/studio/impl/v2/upgrade/operations/site/AddSiteUuidOperation.java
- src/main/java/org/craftercms/studio/api/v2/dal/Organization.java
- src/main/java/org/craftercms/studio/api/v2/exception/OrganizationNotFoundException.java
- src/main/java/org/craftercms/studio/impl/v2/upgrade/operations/plugin/DescriptorV2UpgradeOperation.java
- src/main/resources/crafter/studio/studio-upgrade-context.xml
- src/main/java/org/craftercms/studio/impl/v1/service/webdav/WebDavServiceImpl.java
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main/java/org/craftercms/studio/impl/v2/utils/spring/context/BootstrapManagerImpl.java (1)
101-105:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift
BootstrapFinishedEventfires before site bootstrap completes.This listener releases the semaphore and immediately returns
BootstrapFinishedEvent, but the sites bootstrap path is still running on the async branch. That meanssystemReadycan flip totruewhile repository cleanup / site upgrade work is still in flight. Please move the finished event to the end of the sites-bootstrap chain instead of emitting it from system-upgrade completion.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/org/craftercms/studio/impl/v2/utils/spring/context/BootstrapManagerImpl.java` around lines 101 - 105, The onStartSystemUpgrade() listener currently releases the upgradeSemaphore and immediately returns a new BootstrapFinishedEvent(this), causing BootstrapFinishedEvent to fire before async site bootstrap work finishes; remove the return/new event from onStartSystemUpgrade() (keep upgradeSemaphore.release()) and instead publish/return BootstrapFinishedEvent at the very end of the sites-bootstrap async chain (after all repository cleanup/site upgrade work completes) so that BootstrapFinishedEvent is emitted only when the sites-bootstrap flow has fully finished.src/main/java/org/craftercms/studio/impl/v2/service/site/internal/SitesServiceInternalImpl.java (1)
601-608:⚠️ Potential issue | 🟠 Major | ⚡ Quick winBootstrap-aware state is only applied to the list path.
getAllSites()rewritesREADYtoBOOTSTRAPPING, butgetSite()andgetSiteDetails()still return the raw DAO state. That lets the same site appear bootstrapping in list responses and ready in detail reads. Please centralize this state decoration and reuse it for single-site reads as well.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/org/craftercms/studio/impl/v2/service/site/internal/SitesServiceInternalImpl.java` around lines 601 - 608, The list method getAllSites() currently mutates READY -> BOOTSTRAPPING inline; extract that logic into a single reusable helper (e.g., decorateSiteState(Site) or decorateSites(List<Site>)) and call it from getAllSites(), getSite(), and getSiteDetails() so single-site reads are decorated the same way; use the same symbols siteBootstrapStateProvider, Site.State.READY, and Site.State.BOOTSTRAPPING and avoid duplicating state-mutation logic by reusing the helper from the service methods that return one or many Site objects.
♻️ Duplicate comments (1)
src/main/java/org/craftercms/studio/impl/v2/service/site/SitesServiceImpl.java (1)
129-131:⚠️ Potential issue | 🟠 Major | ⚡ Quick winKeep
@ProtectedResourceId(SITE_ID_RESOURCE_ID)onenablePublishing.Replacing it with
@SiteIdfixes bootstrap resolution, but it drops the resource binding used by the permission check on this endpoint. This regresses the earlier fix; the parameter needs both annotations.💡 Proposed fix
- public void enablePublishing(`@SiteId` String siteId, boolean enabled) { + public void enablePublishing(`@SiteId` `@ProtectedResourceId`(SITE_ID_RESOURCE_ID) String siteId, boolean enabled) { sitesServiceInternal.enablePublishing(siteId, enabled); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/org/craftercms/studio/impl/v2/service/site/SitesServiceImpl.java` around lines 129 - 131, The enablePublishing method lost its resource binding when `@ProtectedResourceId`(SITE_ID_RESOURCE_ID) was removed; restore permission checks by annotating the siteId parameter with both `@SiteId` and `@ProtectedResourceId`(SITE_ID_RESOURCE_ID) so RequireSiteBootstrapComplete and HasPermission (DefaultPermission, PERMISSION_START_STOP_PUBLISHER) still work together for permission resolution and bootstrap handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@src/main/java/org/craftercms/studio/impl/v2/service/site/internal/SitesServiceInternalImpl.java`:
- Around line 601-608: The list method getAllSites() currently mutates READY ->
BOOTSTRAPPING inline; extract that logic into a single reusable helper (e.g.,
decorateSiteState(Site) or decorateSites(List<Site>)) and call it from
getAllSites(), getSite(), and getSiteDetails() so single-site reads are
decorated the same way; use the same symbols siteBootstrapStateProvider,
Site.State.READY, and Site.State.BOOTSTRAPPING and avoid duplicating
state-mutation logic by reusing the helper from the service methods that return
one or many Site objects.
In
`@src/main/java/org/craftercms/studio/impl/v2/utils/spring/context/BootstrapManagerImpl.java`:
- Around line 101-105: The onStartSystemUpgrade() listener currently releases
the upgradeSemaphore and immediately returns a new BootstrapFinishedEvent(this),
causing BootstrapFinishedEvent to fire before async site bootstrap work
finishes; remove the return/new event from onStartSystemUpgrade() (keep
upgradeSemaphore.release()) and instead publish/return BootstrapFinishedEvent at
the very end of the sites-bootstrap async chain (after all repository
cleanup/site upgrade work completes) so that BootstrapFinishedEvent is emitted
only when the sites-bootstrap flow has fully finished.
---
Duplicate comments:
In
`@src/main/java/org/craftercms/studio/impl/v2/service/site/SitesServiceImpl.java`:
- Around line 129-131: The enablePublishing method lost its resource binding
when `@ProtectedResourceId`(SITE_ID_RESOURCE_ID) was removed; restore permission
checks by annotating the siteId parameter with both `@SiteId` and
`@ProtectedResourceId`(SITE_ID_RESOURCE_ID) so RequireSiteBootstrapComplete and
HasPermission (DefaultPermission, PERMISSION_START_STOP_PUBLISHER) still work
together for permission resolution and bootstrap handling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5d8b2a12-541c-4923-a311-19cb13108fdb
📒 Files selected for processing (5)
src/main/java/org/craftercms/studio/api/v2/dal/GroupDAO.javasrc/main/java/org/craftercms/studio/impl/v2/service/security/internal/GroupServiceInternalImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/site/SitesServiceImpl.javasrc/main/java/org/craftercms/studio/impl/v2/service/site/internal/SitesServiceInternalImpl.javasrc/main/java/org/craftercms/studio/impl/v2/utils/spring/context/BootstrapManagerImpl.java
Startup improvements
craftercms/craftercms#8652
Summary by CodeRabbit
New Features
Bug Fixes
Chores