Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.enonic.xp.dump;

import com.enonic.xp.repository.RepositoryIds;

public final class SystemLoadParams
{
private final String dumpName;
Expand All @@ -12,13 +14,16 @@ public final class SystemLoadParams

private final SystemLoadListener listener;

private final RepositoryIds repositories;

private SystemLoadParams( final Builder builder )
{
this.dumpName = builder.dumpName;
this.includeVersions = builder.includeVersions;
this.listener = builder.listener;
this.upgrade = builder.upgrade;
this.archive = builder.archive;
this.repositories = builder.repositories;
}

public String getDumpName()
Expand Down Expand Up @@ -46,6 +51,11 @@ public boolean isArchive()
return archive;
}

public RepositoryIds getRepositories()
{
return repositories;
}

public static Builder create()
{
return new Builder();
Expand All @@ -63,6 +73,8 @@ public static final class Builder

private SystemLoadListener listener;

private RepositoryIds repositories;

private Builder()
{
}
Expand Down Expand Up @@ -97,6 +109,12 @@ public Builder archive( final boolean archive )
return this;
}

public Builder repositories( final RepositoryIds repositories )
{
this.repositories = repositories;
return this;
}

public SystemLoadParams build()
{
return new SystemLoadParams( this );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,92 +305,125 @@ public SystemLoadResult load( final SystemLoadParams params )
throw new RepoLoadException( "Cannot load system-dump; dump does not contain system repository" );
}

this.eventPublisher.publish( RepositoryEvents.restoreInitialized() );
final RepositoryIds requestedRepositories = params.getRepositories();
final boolean isPartialLoad = requestedRepositories != null && !requestedRepositories.isEmpty();

if ( params.getListener() != null )
{
final long branchesCount = dumpRepositories.
stream().
flatMap( repositoryId -> dumpReader.getBranches( repositoryId ).stream() ).
count();
doLoad( params, results, dumpReader, dumpRepositories, isPartialLoad ? requestedRepositories : null );

params.getListener().totalBranches( branchesCount );
}
this.eventPublisher.publish( RepositoryEvents.restored() );
LOG.info( "Dump Load completed" );
}
catch ( IOException e )
{
throw new UncheckedIOException( e );
}
return results.build();
}

