mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 00:15:44 +01:00
Merge with 2.0.0-m3
This commit is contained in:
@@ -58,6 +58,8 @@ import sonia.scm.util.IOUtil;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -77,7 +79,7 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
|
||||
private final ClassLoader parent;
|
||||
private final Set<PluginWrapper> plugins;
|
||||
private Injector injector;
|
||||
|
||||
|
||||
public interface Factory {
|
||||
ScmContextListener create(ClassLoader parent, Set<PluginWrapper> plugins);
|
||||
}
|
||||
@@ -183,6 +185,18 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
|
||||
}
|
||||
|
||||
super.contextDestroyed(servletContextEvent);
|
||||
|
||||
for (PluginWrapper plugin : getPlugins()) {
|
||||
ClassLoader pcl = plugin.getClassLoader();
|
||||
|
||||
if (pcl instanceof Closeable) {
|
||||
try {
|
||||
((Closeable) pcl).close();
|
||||
} catch (IOException ex) {
|
||||
LOG.warn("could not close plugin classloader", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeCloseables() {
|
||||
@@ -205,6 +219,4 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
|
||||
private void destroyServletContextListeners(ServletContextEvent event) {
|
||||
injector.getInstance(ServletContextListenerHolder.class).contextDestroyed(event);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ import com.github.legman.Subscribe;
|
||||
import com.google.inject.servlet.GuiceFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.Stage;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
@@ -99,11 +97,8 @@ public class BootstrapContextFilter extends GuiceFilter
|
||||
|
||||
initGuice();
|
||||
|
||||
if (SCMContext.getContext().getStage() == Stage.DEVELOPMENT)
|
||||
{
|
||||
logger.info("register for restart events");
|
||||
ScmEventBus.getInstance().register(this);
|
||||
}
|
||||
logger.info("register for restart events");
|
||||
ScmEventBus.getInstance().register(this);
|
||||
}
|
||||
|
||||
public void initGuice() throws ServletException {
|
||||
|
||||
@@ -46,6 +46,7 @@ import sonia.scm.ScmEventBusModule;
|
||||
import sonia.scm.ScmInitializerModule;
|
||||
import sonia.scm.Stage;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.migration.UpdateException;
|
||||
import sonia.scm.plugin.DefaultPluginLoader;
|
||||
import sonia.scm.plugin.Plugin;
|
||||
import sonia.scm.plugin.PluginException;
|
||||
@@ -54,6 +55,7 @@ import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.plugin.PluginWrapper;
|
||||
import sonia.scm.plugin.PluginsInternal;
|
||||
import sonia.scm.plugin.SmpArchive;
|
||||
import sonia.scm.update.MigrationWizardContextListener;
|
||||
import sonia.scm.update.UpdateEngine;
|
||||
import sonia.scm.util.ClassLoaders;
|
||||
import sonia.scm.util.IOUtil;
|
||||
@@ -110,18 +112,6 @@ public class BootstrapContextListener implements ServletContextListener {
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
contextListener.contextDestroyed(sce);
|
||||
|
||||
for (PluginWrapper plugin : contextListener.getPlugins()) {
|
||||
ClassLoader pcl = plugin.getClassLoader();
|
||||
|
||||
if (pcl instanceof Closeable) {
|
||||
try {
|
||||
((Closeable) pcl).close();
|
||||
} catch (IOException ex) {
|
||||
logger.warn("could not close plugin classloader", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context = null;
|
||||
contextListener = null;
|
||||
}
|
||||
@@ -151,27 +141,59 @@ public class BootstrapContextListener implements ServletContextListener {
|
||||
}
|
||||
|
||||
private void createContextListener(File pluginDirectory) {
|
||||
ClassLoader cl;
|
||||
Set<PluginWrapper> plugins;
|
||||
PluginLoader pluginLoader;
|
||||
|
||||
try {
|
||||
renameOldPluginsFolder(pluginDirectory);
|
||||
|
||||
if (!isCorePluginExtractionDisabled()) {
|
||||
extractCorePlugins(context, pluginDirectory);
|
||||
} else {
|
||||
logger.info("core plugin extraction is disabled");
|
||||
}
|
||||
|
||||
ClassLoader cl = ClassLoaders.getContextClassLoader(BootstrapContextListener.class);
|
||||
cl = ClassLoaders.getContextClassLoader(BootstrapContextListener.class);
|
||||
|
||||
Set<PluginWrapper> plugins = PluginsInternal.collectPlugins(cl, pluginDirectory.toPath());
|
||||
plugins = PluginsInternal.collectPlugins(cl, pluginDirectory.toPath());
|
||||
|
||||
PluginLoader pluginLoader = new DefaultPluginLoader(context, cl, plugins);
|
||||
pluginLoader = new DefaultPluginLoader(context, cl, plugins);
|
||||
|
||||
Injector bootstrapInjector = createBootstrapInjector(pluginLoader);
|
||||
|
||||
processUpdates(pluginLoader, bootstrapInjector);
|
||||
|
||||
contextListener = bootstrapInjector.getInstance(ScmContextListener.Factory.class).create(cl, plugins);
|
||||
} catch (IOException ex) {
|
||||
throw new PluginLoadException("could not load plugins", ex);
|
||||
}
|
||||
|
||||
Injector bootstrapInjector = createBootstrapInjector(pluginLoader);
|
||||
|
||||
startEitherMigrationOrNormalServlet(cl, plugins, pluginLoader, bootstrapInjector);
|
||||
}
|
||||
|
||||
private void startEitherMigrationOrNormalServlet(ClassLoader cl, Set<PluginWrapper> plugins, PluginLoader pluginLoader, Injector bootstrapInjector) {
|
||||
MigrationWizardContextListener wizardContextListener = prepareWizardIfNeeded(bootstrapInjector);
|
||||
|
||||
if (wizardContextListener.wizardNecessary()) {
|
||||
contextListener = wizardContextListener;
|
||||
} else {
|
||||
processUpdates(pluginLoader, bootstrapInjector);
|
||||
contextListener = bootstrapInjector.getInstance(ScmContextListener.Factory.class).create(cl, plugins);
|
||||
}
|
||||
}
|
||||
|
||||
private void renameOldPluginsFolder(File pluginDirectory) {
|
||||
if (new File(pluginDirectory, "classpath.xml").exists()) {
|
||||
File backupDirectory = new File(pluginDirectory.getParentFile(), "plugins.v1");
|
||||
boolean renamed = pluginDirectory.renameTo(backupDirectory);
|
||||
if (renamed) {
|
||||
logger.warn("moved old plugins directory to {}", backupDirectory);
|
||||
} else {
|
||||
throw new UpdateException("could not rename existing v1 plugin directory");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MigrationWizardContextListener prepareWizardIfNeeded(Injector bootstrapInjector) {
|
||||
return new MigrationWizardContextListener(bootstrapInjector);
|
||||
}
|
||||
|
||||
private Injector createBootstrapInjector(PluginLoader pluginLoader) {
|
||||
@@ -402,7 +424,7 @@ public class BootstrapContextListener implements ServletContextListener {
|
||||
private ServletContext context;
|
||||
|
||||
/** Field description */
|
||||
private ScmContextListener contextListener;
|
||||
private ServletContextListener contextListener;
|
||||
|
||||
/** Field description */
|
||||
private boolean registered = false;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package sonia.scm.update;
|
||||
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.servlet.GuiceServletContextListener;
|
||||
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
|
||||
|
||||
public class MigrationWizardContextListener extends GuiceServletContextListener {
|
||||
|
||||
private final Injector bootstrapInjector;
|
||||
|
||||
public MigrationWizardContextListener(Injector bootstrapInjector) {
|
||||
this.bootstrapInjector = bootstrapInjector;
|
||||
}
|
||||
|
||||
public boolean wizardNecessary() {
|
||||
return !bootstrapInjector.getInstance(XmlRepositoryV1UpdateStep.class).getRepositoriesWithoutMigrationStrategies().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Injector getInjector() {
|
||||
return bootstrapInjector.createChildInjector(new MigrationWizardModule());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package sonia.scm.update;
|
||||
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.PushStateDispatcher;
|
||||
import sonia.scm.WebResourceServlet;
|
||||
|
||||
class MigrationWizardModule extends ServletModule {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MigrationWizardModule.class);
|
||||
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
LOG.info("==========================================================");
|
||||
LOG.info("= =");
|
||||
LOG.info("= STARTING MIGRATION SERVLET =");
|
||||
LOG.info("= =");
|
||||
LOG.info("= Open SCM-Manager in a browser to start the wizard. =");
|
||||
LOG.info("= =");
|
||||
LOG.info("==========================================================");
|
||||
bind(PushStateDispatcher.class).toInstance((request, response, uri) -> {});
|
||||
serve("/images/*", "/styles/*", "/favicon.ico").with(WebResourceServlet.class);
|
||||
serve("/*").with(MigrationWizardServlet.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package sonia.scm.update;
|
||||
|
||||
import com.github.mustachejava.DefaultMustacheFactory;
|
||||
import com.github.mustachejava.Mustache;
|
||||
import com.github.mustachejava.MustacheFactory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.boot.RestartEvent;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.update.repository.MigrationStrategy;
|
||||
import sonia.scm.update.repository.MigrationStrategyDao;
|
||||
import sonia.scm.update.repository.V1Repository;
|
||||
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
|
||||
import sonia.scm.util.ValidationUtil;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
|
||||
@Singleton
|
||||
class MigrationWizardServlet extends HttpServlet {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MigrationWizardServlet.class);
|
||||
|
||||
private final XmlRepositoryV1UpdateStep repositoryV1UpdateStep;
|
||||
private final MigrationStrategyDao migrationStrategyDao;
|
||||
|
||||
@Inject
|
||||
MigrationWizardServlet(XmlRepositoryV1UpdateStep repositoryV1UpdateStep, MigrationStrategyDao migrationStrategyDao) {
|
||||
this.repositoryV1UpdateStep = repositoryV1UpdateStep;
|
||||
this.migrationStrategyDao = migrationStrategyDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
||||
List<RepositoryLineEntry> repositoryLineEntries = getRepositoryLineEntries();
|
||||
doGet(req, resp, repositoryLineEntries);
|
||||
}
|
||||
|
||||
private void doGet(HttpServletRequest req, HttpServletResponse resp, List<RepositoryLineEntry> repositoryLineEntries) {
|
||||
HashMap<String, Object> model = new HashMap<>();
|
||||
|
||||
model.put("contextPath", req.getContextPath());
|
||||
model.put("submitUrl", req.getRequestURI());
|
||||
model.put("repositories", repositoryLineEntries);
|
||||
model.put("strategies", getMigrationStrategies());
|
||||
model.put("validationErrorsFound", repositoryLineEntries
|
||||
.stream()
|
||||
.anyMatch(entry -> entry.isNamespaceInvalid() || entry.isNameInvalid()));
|
||||
|
||||
respondWithTemplate(resp, model, "templates/repository-migration.mustache");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
|
||||
List<RepositoryLineEntry> repositoryLineEntries = getRepositoryLineEntries();
|
||||
|
||||
boolean validationErrorFound = false;
|
||||
for (RepositoryLineEntry repositoryLineEntry : repositoryLineEntries) {
|
||||
String id = repositoryLineEntry.getId();
|
||||
|
||||
String strategy = req.getParameter("strategy-" + id);
|
||||
if (!Strings.isNullOrEmpty(strategy)) {
|
||||
repositoryLineEntry.setSelectedStrategy(MigrationStrategy.valueOf(strategy));
|
||||
}
|
||||
|
||||
String namespace = req.getParameter("namespace-" + id);
|
||||
repositoryLineEntry.setNamespace(namespace);
|
||||
|
||||
String name = req.getParameter("name-" + id);
|
||||
repositoryLineEntry.setName(name);
|
||||
|
||||
if (!ValidationUtil.isRepositoryNameValid(namespace)) {
|
||||
repositoryLineEntry.setNamespaceValid(false);
|
||||
validationErrorFound = true;
|
||||
}
|
||||
if (!ValidationUtil.isRepositoryNameValid(name)) {
|
||||
repositoryLineEntry.setNameValid(false);
|
||||
validationErrorFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (validationErrorFound) {
|
||||
doGet(req, resp, repositoryLineEntries);
|
||||
return;
|
||||
}
|
||||
|
||||
repositoryLineEntries.stream()
|
||||
.map(RepositoryLineEntry::getId)
|
||||
.forEach(
|
||||
id -> {
|
||||
String strategy = req.getParameter("strategy-" + id);
|
||||
String namespace = req.getParameter("namespace-" + id);
|
||||
String name = req.getParameter("name-" + id);
|
||||
migrationStrategyDao.set(id, MigrationStrategy.valueOf(strategy), namespace, name);
|
||||
}
|
||||
);
|
||||
|
||||
Map<String, Object> model = Collections.singletonMap("contextPath", req.getContextPath());
|
||||
|
||||
respondWithTemplate(resp, model, "templates/repository-migration-restart.mustache");
|
||||
|
||||
ScmEventBus.getInstance().post(new RestartEvent(MigrationWizardServlet.class, "wrote migration data"));
|
||||
}
|
||||
|
||||
private List<RepositoryLineEntry> getRepositoryLineEntries() {
|
||||
List<V1Repository> repositoriesWithoutMigrationStrategies =
|
||||
repositoryV1UpdateStep.getRepositoriesWithoutMigrationStrategies();
|
||||
return repositoriesWithoutMigrationStrategies.stream()
|
||||
.map(RepositoryLineEntry::new)
|
||||
.sorted(comparing(RepositoryLineEntry::getPath))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private MigrationStrategy[] getMigrationStrategies() {
|
||||
return MigrationStrategy.values();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void respondWithTemplate(HttpServletResponse resp, Map<String, Object> model, String templateName) {
|
||||
MustacheFactory mf = new DefaultMustacheFactory();
|
||||
Mustache template = mf.compile(templateName);
|
||||
|
||||
PrintWriter writer;
|
||||
try {
|
||||
writer = resp.getWriter();
|
||||
} catch (IOException e) {
|
||||
LOG.error("could not create writer for response", e);
|
||||
resp.setStatus(500);
|
||||
return;
|
||||
}
|
||||
template.execute(writer, model);
|
||||
writer.flush();
|
||||
resp.setStatus(200);
|
||||
}
|
||||
|
||||
private static class RepositoryLineEntry {
|
||||
private final String id;
|
||||
private final String type;
|
||||
private final String path;
|
||||
private MigrationStrategy selectedStrategy;
|
||||
private String namespace;
|
||||
private String name;
|
||||
private boolean namespaceValid = true;
|
||||
private boolean nameValid = true;
|
||||
|
||||
public RepositoryLineEntry(V1Repository repository) {
|
||||
this.id = repository.getId();
|
||||
this.type = repository.getType();
|
||||
this.path = repository.getType() + "/" + repository.getName();
|
||||
this.selectedStrategy = MigrationStrategy.COPY;
|
||||
this.namespace = computeNewNamespace(repository);
|
||||
this.name = computeNewName(repository);
|
||||
}
|
||||
|
||||
private static String computeNewNamespace(V1Repository v1Repository) {
|
||||
String[] nameParts = getNameParts(v1Repository.getName());
|
||||
return nameParts.length > 1 ? nameParts[0] : v1Repository.getType();
|
||||
}
|
||||
|
||||
private static String computeNewName(V1Repository v1Repository) {
|
||||
String[] nameParts = getNameParts(v1Repository.getName());
|
||||
return nameParts.length == 1 ? nameParts[0] : concatPathElements(nameParts);
|
||||
}
|
||||
|
||||
private static String[] getNameParts(String v1Name) {
|
||||
return v1Name.split("/");
|
||||
}
|
||||
|
||||
private static String concatPathElements(String[] nameParts) {
|
||||
return Arrays.stream(nameParts).skip(1).collect(Collectors.joining("_"));
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public MigrationStrategy getSelectedStrategy() {
|
||||
return selectedStrategy;
|
||||
}
|
||||
|
||||
public List<RepositoryLineMigrationStrategy> getStrategies() {
|
||||
return Arrays.stream(MigrationStrategy.values())
|
||||
.map(s -> new RepositoryLineMigrationStrategy(s.name(), selectedStrategy == s))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setNamespaceValid(boolean namespaceValid) {
|
||||
this.namespaceValid = namespaceValid;
|
||||
}
|
||||
|
||||
public void setNameValid(boolean nameValid) {
|
||||
this.nameValid = nameValid;
|
||||
}
|
||||
|
||||
public void setSelectedStrategy(MigrationStrategy selectedStrategy) {
|
||||
this.selectedStrategy = selectedStrategy;
|
||||
}
|
||||
|
||||
public boolean isNamespaceInvalid() {
|
||||
return !namespaceValid;
|
||||
}
|
||||
|
||||
public boolean isNameInvalid() {
|
||||
return !nameValid;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RepositoryLineMigrationStrategy {
|
||||
|
||||
private final String name;
|
||||
private final boolean selected;
|
||||
|
||||
private RepositoryLineMigrationStrategy(String name, boolean selected) {
|
||||
this.name = name;
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package sonia.scm.update.repository;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.repository.RepositoryDirectoryHandler;
|
||||
import sonia.scm.repository.RepositoryLocationResolver;
|
||||
@@ -7,9 +9,14 @@ import sonia.scm.repository.RepositoryLocationResolver;
|
||||
import javax.inject.Inject;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Optional.of;
|
||||
|
||||
class CopyMigrationStrategy extends BaseMigrationStrategy {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CopyMigrationStrategy.class);
|
||||
|
||||
private final RepositoryLocationResolver locationResolver;
|
||||
|
||||
@Inject
|
||||
@@ -19,13 +26,14 @@ class CopyMigrationStrategy extends BaseMigrationStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path migrate(String id, String name, String type) {
|
||||
Path repositoryBasePath = locationResolver.forClass(Path.class).getLocation(id);
|
||||
public Optional<Path> migrate(String id, String name, String type) {
|
||||
Path repositoryBasePath = locationResolver.forClass(Path.class).createLocation(id);
|
||||
Path targetDataPath = repositoryBasePath
|
||||
.resolve(RepositoryDirectoryHandler.REPOSITORIES_NATIVE_DIRECTORY);
|
||||
Path sourceDataPath = getSourceDataPath(name, type);
|
||||
LOG.info("copying repository data from {} to {}", sourceDataPath, targetDataPath);
|
||||
copyData(sourceDataPath, targetDataPath);
|
||||
return repositoryBasePath;
|
||||
return of(repositoryBasePath);
|
||||
}
|
||||
|
||||
private void copyData(Path sourceDirectory, Path targetDirectory) {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package sonia.scm.update.repository;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
public class DeleteMigrationStrategy extends BaseMigrationStrategy {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DeleteMigrationStrategy.class);
|
||||
|
||||
@Inject
|
||||
DeleteMigrationStrategy(SCMContextProvider contextProvider) {
|
||||
super(contextProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Path> migrate(String id, String name, String type) {
|
||||
Path sourceDataPath = getSourceDataPath(name, type);
|
||||
try {
|
||||
IOUtil.delete(sourceDataPath.toFile(), true);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("could not delete old repository path for repository {} with type {} and id {}", name, type, id);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package sonia.scm.update.repository;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
|
||||
public class IgnoreMigrationStrategy implements MigrationStrategy.Instance {
|
||||
@Override
|
||||
public Optional<Path> migrate(String id, String name, String type) {
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,47 @@
|
||||
package sonia.scm.update.repository;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.repository.RepositoryDirectoryHandler;
|
||||
import sonia.scm.repository.RepositoryLocationResolver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Optional.of;
|
||||
|
||||
class InlineMigrationStrategy extends BaseMigrationStrategy {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InlineMigrationStrategy.class);
|
||||
|
||||
private final RepositoryLocationResolver locationResolver;
|
||||
|
||||
@Inject
|
||||
public InlineMigrationStrategy(SCMContextProvider contextProvider) {
|
||||
public InlineMigrationStrategy(SCMContextProvider contextProvider, RepositoryLocationResolver locationResolver) {
|
||||
super(contextProvider);
|
||||
this.locationResolver = locationResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path migrate(String id, String name, String type) {
|
||||
public Optional<Path> migrate(String id, String name, String type) {
|
||||
Path repositoryBasePath = getSourceDataPath(name, type);
|
||||
locationResolver.forClass(Path.class).setLocation(id, repositoryBasePath);
|
||||
Path targetDataPath = repositoryBasePath
|
||||
.resolve(RepositoryDirectoryHandler.REPOSITORIES_NATIVE_DIRECTORY);
|
||||
LOG.info("moving repository data from {} to {}", repositoryBasePath, targetDataPath);
|
||||
moveData(repositoryBasePath, targetDataPath);
|
||||
return repositoryBasePath;
|
||||
return of(repositoryBasePath);
|
||||
}
|
||||
|
||||
private void moveData(Path sourceDirectory, Path targetDirectory) {
|
||||
moveData(sourceDirectory, targetDirectory, false);
|
||||
}
|
||||
|
||||
private void moveData(Path sourceDirectory, Path targetDirectory, boolean deleteDirectory) {
|
||||
createDataDirectory(targetDirectory);
|
||||
listSourceDirectory(sourceDirectory)
|
||||
.filter(sourceFile -> !targetDirectory.equals(sourceFile))
|
||||
@@ -31,11 +49,18 @@ class InlineMigrationStrategy extends BaseMigrationStrategy {
|
||||
sourceFile -> {
|
||||
Path targetFile = targetDirectory.resolve(sourceFile.getFileName());
|
||||
if (Files.isDirectory(sourceFile)) {
|
||||
moveData(sourceFile, targetFile);
|
||||
moveData(sourceFile, targetFile, true);
|
||||
} else {
|
||||
moveFile(sourceFile, targetFile);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (deleteDirectory) {
|
||||
try {
|
||||
Files.delete(sourceDirectory);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("could not delete source repository directory {}", sourceDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,41 @@ package sonia.scm.update.repository;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
enum MigrationStrategy {
|
||||
public enum MigrationStrategy {
|
||||
|
||||
COPY(CopyMigrationStrategy.class),
|
||||
MOVE(MoveMigrationStrategy.class),
|
||||
INLINE(InlineMigrationStrategy.class);
|
||||
COPY(CopyMigrationStrategy.class,
|
||||
"Copy the repository data files to the new native location inside SCM-Manager home directory. " +
|
||||
"This will keep the original directory."),
|
||||
MOVE(MoveMigrationStrategy.class,
|
||||
"Move the repository data files to the new native location inside SCM-Manager home directory. " +
|
||||
"The original directory will be deleted."),
|
||||
INLINE(InlineMigrationStrategy.class,
|
||||
"Use the current directory where the repository data files are stored, but modify the directory " +
|
||||
"structure so that it can be used for SCM-Manager v2. The repository data files will be moved to a new " +
|
||||
"subdirectory 'data' inside the current directory."),
|
||||
IGNORE(IgnoreMigrationStrategy.class,
|
||||
"The repository will not be migrated and will not be visible inside SCM-Manager. " +
|
||||
"The data files will be kept at the current location."),
|
||||
DELETE(DeleteMigrationStrategy.class,
|
||||
"The repository will not be migrated and will not be visible inside SCM-Manager. " +
|
||||
"The data files will be deleted!");
|
||||
|
||||
private Class<? extends Instance> implementationClass;
|
||||
private final Class<? extends Instance> implementationClass;
|
||||
private final String description;
|
||||
|
||||
MigrationStrategy(Class<? extends Instance> implementationClass) {
|
||||
MigrationStrategy(Class<? extends Instance> implementationClass, String description) {
|
||||
this.implementationClass = implementationClass;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Class<? extends Instance> getImplementationClass() {
|
||||
return implementationClass;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
Instance from(Injector injector) {
|
||||
@@ -21,6 +45,6 @@ enum MigrationStrategy {
|
||||
}
|
||||
|
||||
interface Instance {
|
||||
Path migrate(String id, String name, String type);
|
||||
Optional<Path> migrate(String id, String name, String type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import sonia.scm.store.ConfigurationStore;
|
||||
import sonia.scm.store.ConfigurationStoreFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
|
||||
@Singleton
|
||||
public class MigrationStrategyDao {
|
||||
|
||||
private final RepositoryMigrationPlan plan;
|
||||
@@ -17,12 +19,12 @@ public class MigrationStrategyDao {
|
||||
this.plan = store.getOptional().orElse(new RepositoryMigrationPlan());
|
||||
}
|
||||
|
||||
public Optional<MigrationStrategy> get(String id) {
|
||||
public Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> get(String id) {
|
||||
return plan.get(id);
|
||||
}
|
||||
|
||||
public void set(String repositoryId, MigrationStrategy strategy) {
|
||||
plan.set(repositoryId, strategy);
|
||||
public void set(String repositoryId, MigrationStrategy strategy, String newNamespace, String newName) {
|
||||
plan.set(repositoryId, strategy, newNamespace, newName);
|
||||
store.set(plan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Optional.of;
|
||||
|
||||
class MoveMigrationStrategy extends BaseMigrationStrategy {
|
||||
|
||||
@@ -27,14 +29,15 @@ class MoveMigrationStrategy extends BaseMigrationStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path migrate(String id, String name, String type) {
|
||||
Path repositoryBasePath = locationResolver.forClass(Path.class).getLocation(id);
|
||||
public Optional<Path> migrate(String id, String name, String type) {
|
||||
Path repositoryBasePath = locationResolver.forClass(Path.class).createLocation(id);
|
||||
Path targetDataPath = repositoryBasePath
|
||||
.resolve(RepositoryDirectoryHandler.REPOSITORIES_NATIVE_DIRECTORY);
|
||||
Path sourceDataPath = getSourceDataPath(name, type);
|
||||
LOG.info("moving repository data from {} to {}", sourceDataPath, targetDataPath);
|
||||
moveData(sourceDataPath, targetDataPath);
|
||||
deleteOldDataDir(getTypeDependentPath(type), name);
|
||||
return repositoryBasePath;
|
||||
return of(repositoryBasePath);
|
||||
}
|
||||
|
||||
private void deleteOldDataDir(Path rootPath, String name) {
|
||||
|
||||
@@ -13,57 +13,74 @@ import static java.util.Arrays.asList;
|
||||
@XmlRootElement(name = "repository-migration")
|
||||
class RepositoryMigrationPlan {
|
||||
|
||||
private List<RepositoryEntry> entries;
|
||||
private List<RepositoryMigrationEntry> entries;
|
||||
|
||||
RepositoryMigrationPlan() {
|
||||
this(new RepositoryEntry[0]);
|
||||
this(new RepositoryMigrationEntry[0]);
|
||||
}
|
||||
|
||||
RepositoryMigrationPlan(RepositoryEntry... entries) {
|
||||
RepositoryMigrationPlan(RepositoryMigrationEntry... entries) {
|
||||
this.entries = new ArrayList<>(asList(entries));
|
||||
}
|
||||
|
||||
Optional<MigrationStrategy> get(String repositoryId) {
|
||||
return findEntry(repositoryId)
|
||||
.map(RepositoryEntry::getDataMigrationStrategy);
|
||||
}
|
||||
|
||||
public void set(String repositoryId, MigrationStrategy strategy) {
|
||||
Optional<RepositoryEntry> entry = findEntry(repositoryId);
|
||||
if (entry.isPresent()) {
|
||||
entry.get().setStrategy(strategy);
|
||||
} else {
|
||||
entries.add(new RepositoryEntry(repositoryId, strategy));
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<RepositoryEntry> findEntry(String repositoryId) {
|
||||
Optional<RepositoryMigrationEntry> get(String repositoryId) {
|
||||
return entries.stream()
|
||||
.filter(repositoryEntry -> repositoryId.equals(repositoryEntry.repositoryId))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public void set(String repositoryId, MigrationStrategy strategy, String newNamespace, String newName) {
|
||||
Optional<RepositoryMigrationEntry> entry = get(repositoryId);
|
||||
if (entry.isPresent()) {
|
||||
entry.get().setStrategy(strategy);
|
||||
entry.get().setNewNamespace(newNamespace);
|
||||
entry.get().setNewName(newName);
|
||||
} else {
|
||||
entries.add(new RepositoryMigrationEntry(repositoryId, strategy, newNamespace, newName));
|
||||
}
|
||||
}
|
||||
|
||||
@XmlRootElement(name = "entries")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
static class RepositoryEntry {
|
||||
static class RepositoryMigrationEntry {
|
||||
|
||||
private String repositoryId;
|
||||
private MigrationStrategy dataMigrationStrategy;
|
||||
private String newNamespace;
|
||||
private String newName;
|
||||
|
||||
RepositoryEntry() {
|
||||
RepositoryMigrationEntry() {
|
||||
}
|
||||
|
||||
RepositoryEntry(String repositoryId, MigrationStrategy dataMigrationStrategy) {
|
||||
RepositoryMigrationEntry(String repositoryId, MigrationStrategy dataMigrationStrategy, String newNamespace, String newName) {
|
||||
this.repositoryId = repositoryId;
|
||||
this.dataMigrationStrategy = dataMigrationStrategy;
|
||||
this.newNamespace = newNamespace;
|
||||
this.newName = newName;
|
||||
}
|
||||
|
||||
public MigrationStrategy getDataMigrationStrategy() {
|
||||
return dataMigrationStrategy;
|
||||
}
|
||||
|
||||
public String getNewNamespace() {
|
||||
return newNamespace;
|
||||
}
|
||||
|
||||
public String getNewName() {
|
||||
return newName;
|
||||
}
|
||||
|
||||
private void setStrategy(MigrationStrategy strategy) {
|
||||
this.dataMigrationStrategy = strategy;
|
||||
}
|
||||
|
||||
private void setNewNamespace(String newNamespace) {
|
||||
this.newNamespace = newNamespace;
|
||||
}
|
||||
|
||||
private void setNewName(String newName) {
|
||||
this.newName = newName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package sonia.scm.update.repository;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "permissions")
|
||||
class V1Permission {
|
||||
private boolean groupPermission;
|
||||
private String name;
|
||||
private String type;
|
||||
|
||||
public boolean isGroupPermission() {
|
||||
return groupPermission;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package sonia.scm.update.repository;
|
||||
|
||||
import sonia.scm.update.properties.V1Properties;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.util.List;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "repositories")
|
||||
public class V1Repository {
|
||||
private String contact;
|
||||
private long creationDate;
|
||||
private Long lastModified;
|
||||
private String description;
|
||||
private String id;
|
||||
private String name;
|
||||
private boolean isPublic;
|
||||
private boolean archived;
|
||||
private String type;
|
||||
private List<V1Permission> permissions;
|
||||
private V1Properties properties;
|
||||
|
||||
public V1Repository() {
|
||||
}
|
||||
|
||||
public V1Repository(String id, String type, String name) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getContact() {
|
||||
return contact;
|
||||
}
|
||||
|
||||
public long getCreationDate() {
|
||||
return creationDate;
|
||||
}
|
||||
|
||||
public Long getLastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
return isPublic;
|
||||
}
|
||||
|
||||
public boolean isArchived() {
|
||||
return archived;
|
||||
}
|
||||
|
||||
public List<V1Permission> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public V1Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "V1Repository{" +
|
||||
", contact='" + contact + '\'' +
|
||||
", creationDate=" + creationDate +
|
||||
", lastModified=" + lastModified +
|
||||
", description='" + description + '\'' +
|
||||
", id='" + id + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", isPublic=" + isPublic +
|
||||
", archived=" + archived +
|
||||
", type='" + type + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -28,11 +28,12 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static sonia.scm.version.Version.parse;
|
||||
@@ -102,13 +103,30 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(V1RepositoryDatabase.class);
|
||||
readV1Database(jaxbContext).ifPresent(
|
||||
v1Database -> {
|
||||
v1Database.repositoryList.repositories.forEach(this::readMigrationStrategy);
|
||||
v1Database.repositoryList.repositories.forEach(this::readMigrationEntry);
|
||||
v1Database.repositoryList.repositories.forEach(this::update);
|
||||
backupOldRepositoriesFile();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public List<V1Repository> getRepositoriesWithoutMigrationStrategies() {
|
||||
if (!resolveV1File().exists()) {
|
||||
LOG.info("no v1 repositories database file found");
|
||||
return emptyList();
|
||||
}
|
||||
try {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(XmlRepositoryV1UpdateStep.V1RepositoryDatabase.class);
|
||||
return readV1Database(jaxbContext)
|
||||
.map(v1Database -> v1Database.repositoryList.repositories.stream())
|
||||
.orElse(Stream.empty())
|
||||
.filter(v1Repository -> !this.findMigrationStrategy(v1Repository).isPresent())
|
||||
.collect(Collectors.toList());
|
||||
} catch (JAXBException e) {
|
||||
throw new UpdateException("could not read v1 repository database", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void backupOldRepositoriesFile() {
|
||||
Path configDir = contextProvider.getBaseDirectory().toPath().resolve(StoreConstants.CONFIG_DIRECTORY_NAME);
|
||||
Path oldRepositoriesFile = configDir.resolve("repositories.xml");
|
||||
@@ -122,61 +140,59 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep {
|
||||
}
|
||||
|
||||
private void update(V1Repository v1Repository) {
|
||||
Path destination = handleDataDirectory(v1Repository);
|
||||
Repository repository = new Repository(
|
||||
v1Repository.id,
|
||||
v1Repository.type,
|
||||
getNamespace(v1Repository),
|
||||
getName(v1Repository),
|
||||
v1Repository.contact,
|
||||
v1Repository.description,
|
||||
createPermissions(v1Repository));
|
||||
LOG.info("creating new repository {} with id {} from old repository {} in directory {}", repository.getNamespaceAndName(), repository.getId(), v1Repository.name, destination);
|
||||
repositoryDao.add(repository, destination);
|
||||
propertyStore.put(v1Repository.id, v1Repository.properties);
|
||||
RepositoryMigrationPlan.RepositoryMigrationEntry repositoryMigrationEntry = readMigrationEntry(v1Repository);
|
||||
Optional<Path> destination = handleDataDirectory(v1Repository, repositoryMigrationEntry.getDataMigrationStrategy());
|
||||
LOG.info("using strategy {} to migrate repository {} with id {} using new namespace {} and name {}",
|
||||
repositoryMigrationEntry.getDataMigrationStrategy().getClass(),
|
||||
v1Repository.getName(),
|
||||
v1Repository.getId(),
|
||||
repositoryMigrationEntry.getNewNamespace(),
|
||||
repositoryMigrationEntry.getNewName());
|
||||
destination.ifPresent(
|
||||
newPath -> {
|
||||
Repository repository = new Repository(
|
||||
v1Repository.getId(),
|
||||
v1Repository.getType(),
|
||||
repositoryMigrationEntry.getNewNamespace(),
|
||||
repositoryMigrationEntry.getNewName(),
|
||||
v1Repository.getContact(),
|
||||
v1Repository.getDescription(),
|
||||
createPermissions(v1Repository));
|
||||
LOG.info("creating new repository {} with id {} from old repository {} in directory {}", repository.getNamespaceAndName(), repository.getId(), v1Repository.getName(), newPath);
|
||||
repositoryDao.add(repository, newPath);
|
||||
propertyStore.put(v1Repository.getId(), v1Repository.getProperties());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private Path handleDataDirectory(V1Repository v1Repository) {
|
||||
MigrationStrategy dataMigrationStrategy = readMigrationStrategy(v1Repository);
|
||||
return dataMigrationStrategy.from(injector).migrate(v1Repository.id, v1Repository.name, v1Repository.type);
|
||||
private Optional<Path> handleDataDirectory(V1Repository v1Repository, MigrationStrategy dataMigrationStrategy) {
|
||||
return dataMigrationStrategy
|
||||
.from(injector)
|
||||
.migrate(v1Repository.getId(), v1Repository.getName(), v1Repository.getType());
|
||||
}
|
||||
|
||||
private MigrationStrategy readMigrationStrategy(V1Repository v1Repository) {
|
||||
return migrationStrategyDao.get(v1Repository.id)
|
||||
.orElseThrow(() -> new IllegalStateException("no strategy found for repository with id " + v1Repository.id + " and name " + v1Repository.name));
|
||||
private RepositoryMigrationPlan.RepositoryMigrationEntry readMigrationEntry(V1Repository v1Repository) {
|
||||
return findMigrationStrategy(v1Repository)
|
||||
.orElseThrow(() -> new IllegalStateException("no strategy found for repository with id " + v1Repository.getId() + " and name " + v1Repository.getName()));
|
||||
}
|
||||
|
||||
private Optional<RepositoryMigrationPlan.RepositoryMigrationEntry> findMigrationStrategy(V1Repository v1Repository) {
|
||||
return migrationStrategyDao.get(v1Repository.getId());
|
||||
}
|
||||
|
||||
private RepositoryPermission[] createPermissions(V1Repository v1Repository) {
|
||||
if (v1Repository.permissions == null) {
|
||||
if (v1Repository.getPermissions() == null) {
|
||||
return new RepositoryPermission[0];
|
||||
}
|
||||
return v1Repository.permissions
|
||||
return v1Repository.getPermissions()
|
||||
.stream()
|
||||
.map(this::createPermission)
|
||||
.toArray(RepositoryPermission[]::new);
|
||||
}
|
||||
|
||||
private RepositoryPermission createPermission(V1Permission v1Permission) {
|
||||
LOG.info("creating permission {} for {}", v1Permission.type, v1Permission.name);
|
||||
return new RepositoryPermission(v1Permission.name, v1Permission.type, v1Permission.groupPermission);
|
||||
}
|
||||
|
||||
private String getNamespace(V1Repository v1Repository) {
|
||||
String[] nameParts = getNameParts(v1Repository.name);
|
||||
return nameParts.length > 1 ? nameParts[0] : v1Repository.type;
|
||||
}
|
||||
|
||||
private String getName(V1Repository v1Repository) {
|
||||
String[] nameParts = getNameParts(v1Repository.name);
|
||||
return nameParts.length == 1 ? nameParts[0] : concatPathElements(nameParts);
|
||||
}
|
||||
|
||||
private String concatPathElements(String[] nameParts) {
|
||||
return Arrays.stream(nameParts).skip(1).collect(Collectors.joining("_"));
|
||||
}
|
||||
|
||||
private String[] getNameParts(String v1Name) {
|
||||
return v1Name.split("/");
|
||||
LOG.info("creating permission {} for {}", v1Permission.getType(), v1Permission.getName());
|
||||
return new RepositoryPermission(v1Permission.getName(), v1Permission.getType(), v1Permission.isGroupPermission());
|
||||
}
|
||||
|
||||
private Optional<V1RepositoryDatabase> readV1Database(JAXBContext jaxbContext) throws JAXBException {
|
||||
@@ -195,45 +211,6 @@ public class XmlRepositoryV1UpdateStep implements UpdateStep {
|
||||
).toFile();
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "permissions")
|
||||
private static class V1Permission {
|
||||
private boolean groupPermission;
|
||||
private String name;
|
||||
private String type;
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "repositories")
|
||||
private static class V1Repository {
|
||||
private String contact;
|
||||
private long creationDate;
|
||||
private Long lastModified;
|
||||
private String description;
|
||||
private String id;
|
||||
private String name;
|
||||
private boolean isPublic;
|
||||
private boolean archived;
|
||||
private String type;
|
||||
private List<V1Permission> permissions;
|
||||
private V1Properties properties;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "V1Repository{" +
|
||||
", contact='" + contact + '\'' +
|
||||
", creationDate=" + creationDate +
|
||||
", lastModified=" + lastModified +
|
||||
", description='" + description + '\'' +
|
||||
", id='" + id + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", isPublic=" + isPublic +
|
||||
", archived=" + archived +
|
||||
", type='" + type + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private static class RepositoryList {
|
||||
@XmlElement(name = "repository")
|
||||
private List<V1Repository> repositories;
|
||||
|
||||
Reference in New Issue
Block a user