diff --git a/scm-core/src/main/java/sonia/scm/Type.java b/scm-core/src/main/java/sonia/scm/Type.java index b8618b0953..a864fdb0f0 100644 --- a/scm-core/src/main/java/sonia/scm/Type.java +++ b/scm-core/src/main/java/sonia/scm/Type.java @@ -38,11 +38,16 @@ package sonia.scm; import sonia.scm.util.AssertUtil; import sonia.scm.util.Util; +//~--- JDK imports ------------------------------------------------------------ + +import javax.xml.bind.annotation.XmlRootElement; + /** * Base class for all objects which supports different types. * * @author Sebastian Sdorra */ +@XmlRootElement public class Type { diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstactImportHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstactImportHandler.java new file mode 100644 index 0000000000..b9aef60e64 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/AbstactImportHandler.java @@ -0,0 +1,223 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.IOException; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Sebastian Sdorra + * @since 1.12 + */ +public abstract class AbstactImportHandler implements ImportHandler +{ + + /** + * the logger for AbstactImportHandler + */ + private static final Logger logger = + LoggerFactory.getLogger(AbstactImportHandler.class); + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + protected abstract String[] getDirectoryNames(); + + /** + * Method description + * + * + * @return + */ + protected abstract AbstractRepositoryHandler getRepositoryHandler(); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param manager + * + * + * @return + * @throws IOException + * @throws RepositoryException + */ + @Override + public List importRepositories(RepositoryManager manager) + throws IOException, RepositoryException + { + List imported = new ArrayList(); + + if (logger.isTraceEnabled()) + { + logger.trace("search for repositories to import"); + } + + List repositoryNames = + RepositoryUtil.getRepositoryNames(getRepositoryHandler(), + getDirectoryNames()); + + for (String repositoryName : repositoryNames) + { + if (logger.isTraceEnabled()) + { + logger.trace("check repository {} for import", repositoryName); + } + + Repository repository = manager.get(getTypeName(), repositoryName); + + if (repository == null) + { + if (importRepository(manager, repositoryName)) + { + imported.add(repositoryName); + } + } + else if (logger.isDebugEnabled()) + { + logger.debug("repository {} is allready managed", repositoryName); + } + } + + return imported; + } + + /** + * Method description + * + * + * @param repositoryDirectory + * @param repositoryName + * + * @return + * + * @throws IOException + * @throws RepositoryException + */ + protected Repository createRepository(File repositoryDirectory, + String repositoryName) + throws IOException, RepositoryException + { + Repository repository = new Repository(); + + repository.setName(repositoryName); + repository.setPublicReadable(false); + repository.setType(getTypeName()); + + return repository; + } + + /** + * Method description + * + * + * @param manager + * @param repositoryName + * + * + * @return + * @throws IOException + * @throws RepositoryException + */ + private boolean importRepository(RepositoryManager manager, + String repositoryName) + throws IOException, RepositoryException + { + boolean result = false; + Repository repository = + createRepository(getRepositoryDirectory(repositoryName), repositoryName); + + if (repository != null) + { + if (logger.isInfoEnabled()) + { + logger.info("import repository {} of type {}", repositoryName, + getTypeName()); + } + + manager.importRepository(repository); + result = true; + } + else if (logger.isWarnEnabled()) + { + logger.warn("could not create repository object for {}", repositoryName); + } + + return result; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param repositoryName + * + * @return + */ + private File getRepositoryDirectory(String repositoryName) + { + return new File( + getRepositoryHandler().getConfig().getRepositoryDirectory(), + repositoryName); + } + + /** + * Method description + * + * + * @return + */ + private String getTypeName() + { + return getRepositoryHandler().getType().getName(); + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java index fa835e2803..2a459b66d6 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java @@ -39,6 +39,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.ConfigChangedListener; +import sonia.scm.NotSupportedFeatuerException; import sonia.scm.SCMContextProvider; import sonia.scm.store.Store; import sonia.scm.store.StoreFactory; @@ -243,6 +244,21 @@ public abstract class AbstractRepositoryHandler importRepositories(RepositoryManager manager) + throws IOException, RepositoryException; +} diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java index 326d9907a7..5c3c0c13d5 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java @@ -38,6 +38,7 @@ package sonia.scm.repository; import sonia.scm.ConfigChangedListener; import sonia.scm.Handler; import sonia.scm.ListenerSupport; +import sonia.scm.NotSupportedFeatuerException; import sonia.scm.plugin.ExtensionPoint; /** @@ -64,4 +65,17 @@ public interface RepositoryHandler * @return resource path of the {@link Repository} */ public String createResourcePath(Repository repository); + + //~--- get methods ---------------------------------------------------------- + + /** + * Returns the {@link ImportHandler} for the repository type of this handler. + * + * + * @return {@link ImportHandler} for the repository type of this handler + * @since 1.12 + * + * @throws NotSupportedFeatuerException + */ + public ImportHandler getImportHandler() throws NotSupportedFeatuerException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java index 1e90c57d2f..16b908deec 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java @@ -41,6 +41,8 @@ import sonia.scm.TypeManager; //~--- JDK imports ------------------------------------------------------------ +import java.io.IOException; + import java.util.Collection; import javax.servlet.http.HttpServletRequest; @@ -60,6 +62,21 @@ public interface RepositoryManager BlameViewerProvider, DiffViewerProvider { + /** + * Imports an existing {@link Repository}. + * Note: This method should only be called from a {@link RepositoryHandler}. + * + * + * @param repository {@link Repository} to import + * + * @throws IOException + * @throws RepositoryException + */ + public void importRepository(Repository repository) + throws IOException, RepositoryException; + + //~--- get methods ---------------------------------------------------------- + /** * Returns a {@link Repository} by its type and name or * null if the {@link Repository} could not be found. diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java index f4ef899b46..639fc55e25 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryUtil.java @@ -38,6 +38,7 @@ package sonia.scm.repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.io.DirectoryFileFilter; import sonia.scm.util.IOUtil; //~--- JDK imports ------------------------------------------------------------ @@ -45,6 +46,10 @@ import sonia.scm.util.IOUtil; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * * @author Sebastian Sdorra @@ -57,6 +62,27 @@ public class RepositoryUtil private static final Logger logger = LoggerFactory.getLogger(RepositoryUtil.class); + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param directory + * @param names + * + * @return + */ + public static List searchRepositoryDirectories(File directory, + String... names) + { + List repositories = new ArrayList(); + + searchRepositoryDirectories(repositories, directory, Arrays.asList(names)); + + return repositories; + } + //~--- get methods ---------------------------------------------------------- /** @@ -166,4 +192,115 @@ public class RepositoryUtil return name; } + + /** + * Method description + * + * + * @param handler + * @param directoryNames + * + * @return + * + * @throws IOException + */ + public static List getRepositoryNames( + AbstractRepositoryHandler handler, String... directoryNames) + throws IOException + { + return getRepositoryNames(handler.getConfig(), directoryNames); + } + + /** + * Method description + * + * + * @param config + * @param directoryNames + * + * @return + * + * @throws IOException + */ + public static List getRepositoryNames(SimpleRepositoryConfig config, + String... directoryNames) + throws IOException + { + return getRepositoryNames(config.getRepositoryDirectory(), directoryNames); + } + + /** + * Method description + * + * + * @param baseDirectory + * @param directoryNames + * + * @return + * + * @throws IOException + */ + public static List getRepositoryNames(File baseDirectory, + String... directoryNames) + throws IOException + { + List repositories = new ArrayList(); + List repositoryFiles = searchRepositoryDirectories(baseDirectory, + directoryNames); + + for (File file : repositoryFiles) + { + String name = getRepositoryName(baseDirectory, file); + + if (name != null) + { + repositories.add(name); + } + } + + return repositories; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param repositories + * @param directory + * @param names + */ + private static void searchRepositoryDirectories(List repositories, + File directory, List names) + { + boolean found = false; + + for (String name : names) + { + if (new File(directory, name).exists()) + { + found = true; + + break; + } + } + + if (found) + { + repositories.add(directory); + } + else + { + File[] directories = directory.listFiles(DirectoryFileFilter.instance); + + if (directories != null) + { + for (File d : directories) + { + searchRepositoryDirectories(repositories, d, names); + } + } + } + } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitImportHandler.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitImportHandler.java new file mode 100644 index 0000000000..aebdf64441 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitImportHandler.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Sebastian Sdorra + */ +public class GitImportHandler extends AbstactImportHandler +{ + + /** Field description */ + public static final String GIT_DIR = ".git"; + + /** Field description */ + public static final String GIT_DIR_REFS = "refs"; + + /** + * the logger for GitImportHandler + */ + private static final Logger logger = + LoggerFactory.getLogger(GitImportHandler.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param handler + */ + public GitImportHandler(GitRepositoryHandler handler) + { + this.handler = handler; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + protected String[] getDirectoryNames() + { + return new String[] { GIT_DIR, GIT_DIR_REFS }; + } + + /** + * Method description + * + * + * @return + */ + @Override + protected AbstractRepositoryHandler getRepositoryHandler() + { + return handler; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private GitRepositoryHandler handler; +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java index e0ff384b91..314309ea73 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java @@ -41,6 +41,7 @@ import com.google.inject.Singleton; import org.eclipse.jgit.storage.file.FileRepository; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import sonia.scm.NotSupportedFeatuerException; import sonia.scm.Type; import sonia.scm.io.FileSystem; import sonia.scm.plugin.ext.Extension; @@ -184,6 +185,18 @@ public class GitRepositoryHandler return diffViewer; } + /** + * Method description + * + * + * @return + */ + @Override + public ImportHandler getImportHandler() + { + return new GitImportHandler(this); + } + /** * Method description * diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java new file mode 100644 index 0000000000..6960ab43e5 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.io.INIConfiguration; +import sonia.scm.io.INIConfigurationReader; +import sonia.scm.io.INIConfigurationWriter; +import sonia.scm.io.INISection; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.File; +import java.io.IOException; + +/** + * + * @author Sebastian Sdorra + */ +public class HgImportHandler extends AbstactImportHandler +{ + + /** Field description */ + public static final String HG_DIR = ".hg"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param handler + */ + public HgImportHandler(HgRepositoryHandler handler) + { + this.handler = handler; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param repositoryDirectory + * @param repositoryName + * + * @return + * + * @throws IOException + * @throws RepositoryException + */ + @Override + protected Repository createRepository(File repositoryDirectory, + String repositoryName) + throws IOException, RepositoryException + { + Repository repository = super.createRepository(repositoryDirectory, + repositoryName); + File hgrc = new File(repositoryDirectory, HgRepositoryHandler.PATH_HGRC); + + if (hgrc.exists()) + { + INIConfigurationReader reader = new INIConfigurationReader(); + INIConfiguration c = reader.read(hgrc); + INISection web = c.getSection("web"); + + if (web == null) + { + handler.appendWebSection(c); + } + else + { + repository.setDescription(web.getParameter("description")); + repository.setContact(web.getParameter("contact")); + handler.setWebParameter(web); + } + + INIConfigurationWriter writer = new INIConfigurationWriter(); + + writer.write(c, hgrc); + } + else + { + handler.postCreate(repository, repositoryDirectory); + } + + return repository; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + protected String[] getDirectoryNames() + { + return new String[] { HG_DIR }; + } + + /** + * Method description + * + * + * @return + */ + @Override + protected AbstractRepositoryHandler getRepositoryHandler() + { + return handler; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private HgRepositoryHandler handler; +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index 3d14f27c0f..9bbdc4f044 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -293,6 +293,18 @@ public class HgRepositoryHandler return diffViewer; } + /** + * Method description + * + * + * @return + */ + @Override + public ImportHandler getImportHandler() + { + return new HgImportHandler(this); + } + /** * Method description * @@ -320,6 +332,72 @@ public class HgRepositoryHandler return TYPE; } + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param hgrc + */ + void appendHookSection(INIConfiguration hgrc) + { + INISection hooksSection = new INISection("hooks"); + + setHookParameter(hooksSection); + hgrc.addSection(hooksSection); + } + + /** + * Method description + * + * + * @param hgrc + */ + void appendWebSection(INIConfiguration hgrc) + { + INISection webSection = new INISection("web"); + + setWebParameter(webSection); + hgrc.addSection(webSection); + } + + /** + * Method description + * + * + * @param c + * @param repositoryName + * + * @return + */ + boolean registerMissingHook(INIConfiguration c, String repositoryName) + { + INISection hooks = c.getSection("hooks"); + + if (hooks == null) + { + hooks = new INISection("hooks"); + c.addSection(hooks); + } + + boolean write = false; + + if (appendHook(repositoryName, hooks, "changegroup.scm")) + { + write = true; + } + + if (appendHook(repositoryName, hooks, "pretxnchangegroup.scm")) + { + write = true; + } + + return write; + } + + //~--- get methods ---------------------------------------------------------- + /** * Method description * @@ -341,6 +419,34 @@ public class HgRepositoryHandler hgContextProvider.get(), repositoryDirectory); } + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param hooksSection + */ + void setHookParameter(INISection hooksSection) + { + hooksSection.setParameter("changegroup.scm", "python:scmhooks.callback"); + hooksSection.setParameter("pretxnchangegroup.scm", + "python:scmhooks.callback"); + } + + /** + * Method description + * + * + * @param webSection + */ + void setWebParameter(INISection webSection) + { + webSection.setParameter("push_ssl", "false"); + webSection.setParameter("allow_read", "*"); + webSection.setParameter("allow_push", "*"); + } + //~--- methods -------------------------------------------------------------- /** @@ -376,20 +482,11 @@ public class HgRepositoryHandler { File hgrcFile = new File(directory, PATH_HGRC); INIConfiguration hgrc = new INIConfiguration(); - INISection webSection = new INISection("web"); - webSection.setParameter("push_ssl", "false"); - webSection.setParameter("allow_read", "*"); - webSection.setParameter("allow_push", "*"); - hgrc.addSection(webSection); + appendWebSection(hgrc); // register hooks - INISection hooksSection = new INISection("hooks"); - - hooksSection.setParameter("changegroup.scm", "python:scmhooks.callback"); - hooksSection.setParameter("pretxnchangegroup.scm", - "python:scmhooks.callback"); - hgrc.addSection(hooksSection); + appendHookSection(hgrc); INIConfigurationWriter writer = new INIConfigurationWriter(); @@ -483,28 +580,9 @@ public class HgRepositoryHandler { INIConfigurationReader reader = new INIConfigurationReader(); INIConfiguration c = reader.read(hgrc); - INISection hooks = c.getSection("hooks"); - - if (hooks == null) - { - hooks = new INISection("hooks"); - c.addSection(hooks); - } - String repositoryName = repositoryDir.getName(); - boolean write = false; - if (appendHook(repositoryName, hooks, "changegroup.scm")) - { - write = true; - } - - if (appendHook(repositoryName, hooks, "pretxnchangegroup.scm")) - { - write = true; - } - - if (write) + if (registerMissingHook(c, repositoryName)) { if (logger.isDebugEnabled()) { diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnImportHandler.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnImportHandler.java new file mode 100644 index 0000000000..a476180d1b --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnImportHandler.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.repository; + +/** + * + * @author Sebastian Sdorra + */ +public class SvnImportHandler extends AbstactImportHandler +{ + + /** Field description */ + public static final String SVN_DIR_LOCKS = "locks"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param handler + */ + public SvnImportHandler(SvnRepositoryHandler handler) + { + this.handler = handler; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + protected String[] getDirectoryNames() + { + return new String[] { SVN_DIR_LOCKS }; + } + + /** + * Method description + * + * + * @return + */ + @Override + protected AbstractRepositoryHandler getRepositoryHandler() + { + return handler; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private SvnRepositoryHandler handler; +} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java index 536586f95a..7b0dceaa7e 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java @@ -46,6 +46,7 @@ import org.tmatesoft.svn.core.internal.io.fs.FSHooks; import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory; import org.tmatesoft.svn.core.io.SVNRepositoryFactory; +import sonia.scm.NotSupportedFeatuerException; import sonia.scm.Type; import sonia.scm.io.FileSystem; import sonia.scm.plugin.ext.Extension; @@ -206,6 +207,18 @@ public class SvnRepositoryHandler return diffViewer; } + /** + * Method description + * + * + * @return + */ + @Override + public ImportHandler getImportHandler() + { + return new SvnImportHandler(this); + } + /** * Method description * diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java new file mode 100644 index 0000000000..bc787f84cc --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java @@ -0,0 +1,225 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. 2. Redistributions in + * binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. 3. Neither the name of SCM-Manager; + * nor the names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.api.rest.resources; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import org.codehaus.enunciate.jaxrs.TypeHint; +import org.codehaus.enunciate.modules.jersey.SpringManagedLifecycle; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.NotSupportedFeatuerException; +import sonia.scm.Type; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryHandler; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.util.SecurityUtil; +import sonia.scm.web.security.WebSecurityContext; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; + +/** + * + * @author Sebastian Sdorra + */ +@Singleton +@Path("import/repositories") +@SpringManagedLifecycle +public class RepositoryImportResource +{ + + /** + * the logger for RepositoryImportResource + */ + private static final Logger logger = + LoggerFactory.getLogger(RepositoryImportResource.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param manager + * @param securityContextProvider + */ + @Inject + public RepositoryImportResource( + RepositoryManager manager, + Provider securityContextProvider) + { + this.manager = manager; + this.securityContextProvider = securityContextProvider; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param type + * + * @return + */ + @POST + @Path("{type}") + @TypeHint(Repository[].class) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public GenericEntity> importRepositories( + @PathParam("type") String type) + { + SecurityUtil.assertIsAdmin(securityContextProvider); + + List repositories = new ArrayList(); + RepositoryHandler handler = manager.getHandler(type); + + if (handler != null) + { + try + { + List repositoryNames = + handler.getImportHandler().importRepositories(manager); + + if (repositoryNames != null) + { + for (String repositoryName : repositoryNames) + { + Repository repository = manager.get(type, repositoryName); + + if (repository != null) + { + repositories.add(repository); + } + else if (logger.isWarnEnabled()) + { + logger.warn("could not find imported repository {}", + repositoryName); + } + } + } + } + catch (Exception ex) + { + throw new WebApplicationException(ex); + } + } + else if (logger.isWarnEnabled()) + { + logger.warn("could not find handler for type {}", type); + } + + return new GenericEntity>(repositories) {} + ; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @GET + @TypeHint(Type[].class) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public GenericEntity> getImportableTypes() + { + SecurityUtil.assertIsAdmin(securityContextProvider); + + List types = new ArrayList(); + Collection handlerTypes = manager.getTypes(); + + for (Type t : handlerTypes) + { + RepositoryHandler handler = manager.getHandler(t.getName()); + + if (handler != null) + { + try + { + if (handler.getImportHandler() != null) + { + types.add(t); + } + } + catch (NotSupportedFeatuerException ex) + { + if (logger.isTraceEnabled()) + { + logger.trace("import handler is not supported", ex); + } + else if (logger.isInfoEnabled()) + { + logger.info("{} handler does not support import of repositories", + t.getName()); + } + } + } + else if (logger.isWarnEnabled()) + { + logger.warn("could not find handler for type {}", t.getName()); + } + } + + return new GenericEntity>(types) {} + ; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private RepositoryManager manager; + + /** Field description */ + private Provider securityContextProvider; +} diff --git a/scm-webapp/src/main/java/sonia/scm/repository/xml/XmlRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/xml/XmlRepositoryManager.java index 1134ee7e48..79a42a53a4 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/xml/XmlRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/xml/XmlRepositoryManager.java @@ -163,12 +163,12 @@ public class XmlRepositoryManager extends AbstractRepositoryManager * * * @param repository + * @param createRepository * * @throws IOException * @throws RepositoryException */ - @Override - public void create(Repository repository) + public void create(Repository repository, boolean createRepository) throws RepositoryException, IOException { if (logger.isInfoEnabled()) @@ -187,7 +187,11 @@ public class XmlRepositoryManager extends AbstractRepositoryManager repository.setId(UUID.randomUUID().toString()); repository.setCreationDate(System.currentTimeMillis()); - getHandler(repository).create(repository); + + if (createRepository) + { + getHandler(repository).create(repository); + } synchronized (XmlRepositoryDatabase.class) { @@ -198,6 +202,22 @@ public class XmlRepositoryManager extends AbstractRepositoryManager fireEvent(repository, HandlerEvent.CREATE); } + /** + * Method description + * + * + * @param repository + * + * @throws IOException + * @throws RepositoryException + */ + @Override + public void create(Repository repository) + throws RepositoryException, IOException + { + create(repository, true); + } + /** * Method description * @@ -285,6 +305,22 @@ public class XmlRepositoryManager extends AbstractRepositoryManager fireHookEvent(repository, event); } + /** + * Method description + * + * + * @param repository + * + * @throws IOException + * @throws RepositoryException + */ + @Override + public void importRepository(Repository repository) + throws RepositoryException, IOException + { + create(repository, false); + } + /** * Method description * diff --git a/scm-webapp/src/main/webapp/index.html b/scm-webapp/src/main/webapp/index.html index ea2425317d..d435d31368 100644 --- a/scm-webapp/src/main/webapp/index.html +++ b/scm-webapp/src/main/webapp/index.html @@ -113,6 +113,7 @@ + diff --git a/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js new file mode 100644 index 0000000000..825b900da9 --- /dev/null +++ b/scm-webapp/src/main/webapp/resources/js/repository/sonia.repository.importwindow.js @@ -0,0 +1,227 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + +Sonia.repository.ImportWindow = Ext.extend(Ext.Window,{ + + // TODO i18n + titleText: 'Import Repositories', + okText: 'Ok', + closeText: 'Close', + + // cache + importForm: null, + + imported: [], + importJobsFinished: 0, + importJobs: 0, + + initComponent: function(){ + var config = { + layout:'fit', + width:300, + height:170, + closable: true, + resizable: false, + plain: true, + border: false, + modal: true, + title: this.titleText, + items: [{ + id: 'importRepositoryForm', + frame: true, + xtype: 'form', + defaultType: 'checkbox' + }], + buttons: [{ + id: 'startRepositoryImportButton', + text: this.okText, + formBind: true, + scope: this, + handler: this.importRepositories + },{ + text: this.closeText, + scope: this, + handler: this.close + }], + listeners: { + afterrender: { + fn: this.readImportableTypes, + scope: this + } + } + } + Ext.apply(this, Ext.apply(this.initialConfig, config)); + Sonia.repository.ImportWindow.superclass.initComponent.apply(this, arguments); + }, + + readImportableTypes: function(){ + if (debug){ + console.debug('read importable types'); + } + + Ext.Ajax.request({ + url: restUrl + 'import/repositories.json', + method: 'GET', + scope: this, + success: function(response){ + var obj = Ext.decode(response.responseText); + this.renderTypeCheckboxes(obj); + this.doLayout(); + }, + failure: function(result){ + main.handleFailure( + result.status, + this.errorTitleText, + this.errorMsgText + ); + } + }); + + }, + + renderTypeCheckboxes: function(types){ + Ext.each(types, function(type){ + this.renderCheckbox(type); + }, this); + }, + + getImportForm: function(){ + if (!this.importForm){ + this.importForm = Ext.getCmp('importRepositoryForm'); + } + return this.importForm; + }, + + renderCheckbox: function(type){ + this.getImportForm().add({ + xtype: 'checkbox', + name: 'type', + fieldLabel: type.displayName, + inputValue: type.name + }); + }, + + importRepositories: function(){ + if (debug){ + console.debug('start import of repositories'); + } + var form = this.getImportForm().getForm(); + var values = form.getValues().type; + if ( values ){ + if ( Ext.isArray(values) ){ + this.importJobs = values.length; + } else { + this.importJobs = 1; + } + } else { + this.importJobs = 0; + } + Ext.each(values, function(value){ + this.importRepositoriesOfType(value); + }, this); + }, + + appendImported: function(repositories){ + for (var i=0; i= this.importJobs ){ + if (debug){ + console.debug( 'import of ' + this.importJobsFinished + ' jobs finished' ); + } + this.printImported(); + } + }, + + printImported: function(){ + var store = new Ext.data.JsonStore({ + fields: ['type', 'name'] + }); + store.loadData(this.imported); + + var colModel = new Ext.grid.ColumnModel({ + defaults: { + sortable: true, + scope: this + }, + columns: [ + {id: 'name', header: 'Name', dataIndex: 'name'}, + {id: 'type', header: 'Type', dataIndex: 'type'} + ] + }); + + this.getImportForm().add({ + xtype: 'grid', + autoExpandColumn: 'name', + store: store, + colModel: colModel, + height: 100 + }); + var h = this.getHeight(); + this.setHeight( h + 100 ); + this.doLayout(); + + // reload repositories panel + var panel = Ext.getCmp('repositories'); + if (panel){ + panel.getGrid().reload(); + } + }, + + importRepositoriesOfType: function(type){ + if (debug){ + console.debug('start import of ' + type + ' repositories'); + } + var b = Ext.getCmp('startRepositoryImportButton'); + if ( b ){ + b.setDisabled(true); + } + Ext.Ajax.request({ + url: restUrl + 'import/repositories/' + type + '.json', + method: 'POST', + scope: this, + success: function(response){ + var obj = Ext.decode(response.responseText); + this.appendImported(obj); + }, + failure: function(result){ + main.handleFailure( + result.status, + this.errorTitleText, + this.errorMsgText + ); + } + }); + } + +}); \ No newline at end of file diff --git a/scm-webapp/src/main/webapp/resources/js/sonia.scm.js b/scm-webapp/src/main/webapp/resources/js/sonia.scm.js index 03df5d8ca9..933619ad4d 100644 --- a/scm-webapp/src/main/webapp/resources/js/sonia.scm.js +++ b/scm-webapp/src/main/webapp/resources/js/sonia.scm.js @@ -33,6 +33,8 @@ Ext.ns("Sonia.scm"); Sonia.scm.Main = Ext.extend(Ext.util.Observable, { tabRepositoriesText: 'Repositories', + // todo i18n + navImportRepositoriesText: 'Import Repositories', navChangePasswordText: 'Change Password', sectionMainText: 'Main', sectionSecurityText: 'Security', @@ -156,14 +158,26 @@ Sonia.scm.Main = Ext.extend(Ext.util.Observable, { console.debug('create main menu'); } var panel = Ext.getCmp('navigationPanel'); + + var repositoryLinks = [{ + label: this.navRepositoriesText, + fn: this.addRepositoriesTabPanel, + scope: this + }]; + + if ( admin ){ + repositoryLinks.push({ + label: this.navImportRepositoriesText, + fn: function(){ + new Sonia.repository.ImportWindow().show(); + } + }); + } + panel.addSection({ id: 'navMain', title: this.sectionMainText, - links: [{ - label: this.navRepositoriesText, - fn: this.addRepositoriesTabPanel, - scope: this - }] + links: repositoryLinks }); var securitySection = null;