final boolean includeVersions = params.isIncludeVersions();
private void doLoad( final SystemLoadParams params, final SystemLoadResult.Builder results, final DumpReader dumpReader,

Choose a reason for hiding this comment

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

final RepositoryIds dumpRepositories, final RepositoryIds requestedRepositories )
{
final boolean isPartialLoad = requestedRepositories != null && !requestedRepositories.isEmpty();

final RepositorySettings currentSystemSettings =
repositoryEntryService.getRepositoryEntry( SystemConstants.SYSTEM_REPO_ID ).getSettings();
// Determine which repositories to load (excluding system-repo for partial load)
final RepositoryIds repositoriesToLoad = isPartialLoad ? dumpRepositories.stream()
.filter( requestedRepositories::contains )
.filter( Predicate.isEqual( SystemConstants.SYSTEM_REPO_ID ).negate() )
.collect( RepositoryIds.collector() ) : dumpRepositories;

final Map<RepositoryId, RepositorySettings> repoSettings = SYSTEM_REPO_IDS.stream()
.collect(
Collectors.toMap( Function.identity(), repo -> repositoryEntryService.getRepositoryEntry( repo ).getSettings() ) );
this.eventPublisher.publish( RepositoryEvents.restoreInitialized() );

repositoryEntryService.findRepositoryEntryIds().
stream().
filter( Predicate.isEqual( SystemConstants.SYSTEM_REPO_ID ).
or( SYSTEM_REPO_IDS::contains ).
negate() ).
forEach( this::doDeleteRepository );
if ( params.getListener() != null )
{
final long branchesCount =
repositoriesToLoad.stream().flatMap( repositoryId -> dumpReader.getBranches( repositoryId ).stream() ).count();
params.getListener().totalBranches( branchesCount );
}

SYSTEM_REPO_IDS.forEach( this::doDeleteRepository );
final boolean includeVersions = params.isIncludeVersions();

// system-repo must be deleted last
// Collect settings for repositories before deleting them
final Map<RepositoryId, RepositorySettings> repoSettings = repositoriesToLoad.stream()
.filter( repositoryId -> repositoryEntryService.getRepositoryEntry( repositoryId ) != null )
.collect( Collectors.toMap( Function.identity(), repo -> repositoryEntryService.getRepositoryEntry( repo ).getSettings() ) );

// Delete repositories
if ( isPartialLoad )
{
RepositoryIds systemReposToDelete =
repositoriesToLoad.stream().filter( SYSTEM_REPO_IDS::contains ).collect( RepositoryIds.collector() );
Comment on lines +353 to +354
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The variable systemReposToDelete is defined but never used in this code block. The actual deletion happens on line 362 using systemReposToDelete.forEach(...), but this variable appears to be redundant with the filtering already done on lines 357-360.

Copilot uses AI. Check for mistakes.

// Partial load: delete only repositories that will be loaded
repositoriesToLoad.stream()
.filter( repositoryId -> repositoryEntryService.getRepositoryEntry( repositoryId ) != null )
.filter( Predicate.not( SYSTEM_REPO_IDS::contains ) )
.forEach( this::doDeleteRepository );
Comment on lines +357 to +360
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

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

The code filters out system repositories then defines a separate variable systemReposToDelete that is used later. Consider combining these operations or clarifying the logic by filtering non-system repos in a single pass to avoid confusion about which repositories are being deleted.

Copilot uses AI. Check for mistakes.

systemReposToDelete.forEach( this::doDeleteRepository );

}
else
{
// Full load: delete all repositories (system-repo last)
repositoryEntryService.findRepositoryEntryIds()
.stream()
.filter( Predicate.isEqual( SystemConstants.SYSTEM_REPO_ID ).or( SYSTEM_REPO_IDS::contains ).negate() )
.forEach( this::doDeleteRepository );

SYSTEM_REPO_IDS.forEach( this::doDeleteRepository );
doDeleteRepository( SystemConstants.SYSTEM_REPO_ID );

// Load system-repo to be able to read repository settings and data
initAndLoad( includeVersions, results, dumpReader, SystemConstants.SYSTEM_REPO_ID, currentSystemSettings, null,
AttachedBinaries.empty() );
// Load system-repo first to be able to read repository settings and data
initAndLoad( includeVersions, results, dumpReader, SystemConstants.SYSTEM_REPO_ID,
repoSettings.get( SystemConstants.SYSTEM_REPO_ID ), null, AttachedBinaries.empty() );

// Transient repositories are not part of the dump. Clean them up.
final RepositoryIds repositoryEntryIds = repositoryEntryService.findRepositoryEntryIds();
for ( RepositoryId repositoryId : repositoryEntryIds )
for ( RepositoryId repositoryId : repositoryEntryService.findRepositoryEntryIds() )
{
if ( repositoryEntryService.getRepositoryEntry( repositoryId ).isTransient() )
{
repositoryEntryService.deleteRepositoryEntry( repositoryId );
}
}
}

// Load other system repositories (auditlog, scheduler, app)
SYSTEM_REPO_IDS.stream().filter( repositoriesToLoad::contains ).forEach( repositoryId -> {
final RepositorySettings settings = repoSettings.getOrDefault( repositoryId, RepositorySettings.create().build() );
if ( dumpRepositories.contains( repositoryId ) )
{
initAndLoad( includeVersions, results, dumpReader, repositoryId, settings, new PropertyTree(), AttachedBinaries.empty() );
}
else if ( !isPartialLoad )
{
// Full load: recreate missing system repos with current settings
initializeRepo( repositoryId, settings, null, AttachedBinaries.empty() );
createRootNode( repositoryId );
}
} );

// Load non-system repositories
repositoriesToLoad.stream()
.filter( Predicate.isEqual( SystemConstants.SYSTEM_REPO_ID ).or( SYSTEM_REPO_IDS::contains ).negate() )
.forEach( repositoryId -> {
final RepositorySettings settings = repoSettings.getOrDefault( repositoryId, RepositorySettings.create().build() );

// Load other system repositories
SYSTEM_REPO_IDS.forEach( repositoryId -> {
if ( dumpRepositories.contains( repositoryId ) )
if ( isPartialLoad )
{
// Dump contains repository. Do a normal load.
initAndLoad( includeVersions, results, dumpReader, repositoryId, repoSettings.get( repositoryId ), new PropertyTree(),
AttachedBinaries.empty() );
// Partial load: get data and attachments from dump's system-repo
final RepositoryEntry dumpRepoEntry = dumpReader.getRepositoryEntry( repositoryId );
final PropertyTree data = dumpRepoEntry != null ? dumpRepoEntry.getData() : null;
final AttachedBinaries attachments = dumpRepoEntry != null ? dumpRepoEntry.getAttachments() : AttachedBinaries.empty();
initAndLoad( includeVersions, results, dumpReader, repositoryId, settings, data, attachments );
}
else
{
// If it is an old dump it does not contain repo. It should be recreated with current settings
initializeRepo( repositoryId, repoSettings.get( repositoryId ), null, AttachedBinaries.empty() );
createRootNode( repositoryId );
// Full load: get data and attachments from loaded system-repo
final RepositoryEntry repository = repositoryEntryService.getRepositoryEntry( repositoryId );
initAndLoad( includeVersions, results, dumpReader, repositoryId, repository.getSettings(), repository.getData(),
repository.getAttachments() );
}

} );

// Load non-system repositories
dumpRepositories.
stream().
filter( Predicate.isEqual( SystemConstants.SYSTEM_REPO_ID ).
or( SYSTEM_REPO_IDS::contains ).
negate() ).
forEach( repositoryId -> {
final RepositoryEntry repository = repositoryEntryService.getRepositoryEntry( repositoryId );
final RepositorySettings settings = repository.getSettings();
final PropertyTree data = repository.getData();
final AttachedBinaries attachedBinaries = repository.getAttachments();
initAndLoad( includeVersions, results, dumpReader, repositoryId, settings, data, attachedBinaries );
} );

this.eventPublisher.publish( RepositoryEvents.restored() );
LOG.info( "Dump Load completed" );
}
catch ( IOException e )
{
throw new UncheckedIOException( e );
}
return results.build();
}

void verifyOrUpdateDumpVersion( final Path basePath, final SystemLoadParams params, final DumpReader dumpReader )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import com.google.common.io.LineProcessor;

import com.enonic.xp.blob.BlobKey;
import com.enonic.xp.node.NodeVersionKey;
import com.enonic.xp.blob.SegmentLevel;
import com.enonic.xp.branch.Branch;
import com.enonic.xp.branch.Branches;
Expand All @@ -29,19 +28,29 @@
import com.enonic.xp.dump.SystemDumpResult;
import com.enonic.xp.dump.SystemLoadListener;
import com.enonic.xp.dump.VersionsLoadResult;
import com.enonic.xp.index.ChildOrder;
import com.enonic.xp.node.Node;
import com.enonic.xp.node.NodeId;
import com.enonic.xp.node.NodeVersion;
import com.enonic.xp.node.NodeVersionKey;
import com.enonic.xp.repo.impl.dump.FilePaths;
import com.enonic.xp.repo.impl.dump.PathRef;
import com.enonic.xp.repo.impl.dump.RepoDumpException;
import com.enonic.xp.repo.impl.dump.RepoLoadException;
import com.enonic.xp.repo.impl.dump.blobstore.BlobReference;
import com.enonic.xp.repo.impl.dump.model.BranchDumpEntry;
import com.enonic.xp.repo.impl.dump.model.DumpMeta;
import com.enonic.xp.repo.impl.dump.serializer.json.DumpMetaJsonSerializer;
import com.enonic.xp.repo.impl.dump.serializer.json.JsonDumpSerializer;
import com.enonic.xp.repo.impl.node.NodeConstants;
import com.enonic.xp.repo.impl.node.json.NodeVersionJsonSerializer;
import com.enonic.xp.repo.impl.repository.RepositoryEntry;
import com.enonic.xp.repo.impl.repository.RepositoryNodeTranslator;
import com.enonic.xp.repository.RepositoryConstants;
import com.enonic.xp.repository.RepositoryId;
import com.enonic.xp.repository.RepositoryIds;
import com.enonic.xp.repository.RepositorySegmentUtils;
import com.enonic.xp.security.SystemConstants;

public abstract class AbstractDumpReader
implements DumpReader
Expand Down Expand Up @@ -195,6 +204,56 @@ public DumpMeta getDumpMeta()
return readDumpMetaData();
}

@Override
public RepositoryEntry getRepositoryEntry( final RepositoryId repositoryId )
{
final PathRef tarFile = filePaths.branchMetaPath( SystemConstants.SYSTEM_REPO_ID, SystemConstants.BRANCH_SYSTEM );

if ( !exists( tarFile ) )
{
return null;
}

final JsonDumpSerializer serializer = new JsonDumpSerializer();
final NodeId targetNodeId = NodeId.from( repositoryId.toString() );

try (TarArchiveInputStream tarInputStream = openStream( tarFile ))
{
TarArchiveEntry entry = tarInputStream.getNextEntry();
while ( entry != null )
{
final String content = readEntry( tarInputStream );
final BranchDumpEntry branchDumpEntry = serializer.toBranchMetaEntry( content );

if ( targetNodeId.equals( branchDumpEntry.getNodeId() ) )
{
final NodeVersionKey nodeVersionKey = branchDumpEntry.getMeta().nodeVersionKey();
final NodeVersion nodeVersion = get( SystemConstants.SYSTEM_REPO_ID, nodeVersionKey );

final Node node = Node.create()
.id( branchDumpEntry.getNodeId() )
.childOrder( ChildOrder.defaultOrder() )
.data( nodeVersion.getData() )
.name( repositoryId.toString() )
.parentPath( RepositoryConstants.REPOSITORY_STORAGE_PARENT_PATH )
.permissions( nodeVersion.getPermissions() )
.attachedBinaries( nodeVersion.getAttachedBinaries() )
.build();

return RepositoryNodeTranslator.toRepository( node );
}

entry = tarInputStream.getNextEntry();
}
}
catch ( IOException e )
{
throw new RepoDumpException( "Cannot read repository entry from dump", e );
}

return null;
}

@Override
public void close()
throws IOException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
import com.google.common.io.LineProcessor;

import com.enonic.xp.blob.BlobKey;
import com.enonic.xp.node.NodeVersionKey;
import com.enonic.xp.branch.Branch;
import com.enonic.xp.branch.Branches;
import com.enonic.xp.dump.BranchLoadResult;
import com.enonic.xp.dump.CommitsLoadResult;
import com.enonic.xp.dump.VersionsLoadResult;
import com.enonic.xp.node.NodeVersion;
import com.enonic.xp.node.NodeVersionKey;
import com.enonic.xp.repo.impl.dump.model.DumpMeta;
import com.enonic.xp.repo.impl.repository.RepositoryEntry;
import com.enonic.xp.repository.RepositoryId;
import com.enonic.xp.repository.RepositoryIds;

Expand All @@ -35,4 +36,6 @@ public interface DumpReader
ByteSource getBinary( RepositoryId repositoryId, BlobKey blobKey );

DumpMeta getDumpMeta();

RepositoryEntry getRepositoryEntry( RepositoryId repositoryId );
}
Loading