mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-05-07 18:16:46 +02:00
merge with branch 1.x
This commit is contained in:
7
pom.xml
7
pom.xml
@@ -421,7 +421,7 @@
|
||||
|
||||
<properties>
|
||||
<!-- test libraries -->
|
||||
<mokito.version>1.9.5</mokito.version>
|
||||
<mokito.version>1.10.8</mokito.version>
|
||||
<hamcrest.version>1.3</hamcrest.version>
|
||||
<junit.version>4.11</junit.version>
|
||||
|
||||
@@ -430,8 +430,7 @@
|
||||
<logback.version>1.1.2</logback.version>
|
||||
<servlet.version>2.5</servlet.version>
|
||||
<guice.version>3.0</guice.version>
|
||||
<jersey.version>1.18.1</jersey.version>
|
||||
<freemarker.version>2.3.20</freemarker.version>
|
||||
<jersey.version>1.18.2</jersey.version>
|
||||
|
||||
<!-- event bus -->
|
||||
<legman.version>1.2.0</legman.version>
|
||||
@@ -445,7 +444,7 @@
|
||||
<!-- repostitory libraries -->
|
||||
<jgit.version>3.4.1.201406201815-r</jgit.version>
|
||||
<svnkit.version>1.8.5-scm2</svnkit.version>
|
||||
|
||||
|
||||
<!-- util libraries -->
|
||||
<guava.version>16.0.1</guava.version>
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<dependency>
|
||||
<groupId>args4j</groupId>
|
||||
<artifactId>args4j</artifactId>
|
||||
<version>2.0.28</version>
|
||||
<version>2.0.29</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -52,7 +52,7 @@
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
<version>${freemarker.version}</version>
|
||||
<version>2.3.21</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.cli.cmd;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import sonia.scm.ConfigurationException;
|
||||
import sonia.scm.client.ImportBundleRequest;
|
||||
import sonia.scm.client.ScmClientSession;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
@Command(
|
||||
name = "import-from-bundle",
|
||||
usage = "usageImportBundle",
|
||||
group = "repository"
|
||||
)
|
||||
public class ImportBundleSubCommand extends ImportSubCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public File getBundle()
|
||||
{
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isCompressed()
|
||||
{
|
||||
return compressed;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param bundle
|
||||
*/
|
||||
public void setBundle(File bundle)
|
||||
{
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param compressed
|
||||
*/
|
||||
public void setCompressed(boolean compressed)
|
||||
{
|
||||
this.compressed = compressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected void run()
|
||||
{
|
||||
if (!bundle.exists())
|
||||
{
|
||||
throw new ConfigurationException("could not find bundle");
|
||||
}
|
||||
else
|
||||
{
|
||||
ScmClientSession session = createSession();
|
||||
|
||||
ImportBundleRequest req = new ImportBundleRequest(getType(), name,
|
||||
Files.asByteSource(bundle));
|
||||
|
||||
req.setCompressed(compressed);
|
||||
|
||||
Repository repository =
|
||||
session.getRepositoryHandler().importFromBundle(req);
|
||||
|
||||
printImportedRepository(repository);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@Option(
|
||||
name = "--bundle",
|
||||
required = true,
|
||||
usage = "optionRepositoryBundle",
|
||||
aliases = { "-b" }
|
||||
)
|
||||
private File bundle;
|
||||
|
||||
/** Field description */
|
||||
@Option(
|
||||
name = "--compressed",
|
||||
usage = "optionRepositoryBundleCompressed",
|
||||
aliases = { "-c" }
|
||||
)
|
||||
private boolean compressed = false;
|
||||
|
||||
/** Field description */
|
||||
@Option(
|
||||
name = "--name",
|
||||
required = true,
|
||||
usage = "optionRepositoryName",
|
||||
aliases = { "-n" }
|
||||
)
|
||||
private String name;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.cli.cmd;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import sonia.scm.client.ImportResultWrapper;
|
||||
import sonia.scm.client.ScmClientSession;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
@Command(
|
||||
name = "import-from-directory",
|
||||
usage = "usageImportDirectory",
|
||||
group = "repository"
|
||||
)
|
||||
public class ImportDirectorySubCommand extends ImportSubCommand
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String TEMPLATE =
|
||||
"/sonia/resources/import-from-directory.ftl";
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected void run()
|
||||
{
|
||||
ScmClientSession session = createSession();
|
||||
ImportResultWrapper wrapper =
|
||||
session.getRepositoryHandler().importFromDirectory(getType());
|
||||
Map<String, Object> env = Maps.newHashMap();
|
||||
|
||||
env.put("importedDirectories", wrapper.getImportedDirectories());
|
||||
env.put("failedDirectories", wrapper.getFailedDirectories());
|
||||
renderTemplate(env, TEMPLATE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.cli.cmd;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.kohsuke.args4j.Argument;
|
||||
|
||||
import sonia.scm.cli.wrapper.RepositoryWrapper;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
public abstract class ImportSubCommand extends TemplateSubCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
*/
|
||||
public void setType(String type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
*/
|
||||
protected void printImportedRepository(Repository repository)
|
||||
{
|
||||
Map<String, Object> env = Maps.newHashMap();
|
||||
|
||||
env.put("repository", new RepositoryWrapper(config, repository));
|
||||
renderTemplate(env, GetRepositorySubCommand.TEMPLATE);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@Argument(
|
||||
usage = "optionRepositoryType",
|
||||
metaVar = "repositorytype",
|
||||
required = true
|
||||
)
|
||||
private String type;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.cli.cmd;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import sonia.scm.client.ImportUrlRequest;
|
||||
import sonia.scm.client.ScmClientSession;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Command(
|
||||
name = "import-from-url",
|
||||
usage = "usageImportUrl",
|
||||
group = "repository"
|
||||
)
|
||||
public class ImportUrlSubCommand extends ImportSubCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public URL getUrl()
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
public void setUrl(URL url)
|
||||
{
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected void run()
|
||||
{
|
||||
ScmClientSession session = createSession();
|
||||
|
||||
ImportUrlRequest request = new ImportUrlRequest(getType(), name,
|
||||
url.toExternalForm());
|
||||
Repository repository =
|
||||
session.getRepositoryHandler().importFromUrl(request);
|
||||
|
||||
printImportedRepository(repository);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@Option(
|
||||
name = "--name",
|
||||
required = true,
|
||||
usage = "optionRepositoryName",
|
||||
aliases = { "-n" }
|
||||
)
|
||||
private String name;
|
||||
|
||||
/** Field description */
|
||||
@Option(
|
||||
name = "--url",
|
||||
required = true,
|
||||
usage = "optionRemoteRepositoryUrl",
|
||||
aliases = { "-r" }
|
||||
)
|
||||
private URL url;
|
||||
}
|
||||
@@ -37,6 +37,9 @@ sonia.scm.cli.cmd.ModifyRepositorySubCommand
|
||||
sonia.scm.cli.cmd.GetRepositorySubCommand
|
||||
sonia.scm.cli.cmd.ListRepositoriesSubCommand
|
||||
sonia.scm.cli.cmd.DeleteRepositorySubCommand
|
||||
sonia.scm.cli.cmd.ImportDirectorySubCommand
|
||||
sonia.scm.cli.cmd.ImportUrlSubCommand
|
||||
sonia.scm.cli.cmd.ImportBundleSubCommand
|
||||
|
||||
# permission
|
||||
sonia.scm.cli.cmd.AddPermissionSubCommand
|
||||
|
||||
@@ -46,11 +46,14 @@ optionTemplateFile = Template file
|
||||
optionRepositoryId = Repository Id
|
||||
optionRepositoryIdOrTypeAndName = Repository Id or type/name
|
||||
optionRepositoryName = Repository name
|
||||
optionRepositoryType = Repository name
|
||||
optionRepositoryType = Repository type
|
||||
optionRepositoryContact = Repository contact
|
||||
optionRepositoryDescription = Repository description
|
||||
optionRepositoryPublic = Repository public readable
|
||||
optionRepositoryArchive = Repository archived
|
||||
optionRemoteRepositoryUrl = Remote repository url
|
||||
optionRepositoryBundle = Import repository from a bundle file (e.g. svn dump)
|
||||
optionRepositoryBundleCompressed = Indicates that the bundle is gzip compressed
|
||||
|
||||
optionPermissionGroup = Group
|
||||
optionPermissionName = Group or user name
|
||||
@@ -81,6 +84,7 @@ permissiontype = value
|
||||
groupname = groupname
|
||||
repositoryid = repositoryid
|
||||
username = username
|
||||
repositorytype = type
|
||||
|
||||
config = Configuration
|
||||
misc = Miscellaneous
|
||||
@@ -90,6 +94,8 @@ user = User
|
||||
security = Security
|
||||
level = Logging-Level
|
||||
boolean = true or false
|
||||
URL = url
|
||||
bundle = file
|
||||
|
||||
options = Options
|
||||
usage = scm-cli-client [options] command [command options]
|
||||
@@ -118,6 +124,8 @@ usageModifyRepository = Modify a repository
|
||||
usageStoreConfig = Stores the current configuration
|
||||
usageVersion = Show the version of scm-cli-client
|
||||
usageServerVersion = Show the version of the scm-manager
|
||||
usageImportDirectory = Import repositories from repository directory
|
||||
usageImportUrl = Import repository from remote url
|
||||
|
||||
usageEncrypt = Encrypts the given value
|
||||
usageGenerateKey = Generates a unique key
|
||||
@@ -0,0 +1,9 @@
|
||||
Imported repositories:
|
||||
<#list importedDirectories as imported>
|
||||
- ${imported}
|
||||
</#list>
|
||||
|
||||
Repositories failed to import:
|
||||
<#list failedDirectories as failed>
|
||||
- ${failed}
|
||||
</#list>
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.client;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
public class ImportBundleRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
ImportBundleRequest() {}
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param name
|
||||
* @param bundle
|
||||
*/
|
||||
public ImportBundleRequest(String type, String name, ByteSource bundle)
|
||||
{
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ByteSource getBundle()
|
||||
{
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isCompressed()
|
||||
{
|
||||
return compressed;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param compressed
|
||||
*/
|
||||
public void setCompressed(boolean compressed)
|
||||
{
|
||||
this.compressed = compressed;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private ByteSource bundle;
|
||||
|
||||
/** Field description */
|
||||
private boolean compressed = false;
|
||||
|
||||
/** Field description */
|
||||
private String name;
|
||||
|
||||
/** Field description */
|
||||
private String type;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.client;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import sonia.scm.repository.ImportResult;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
public class ImportResultWrapper
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param client
|
||||
* @param type
|
||||
* @param result
|
||||
*/
|
||||
public ImportResultWrapper(RepositoryClientHandler client, String type,
|
||||
ImportResult result)
|
||||
{
|
||||
this.client = client;
|
||||
this.type = type;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getFailedDirectories()
|
||||
{
|
||||
List<String> directories = result.getFailedDirectories();
|
||||
|
||||
if (directories == null)
|
||||
{
|
||||
directories = ImmutableList.of();
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getImportedDirectories()
|
||||
{
|
||||
List<String> directories = result.getImportedDirectories();
|
||||
|
||||
if (directories == null)
|
||||
{
|
||||
directories = ImmutableList.of();
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Iterable<Repository> getImportedRepositories()
|
||||
{
|
||||
return Iterables.transform(getImportedDirectories(),
|
||||
new RepositoryResolver(client, type));
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 14/11/29
|
||||
* @author Enter your name here...
|
||||
*/
|
||||
private static class RepositoryResolver
|
||||
implements Function<String, Repository>
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param clientHandler
|
||||
* @param type
|
||||
*/
|
||||
public RepositoryResolver(RepositoryClientHandler clientHandler,
|
||||
String type)
|
||||
{
|
||||
this.clientHandler = clientHandler;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Repository apply(String name)
|
||||
{
|
||||
return clientHandler.get(type, type);
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final RepositoryClientHandler clientHandler;
|
||||
|
||||
/** Field description */
|
||||
private final String type;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final RepositoryClientHandler client;
|
||||
|
||||
/** Field description */
|
||||
private final ImportResult result;
|
||||
|
||||
/** Field description */
|
||||
private final String type;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.client;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
@XmlRootElement(name = "import")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class ImportUrlRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
ImportUrlRequest() {}
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param name
|
||||
* @param url
|
||||
*/
|
||||
public ImportUrlRequest(String type, String name, String url)
|
||||
{
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getUrl()
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private String name;
|
||||
|
||||
/** Field description */
|
||||
@XmlTransient
|
||||
private String type;
|
||||
|
||||
/** Field description */
|
||||
private String url;
|
||||
}
|
||||
@@ -51,6 +51,43 @@ import java.util.Collection;
|
||||
public interface RepositoryClientHandler extends ClientHandler<Repository>
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public ImportResultWrapper importFromDirectory(String type);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public Repository importFromBundle(ImportBundleRequest request);
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public Repository importFromUrl(ImportUrlRequest request);
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -45,6 +45,12 @@
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey.contribs</groupId>
|
||||
<artifactId>jersey-multipart</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test scope -->
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -51,8 +51,11 @@ import com.sun.jersey.api.client.ClientHandlerException;
|
||||
import com.sun.jersey.api.client.ClientRequest;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.api.client.config.ClientConfig;
|
||||
import com.sun.jersey.api.client.config.DefaultClientConfig;
|
||||
import com.sun.jersey.api.client.filter.ClientFilter;
|
||||
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
||||
import com.sun.jersey.multipart.impl.MultiPartWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -122,7 +125,8 @@ public class JerseyClientProvider implements ScmClientProvider
|
||||
UrlProvider urlProvider = UrlProviderFactory.createUrlProvider(url,
|
||||
UrlProviderFactory.TYPE_RESTAPI_XML);
|
||||
|
||||
Client client = Client.create();
|
||||
Client client =
|
||||
Client.create(new DefaultClientConfig(MultiPartWriter.class));
|
||||
|
||||
client.addFilter(new CookieClientFilter());
|
||||
|
||||
|
||||
@@ -35,20 +35,32 @@ package sonia.scm.client;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import sonia.scm.NotSupportedFeatuerException;
|
||||
import sonia.scm.Type;
|
||||
import sonia.scm.repository.ImportResult;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.Tags;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.GenericType;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.multipart.FormDataMultiPart;
|
||||
import com.sun.jersey.multipart.file.StreamDataBodyPart;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -57,6 +69,29 @@ public class JerseyRepositoryClientHandler
|
||||
extends AbstractClientHandler<Repository> implements RepositoryClientHandler
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String IMPORT_TYPE_BUNDLE = "bundle";
|
||||
|
||||
/** Field description */
|
||||
private static final String IMPORT_TYPE_DIRECTORY = "directory";
|
||||
|
||||
/** Field description */
|
||||
private static final String IMPORT_TYPE_URL = "url";
|
||||
|
||||
/** Field description */
|
||||
private static final String PARAM_BUNDLE = "bundle";
|
||||
|
||||
/** Field description */
|
||||
private static final String PARAM_COMPRESSED = "compressed";
|
||||
|
||||
/** Field description */
|
||||
private static final String PARAM_NAME = "name";
|
||||
|
||||
/** Field description */
|
||||
private static final String URL_IMPORT = "import/repositories/";
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
@@ -68,6 +103,108 @@ public class JerseyRepositoryClientHandler
|
||||
super(session, Repository.class);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Repository importFromBundle(ImportBundleRequest request)
|
||||
{
|
||||
WebResource r = client.resource(getImportUrl(request.getType(),
|
||||
IMPORT_TYPE_BUNDLE)).queryParam(PARAM_COMPRESSED,
|
||||
Boolean.toString(request.isCompressed()));
|
||||
Repository repository = null;
|
||||
InputStream stream = null;
|
||||
|
||||
try
|
||||
{
|
||||
stream = request.getBundle().openStream();
|
||||
|
||||
FormDataMultiPart form = new FormDataMultiPart();
|
||||
|
||||
form.field(PARAM_NAME, request.getName());
|
||||
form.bodyPart(new StreamDataBodyPart(PARAM_BUNDLE, stream));
|
||||
|
||||
ClientResponse response =
|
||||
r.type(MediaType.MULTIPART_FORM_DATA).post(ClientResponse.class, form);
|
||||
|
||||
ClientUtil.checkResponse(response);
|
||||
|
||||
String location =
|
||||
response.getHeaders().getFirst(HttpUtil.HEADER_LOCATION);
|
||||
|
||||
if (Strings.isNullOrEmpty(location))
|
||||
{
|
||||
throw new ScmClientException("no location header found after import");
|
||||
}
|
||||
|
||||
repository = getItemByUrl(location);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new ScmClientException("could not import bundle", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(stream);
|
||||
}
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ImportResultWrapper importFromDirectory(String type)
|
||||
{
|
||||
WebResource r = client.resource(getImportUrl(type, IMPORT_TYPE_DIRECTORY));
|
||||
ClientResponse response = r.post(ClientResponse.class);
|
||||
|
||||
ClientUtil.checkResponse(response);
|
||||
|
||||
return new ImportResultWrapper(this, type,
|
||||
response.getEntity(ImportResult.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Repository importFromUrl(ImportUrlRequest request)
|
||||
{
|
||||
WebResource r = client.resource(getImportUrl(request.getType(),
|
||||
IMPORT_TYPE_URL));
|
||||
ClientResponse response = r.post(ClientResponse.class, request);
|
||||
|
||||
ClientUtil.checkResponse(response);
|
||||
|
||||
String location = response.getHeaders().getFirst(HttpUtil.HEADER_LOCATION);
|
||||
|
||||
if (Strings.isNullOrEmpty(location))
|
||||
{
|
||||
throw new ScmClientException("no location header found after import");
|
||||
}
|
||||
|
||||
return getItemByUrl(location);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -228,4 +365,22 @@ public class JerseyRepositoryClientHandler
|
||||
{
|
||||
return urlProvider.getRepositoryUrlProvider().getAllUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param importType
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private String getImportUrl(String type, String importType)
|
||||
{
|
||||
StringBuilder buffer = new StringBuilder(URL_IMPORT);
|
||||
|
||||
buffer.append(type).append(HttpUtil.SEPARATOR_PATH).append(importType);
|
||||
|
||||
return HttpUtil.append(urlProvider.getBaseUrl(), buffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,23 +35,28 @@ package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.ImportResult.Builder;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract base class for directory based {@link ImportHandler} and
|
||||
* {@link AdvancedImportHandler}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.12
|
||||
*/
|
||||
public abstract class AbstactImportHandler implements ImportHandler
|
||||
public abstract class AbstactImportHandler implements AdvancedImportHandler
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -63,80 +68,65 @@ public abstract class AbstactImportHandler implements ImportHandler
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Returns array of repository directory names.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* @return repository directory names
|
||||
*/
|
||||
protected abstract String[] getDirectoryNames();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Returns repository handler.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* @return repository handler
|
||||
*/
|
||||
protected abstract AbstractRepositoryHandler<?> getRepositoryHandler();
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param manager
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<String> importRepositories(RepositoryManager manager)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
List<String> imported = new ArrayList<String>();
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("search for repositories to import");
|
||||
}
|
||||
|
||||
List<String> 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)
|
||||
{
|
||||
importRepository(manager, repositoryName);
|
||||
imported.add(repositoryName);
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("repository {} is allready managed", repositoryName);
|
||||
}
|
||||
}
|
||||
|
||||
return imported;
|
||||
return doRepositoryImport(manager, true).getImportedDirectories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ImportResult importRepositoriesFromDirectory(RepositoryManager manager)
|
||||
{
|
||||
try
|
||||
{
|
||||
return doRepositoryImport(manager, false);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
|
||||
// should never happen
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
catch (RepositoryException ex)
|
||||
{
|
||||
|
||||
// should never happen
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a repository.
|
||||
*
|
||||
*
|
||||
* @param repositoryDirectory
|
||||
* @param repositoryName
|
||||
* @param repositoryDirectory repository base directory
|
||||
* @param repositoryName name of the repository
|
||||
*
|
||||
* @return
|
||||
* @return repository
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
@@ -154,6 +144,118 @@ public abstract class AbstactImportHandler implements ImportHandler
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository import.
|
||||
*
|
||||
*
|
||||
* @param manager repository manager
|
||||
* @param throwExceptions true to throw exception
|
||||
*
|
||||
* @return import result
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private ImportResult doRepositoryImport(RepositoryManager manager,
|
||||
boolean throwExceptions)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
Builder builder = ImportResult.builder();
|
||||
|
||||
logger.trace("search for repositories to import");
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
List<String> repositoryNames =
|
||||
RepositoryUtil.getRepositoryNames(getRepositoryHandler(),
|
||||
getDirectoryNames());
|
||||
|
||||
for (String repositoryName : repositoryNames)
|
||||
{
|
||||
importRepository(manager, builder, throwExceptions, repositoryName);
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
handleException(ex, throwExceptions);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param ex
|
||||
* @param throwExceptions
|
||||
* @param <T>
|
||||
*
|
||||
* @throws T
|
||||
*/
|
||||
private <T extends Exception> void handleException(T ex,
|
||||
boolean throwExceptions)
|
||||
throws T
|
||||
{
|
||||
logger.warn("error durring repository directory import", ex);
|
||||
|
||||
if (throwExceptions)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param manager
|
||||
* @param builder
|
||||
* @param throwExceptions
|
||||
* @param repositoryName
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private void importRepository(RepositoryManager manager, Builder builder,
|
||||
boolean throwExceptions, String repositoryName)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
logger.trace("check repository {} for import", repositoryName);
|
||||
|
||||
Repository repository = manager.get(getTypeName(), repositoryName);
|
||||
|
||||
if (repository == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
importRepository(manager, repositoryName);
|
||||
builder.addImportedDirectory(repositoryName);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
builder.addFailedDirectory(repositoryName);
|
||||
handleException(ex, throwExceptions);
|
||||
}
|
||||
catch (IllegalStateException ex)
|
||||
{
|
||||
builder.addFailedDirectory(repositoryName);
|
||||
handleException(ex, throwExceptions);
|
||||
}
|
||||
catch (RepositoryException ex)
|
||||
{
|
||||
builder.addFailedDirectory(repositoryName);
|
||||
handleException(ex, throwExceptions);
|
||||
}
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("repository {} is allready managed", repositoryName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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;
|
||||
|
||||
/**
|
||||
* Searches and import existing repositories. The {@link AdvancedImportHandler}
|
||||
* gives more control over the result of the import as the
|
||||
* {@link ImportHandler}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
public interface AdvancedImportHandler extends ImportHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Import existing and non managed repositories. Returns result which
|
||||
* contains names of the successfully imported directories and the names of
|
||||
* the failed directories
|
||||
*
|
||||
*
|
||||
* @param manager The global {@link RepositoryManager}
|
||||
*
|
||||
* @return result which contains names of the successfully imported
|
||||
* directories and the names of the failed directories.
|
||||
*/
|
||||
public ImportResult importRepositoriesFromDirectory(
|
||||
RepositoryManager manager);
|
||||
}
|
||||
239
scm-core/src/main/java/sonia/scm/repository/ImportResult.java
Normal file
239
scm-core/src/main/java/sonia/scm/repository/ImportResult.java
Normal file
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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 com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.List;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* Import result of the {@link AdvancedImportHandler}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "import-result")
|
||||
public final class ImportResult
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
ImportResult() {}
|
||||
|
||||
/**
|
||||
* Constructs a new import result.
|
||||
*
|
||||
*
|
||||
* @param importedDirectories imported directories
|
||||
* @param failedDirectories failed directories
|
||||
*/
|
||||
public ImportResult(List<String> importedDirectories,
|
||||
List<String> failedDirectories)
|
||||
{
|
||||
this.importedDirectories = checkNotNull(importedDirectories,
|
||||
"list of imported directories is required");
|
||||
this.failedDirectories = checkNotNull(failedDirectories,
|
||||
"list of failed directories is required");
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a import result builder.
|
||||
*
|
||||
*
|
||||
* @return import result builder
|
||||
*/
|
||||
public static Builder builder()
|
||||
{
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final ImportResult other = (ImportResult) obj;
|
||||
|
||||
return Objects.equal(importedDirectories, other.importedDirectories)
|
||||
&& Objects.equal(failedDirectories, other.failedDirectories);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(importedDirectories, failedDirectories);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
//J-
|
||||
return Objects.toStringHelper(this)
|
||||
.add("importedDirectories", importedDirectories)
|
||||
.add("failedDirectories", failedDirectories)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns list of failed directories.
|
||||
*
|
||||
*
|
||||
* @return list of failed directories
|
||||
*/
|
||||
public List<String> getFailedDirectories()
|
||||
{
|
||||
return failedDirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of successfully imported directories.
|
||||
*
|
||||
*
|
||||
* @return list of successfully imported directories
|
||||
*/
|
||||
public List<String> getImportedDirectories()
|
||||
{
|
||||
return importedDirectories;
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Builder for {@link ImportResult}.
|
||||
*/
|
||||
public static class Builder
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
private Builder() {}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Adds a failed directory to the import result.
|
||||
*
|
||||
*
|
||||
* @param name name of the directory
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public Builder addFailedDirectory(String name)
|
||||
{
|
||||
this.failedDirectories.add(name);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a successfully imported directory to the import result.
|
||||
*
|
||||
*
|
||||
* @param name name of the directory
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public Builder addImportedDirectory(String name)
|
||||
{
|
||||
this.importedDirectories.add(name);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the final import result.
|
||||
*
|
||||
*
|
||||
* @return final import result
|
||||
*/
|
||||
public ImportResult build()
|
||||
{
|
||||
return new ImportResult(ImmutableList.copyOf(importedDirectories),
|
||||
ImmutableList.copyOf(failedDirectories));
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** successfully imported directories */
|
||||
private final List<String> importedDirectories = Lists.newArrayList();
|
||||
|
||||
/** failed directories */
|
||||
private final List<String> failedDirectories = Lists.newArrayList();
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** failed directories */
|
||||
private List<String> failedDirectories;
|
||||
|
||||
/** successfully imported directories */
|
||||
private List<String> importedDirectories;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.api;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* Abstract class for bundle or unbundle command.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.43
|
||||
*/
|
||||
public abstract class AbstractBundleOrUnbundleCommandResponse
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new bundle/unbundle response.
|
||||
*
|
||||
*
|
||||
* @param changesetCount count of bundled/unbundled changesets
|
||||
*/
|
||||
protected AbstractBundleOrUnbundleCommandResponse(long changesetCount)
|
||||
{
|
||||
this.changesetCount = changesetCount;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final AbstractBundleOrUnbundleCommandResponse other =
|
||||
(AbstractBundleOrUnbundleCommandResponse) obj;
|
||||
|
||||
return Objects.equal(changesetCount, other.changesetCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(changesetCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
//J-
|
||||
return Objects.toStringHelper(this)
|
||||
.add("changesetCount", changesetCount)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the count of bundled/unbundled changesets.
|
||||
*
|
||||
*
|
||||
* @return count of bundled/unbundled changesets
|
||||
*/
|
||||
public long getChangesetCount()
|
||||
{
|
||||
return changesetCount;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** count of bundled/unbundled changesets */
|
||||
private final long changesetCount;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.ByteSink;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.io.OutputSupplier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.spi.BundleCommand;
|
||||
import sonia.scm.repository.spi.BundleCommandRequest;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* The bundle command dumps a repository to a byte source such as a file. The
|
||||
* created bundle can be restored to an empty repository with the
|
||||
* {@link UnbundleCommandBuilder}.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.43
|
||||
*/
|
||||
public final class BundleCommandBuilder
|
||||
{
|
||||
|
||||
/** logger for BundleCommandBuilder */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(BundleCommandBuilder.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a new {@link BundleCommandBuilder}.
|
||||
*
|
||||
*
|
||||
* @param bundleCommand bundle command implementation
|
||||
* @param repository repository
|
||||
*/
|
||||
BundleCommandBuilder(BundleCommand bundleCommand, Repository repository)
|
||||
{
|
||||
this.bundleCommand = bundleCommand;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Dumps the repository to the given {@link File}.
|
||||
*
|
||||
* @param outputFile output file
|
||||
*
|
||||
* @return bundle response
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public BundleResponse bundle(File outputFile)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
checkArgument((outputFile != null) &&!outputFile.exists(),
|
||||
"file is null or exists already");
|
||||
|
||||
BundleCommandRequest request =
|
||||
new BundleCommandRequest(Files.asByteSink(outputFile));
|
||||
|
||||
logger.info("create bundle at {} for repository {}", outputFile,
|
||||
repository.getId());
|
||||
|
||||
return bundleCommand.bundle(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the repository to the given {@link OutputStream}.
|
||||
*
|
||||
*
|
||||
* @param outputStream output stream
|
||||
*
|
||||
* @return bundle response
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public BundleResponse bundle(OutputStream outputStream)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
checkNotNull(outputStream, "output stream is required");
|
||||
|
||||
logger.info("bundle {} to output stream", repository.getId());
|
||||
|
||||
return bundleCommand.bundle(
|
||||
new BundleCommandRequest(asByteSink(outputStream)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the repository to the given {@link ByteSink}.
|
||||
*
|
||||
* @param sink byte sink
|
||||
*
|
||||
* @return bundle response
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public BundleResponse bundle(ByteSink sink)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
checkNotNull(sink, "byte sink is required");
|
||||
logger.info("bundle {} to byte sink");
|
||||
|
||||
return bundleCommand.bundle(new BundleCommandRequest(sink));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an {@link OutputStream} into a {@link ByteSink}.
|
||||
*
|
||||
*
|
||||
* @param outputStream ouput stream to convert
|
||||
*
|
||||
* @return converted byte sink
|
||||
*/
|
||||
private ByteSink asByteSink(final OutputStream outputStream)
|
||||
{
|
||||
return ByteStreams.asByteSink(new OutputSupplier<OutputStream>()
|
||||
{
|
||||
|
||||
@Override
|
||||
public OutputStream getOutput() throws IOException
|
||||
{
|
||||
return outputStream;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** bundle command implementation */
|
||||
private final BundleCommand bundleCommand;
|
||||
|
||||
/** repository */
|
||||
private final Repository repository;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
/**
|
||||
* Response of bundle command.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.43
|
||||
*/
|
||||
public final class BundleResponse
|
||||
extends AbstractBundleOrUnbundleCommandResponse
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new bundle response.
|
||||
*
|
||||
*
|
||||
* @param changesetCount count of bundled changesets
|
||||
*/
|
||||
public BundleResponse(long changesetCount)
|
||||
{
|
||||
super(changesetCount);
|
||||
}
|
||||
}
|
||||
@@ -56,5 +56,10 @@ public enum Command
|
||||
/**
|
||||
* @since 1.31
|
||||
*/
|
||||
INCOMING, OUTGOING, PUSH, PULL;
|
||||
INCOMING, OUTGOING, PUSH, PULL,
|
||||
|
||||
/**
|
||||
* @since 1.43
|
||||
*/
|
||||
BUNDLE, UNBUNDLE;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import sonia.scm.security.RepositoryPermission;
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* The pull command pull changes from a other repository.
|
||||
@@ -83,6 +84,38 @@ public final class PullCommandBuilder
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Pull all changes from the given remote url.
|
||||
*
|
||||
*
|
||||
* @param url remote url
|
||||
*
|
||||
* @return informations over the executed pull command
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public PullResponse pull(String url)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
//J-
|
||||
subject.checkPermission(
|
||||
new RepositoryPermission(localRepository, PermissionType.WRITE)
|
||||
);
|
||||
//J+
|
||||
|
||||
URL remoteUrl = new URL(url);
|
||||
request.reset();
|
||||
request.setRemoteUrl(remoteUrl);
|
||||
|
||||
logger.info("pull changes from url {}", url);
|
||||
|
||||
return command.pull(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull all changes from the given remote repository.
|
||||
*
|
||||
@@ -108,6 +141,7 @@ public final class PullCommandBuilder
|
||||
);
|
||||
//J+
|
||||
|
||||
request.reset();
|
||||
request.setRemoteRepository(remoteRepository);
|
||||
|
||||
logger.info("pull changes from {}", remoteRepository);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository.api;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -37,6 +38,9 @@ package sonia.scm.repository.api;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.PermissionType;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
@@ -48,15 +52,25 @@ import sonia.scm.security.RepositoryPermission;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* The push command push changes to a other repository.
|
||||
*
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.31
|
||||
*/
|
||||
public final class PushCommandBuilder
|
||||
{
|
||||
|
||||
/**
|
||||
* the logger for PushCommandBuilder
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(PushCommandBuilder.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a new PushCommandBuilder.
|
||||
*
|
||||
@@ -90,11 +104,39 @@ public final class PushCommandBuilder
|
||||
);
|
||||
//J+
|
||||
|
||||
logger.info("push changes to repository {}", remoteRepository.getId());
|
||||
|
||||
request.reset();
|
||||
request.setRemoteRepository(remoteRepository);
|
||||
|
||||
return command.push(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push all changes to the given remote url.
|
||||
*
|
||||
* @param url url of a remote repository
|
||||
*
|
||||
* @return informations of the executed push command
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public PushResponse push(String url) throws IOException, RepositoryException
|
||||
{
|
||||
|
||||
URL remoteUrl = new URL(url);
|
||||
|
||||
logger.info("push changes to url {}", url);
|
||||
|
||||
request.reset();
|
||||
request.setRemoteUrl(remoteUrl);
|
||||
|
||||
return command.push(request);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** push command implementation */
|
||||
|
||||
@@ -83,6 +83,8 @@ import java.io.IOException;
|
||||
* @apiviz.uses sonia.scm.repository.api.OutgoingCommandBuilder
|
||||
* @apiviz.uses sonia.scm.repository.api.PullCommandBuilder
|
||||
* @apiviz.uses sonia.scm.repository.api.PushCommandBuilder
|
||||
* @apiviz.uses sonia.scm.repository.api.BundleCommandBuilder
|
||||
* @apiviz.uses sonia.scm.repository.api.UnbundleCommandBuilder
|
||||
*/
|
||||
public final class RepositoryService implements Closeable
|
||||
{
|
||||
@@ -204,6 +206,25 @@ public final class RepositoryService implements Closeable
|
||||
repository, preProcessorUtil);
|
||||
}
|
||||
|
||||
/**
|
||||
* The bundle command creates an archive from the repository.
|
||||
*
|
||||
* @return instance of {@link BundleCommandBuilder}
|
||||
* @throws CommandNotSupportedException if the command is not supported
|
||||
* by the implementation of the repository service provider.
|
||||
* @since 1.43
|
||||
*/
|
||||
public BundleCommandBuilder getBundleCommand()
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("create bundle command for repository {}",
|
||||
repository.getName());
|
||||
}
|
||||
|
||||
return new BundleCommandBuilder(provider.getBundleCommand(), repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* The cat command show the content of a given file.
|
||||
*
|
||||
@@ -370,6 +391,26 @@ public final class RepositoryService implements Closeable
|
||||
repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* The unbundle command restores a repository from the given bundle.
|
||||
*
|
||||
* @return instance of {@link UnbundleCommandBuilder}
|
||||
* @throws CommandNotSupportedException if the command is not supported
|
||||
* by the implementation of the repository service provider.
|
||||
* @since 1.43
|
||||
*/
|
||||
public UnbundleCommandBuilder getUnbundleCommand()
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("create bundle command for repository {}",
|
||||
repository.getName());
|
||||
}
|
||||
|
||||
return new UnbundleCommandBuilder(provider.getUnbundleCommand(),
|
||||
repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the command is supported by the repository service.
|
||||
*
|
||||
@@ -390,7 +431,7 @@ public final class RepositoryService implements Closeable
|
||||
* @param feature feature
|
||||
*
|
||||
* @return true if the feature is supported
|
||||
*
|
||||
*
|
||||
* @since 1.25
|
||||
*/
|
||||
public boolean isSupported(Feature feature)
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.io.InputSupplier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.spi.UnbundleCommand;
|
||||
import sonia.scm.repository.spi.UnbundleCommandRequest;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* The unbundle command can restore an empty repository from a bundle. The
|
||||
* bundle can be created with the {@link BundleCommandBuilder}.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.43
|
||||
*/
|
||||
public final class UnbundleCommandBuilder
|
||||
{
|
||||
|
||||
/** logger for UnbundleCommandBuilder */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(UnbundleCommandBuilder.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a new UnbundleCommandBuilder.
|
||||
*
|
||||
*
|
||||
* @param unbundleCommand unbundle command implementation
|
||||
* @param repository repository
|
||||
*/
|
||||
public UnbundleCommandBuilder(UnbundleCommand unbundleCommand,
|
||||
Repository repository)
|
||||
{
|
||||
this.unbundleCommand = unbundleCommand;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Restores the repository from the given bundle.
|
||||
*
|
||||
*
|
||||
* @param inputFile input file
|
||||
*
|
||||
* @return unbundle response
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public UnbundleResponse unbundle(File inputFile)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
checkArgument((inputFile != null) && inputFile.exists(),
|
||||
"existing file is required");
|
||||
|
||||
UnbundleCommandRequest request =
|
||||
createRequest(Files.asByteSource(inputFile));
|
||||
|
||||
logger.info("unbundle archive {} at {}", inputFile, repository.getId());
|
||||
|
||||
return unbundleCommand.unbundle(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the repository from the given bundle.
|
||||
*
|
||||
*
|
||||
* @param inputStream input stream
|
||||
*
|
||||
* @return unbundle response
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public UnbundleResponse unbundle(InputStream inputStream)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
checkNotNull(inputStream, "input stream is required");
|
||||
logger.info("unbundle archive from stream");
|
||||
|
||||
return unbundleCommand.unbundle(createRequest(asByteSource(inputStream)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the repository from the given bundle.
|
||||
*
|
||||
*
|
||||
* @param byteSource byte source
|
||||
*
|
||||
* @return unbundle response
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public UnbundleResponse unbundle(ByteSource byteSource)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
checkNotNull(byteSource, "byte source is required");
|
||||
logger.info("unbundle from byte source");
|
||||
|
||||
return unbundleCommand.unbundle(createRequest(byteSource));
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set to {@code true} if bundle is gzip compressed. Default is {@code false}.
|
||||
*
|
||||
*
|
||||
* @param compressed {@code true} if bundle is gzip compressed
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public UnbundleCommandBuilder setCompressed(boolean compressed)
|
||||
{
|
||||
this.compressed = compressed;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Converts an {@link InputStream} into a {@link ByteSource}.
|
||||
*
|
||||
*
|
||||
* @param inputStream input stream
|
||||
*
|
||||
* @return byte source
|
||||
*/
|
||||
private ByteSource asByteSource(final InputStream inputStream)
|
||||
{
|
||||
return ByteStreams.asByteSource(new InputSupplier<InputStream>()
|
||||
{
|
||||
|
||||
@Override
|
||||
public InputStream getInput() throws IOException
|
||||
{
|
||||
return inputStream;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link UnbundleCommandRequest}.
|
||||
*
|
||||
*
|
||||
* @param source byte source
|
||||
*
|
||||
* @return the create request
|
||||
*/
|
||||
private UnbundleCommandRequest createRequest(ByteSource source)
|
||||
{
|
||||
ByteSource bs;
|
||||
|
||||
if (compressed)
|
||||
{
|
||||
logger.debug("decode gzip stream for unbundle command");
|
||||
bs = new CompressedByteSource(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs = source;
|
||||
}
|
||||
|
||||
return new UnbundleCommandRequest(bs);
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* ByteSource which is able to handle gzip compressed resources.
|
||||
*/
|
||||
private static class CompressedByteSource extends ByteSource
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param wrapped
|
||||
*/
|
||||
public CompressedByteSource(ByteSource wrapped)
|
||||
{
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Opens the stream for reading the compressed source.
|
||||
*
|
||||
*
|
||||
* @return input stream
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public InputStream openStream() throws IOException
|
||||
{
|
||||
return new GZIPInputStream(wrapped.openStream());
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final ByteSource wrapped;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** repository */
|
||||
private final Repository repository;
|
||||
|
||||
/** unbundle command implementation */
|
||||
private final UnbundleCommand unbundleCommand;
|
||||
|
||||
/** Field description */
|
||||
private boolean compressed = false;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
/**
|
||||
* Response of unbundle command.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.43
|
||||
*/
|
||||
public class UnbundleResponse extends AbstractBundleOrUnbundleCommandResponse
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new unbundle response.
|
||||
*
|
||||
*
|
||||
* @param changesetCount count of unbundled changesets
|
||||
*/
|
||||
public UnbundleResponse(long changesetCount)
|
||||
{
|
||||
super(changesetCount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.api.BundleResponse;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Service provider implementation for the bundle command.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.43
|
||||
*/
|
||||
public interface BundleCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The bundle command dumps a repository to a byte source such as a file.
|
||||
*
|
||||
*
|
||||
* @param request bundle command request
|
||||
*
|
||||
* @return bundle response
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public BundleResponse bundle(BundleCommandRequest request)
|
||||
throws IOException, RepositoryException;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.io.ByteSink;
|
||||
|
||||
/**
|
||||
* Request for the bundle command.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.43
|
||||
*/
|
||||
public final class BundleCommandRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new bundle command request.
|
||||
*
|
||||
*
|
||||
* @param archive byte sink archive
|
||||
*/
|
||||
public BundleCommandRequest(ByteSink archive)
|
||||
{
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BundleCommandRequest other = (BundleCommandRequest) obj;
|
||||
|
||||
return Objects.equal(archive, other.archive);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(archive);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the archive as {@link ByteSink}.
|
||||
*
|
||||
*
|
||||
* @return {@link ByteSink} archive.
|
||||
*/
|
||||
ByteSink getArchive()
|
||||
{
|
||||
return archive;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** byte sink archive */
|
||||
private final ByteSink archive;
|
||||
}
|
||||
@@ -33,7 +33,8 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
/**
|
||||
*
|
||||
* Request object for {@link PullCommand}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.31
|
||||
*/
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
/**
|
||||
* Request object for {@link PushCommand}.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.31
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -38,12 +39,16 @@ import com.google.common.base.Objects;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.31
|
||||
*/
|
||||
public abstract class RemoteCommandRequest
|
||||
public abstract class RemoteCommandRequest implements Resetable
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -64,7 +69,8 @@ public abstract class RemoteCommandRequest
|
||||
|
||||
final RemoteCommandRequest other = (RemoteCommandRequest) obj;
|
||||
|
||||
return Objects.equal(remoteRepository, other.remoteRepository);
|
||||
return Objects.equal(remoteRepository, other.remoteRepository)
|
||||
&& Objects.equal(remoteUrl, other.remoteUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +79,19 @@ public abstract class RemoteCommandRequest
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(remoteRepository);
|
||||
return Objects.hashCode(remoteRepository, remoteUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the request object.
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
remoteRepository = null;
|
||||
remoteUrl = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,10 +100,10 @@ public abstract class RemoteCommandRequest
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
||||
//J-
|
||||
return Objects.toStringHelper(this)
|
||||
.add("remoteRepository", remoteRepository)
|
||||
.add("remoteUrl", remoteUrl)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
@@ -102,6 +120,19 @@ public abstract class RemoteCommandRequest
|
||||
this.remoteRepository = remoteRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param remoteUrl
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public void setRemoteUrl(URL remoteUrl)
|
||||
{
|
||||
this.remoteUrl = remoteUrl;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -115,8 +146,24 @@ public abstract class RemoteCommandRequest
|
||||
return remoteRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
URL getRemoteUrl()
|
||||
{
|
||||
return remoteUrl;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
protected Repository remoteRepository;
|
||||
|
||||
/** remote url */
|
||||
protected URL remoteUrl;
|
||||
}
|
||||
|
||||
@@ -115,6 +115,19 @@ public abstract class RepositoryServiceProvider implements Closeable
|
||||
throw new CommandNotSupportedException(Command.BROWSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public BundleCommand getBundleCommand()
|
||||
{
|
||||
throw new CommandNotSupportedException(Command.BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -218,4 +231,17 @@ public abstract class RepositoryServiceProvider implements Closeable
|
||||
{
|
||||
throw new CommandNotSupportedException(Command.TAGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public UnbundleCommand getUnbundleCommand()
|
||||
{
|
||||
throw new CommandNotSupportedException(Command.UNBUNDLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.api.UnbundleResponse;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Service provider implementation for the unbundle command.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.43
|
||||
*/
|
||||
public interface UnbundleCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* The unbundle command can restore an empty repository from a bundle.
|
||||
*
|
||||
*
|
||||
* @param request unbundle request
|
||||
*
|
||||
* @return unbundle response
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public UnbundleResponse unbundle(UnbundleCommandRequest request)
|
||||
throws IOException, RepositoryException;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.io.ByteSource;
|
||||
|
||||
/**
|
||||
* Request object for the unbundle command.
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
* @since 1.43
|
||||
*/
|
||||
public final class UnbundleCommandRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs a new unbundle command request.
|
||||
*
|
||||
*
|
||||
* @param archive byte source archive
|
||||
*/
|
||||
public UnbundleCommandRequest(ByteSource archive)
|
||||
{
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final UnbundleCommandRequest other = (UnbundleCommandRequest) obj;
|
||||
|
||||
return Objects.equal(archive, other.archive);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(archive);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the archive as {@link ByteSource}.
|
||||
*
|
||||
*
|
||||
* @return {@link ByteSource} archive
|
||||
*/
|
||||
ByteSource getArchive()
|
||||
{
|
||||
return archive;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** byte source archive */
|
||||
private final ByteSource archive;
|
||||
}
|
||||
@@ -94,6 +94,20 @@ public class RestUrlProvider implements UrlProvider
|
||||
return HttpUtil.append(baseUrl, PART_AUTHENTICATION).concat(extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
@Override
|
||||
public String getBaseUrl()
|
||||
{
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -48,6 +48,16 @@ public interface UrlProvider
|
||||
*/
|
||||
public String getAuthenticationUrl();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
public String getBaseUrl();
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -77,7 +87,7 @@ public interface UrlProvider
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
*
|
||||
* @since 1.41
|
||||
*/
|
||||
public SecurityUrlProvider getSecurityUrlProvider();
|
||||
|
||||
@@ -88,6 +88,20 @@ public class WUIUrlProvider implements UrlProvider
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.43
|
||||
*/
|
||||
@Override
|
||||
public String getBaseUrl()
|
||||
{
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -73,6 +73,12 @@ public final class HttpUtil
|
||||
/** Field description */
|
||||
public static final String ENCODING = "UTF-8";
|
||||
|
||||
/**
|
||||
* location header
|
||||
* @since 1.43
|
||||
*/
|
||||
public static final String HEADER_LOCATION = "Location";
|
||||
|
||||
/**
|
||||
* header for identifying the scm-manager client
|
||||
* @since 1.19
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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.web.filter;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Helper methods for implementations of the {@link AutoLoginModule}.
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@gmail.com>
|
||||
*
|
||||
* @since 1.42
|
||||
*/
|
||||
public final class AutoLoginModules
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String FLAG_COMPLETE =
|
||||
AutoLoginModules.class.getName().concat("complete");
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
private AutoLoginModules() {}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Mark the request as completed. No further actions will be executed.
|
||||
*
|
||||
* @param request http servlet request
|
||||
*/
|
||||
public static void markAsComplete(HttpServletRequest request)
|
||||
{
|
||||
request.setAttribute(FLAG_COMPLETE, Boolean.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a redirect to the specified url and marks the request as completed.
|
||||
* This method is useful for SSO solutions which have to redirect the user
|
||||
* to a central login page. This method must be used in favor of
|
||||
* {@link HttpServletResponse#sendRedirect(java.lang.String)} which could
|
||||
* result in an error.
|
||||
*
|
||||
* @param request http servlet request
|
||||
* @param response http servlet response
|
||||
* @param url redirect target
|
||||
*
|
||||
* @throws IOException if client could not be redirected
|
||||
*/
|
||||
public static void sendRedirect(HttpServletRequest request,
|
||||
HttpServletResponse response, String url)
|
||||
throws IOException
|
||||
{
|
||||
markAsComplete(request);
|
||||
response.sendRedirect(url);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns {@code true} is the request is marked as complete.
|
||||
*
|
||||
* @param request http servlet request
|
||||
*
|
||||
* @return {@code true} if request is complete
|
||||
*/
|
||||
public static boolean isComplete(HttpServletRequest request)
|
||||
{
|
||||
return request.getAttribute(FLAG_COMPLETE) != null;
|
||||
}
|
||||
}
|
||||
@@ -126,60 +126,17 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
|
||||
throws IOException, ServletException
|
||||
{
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
// get authenticated user or process AutoLoginModule's
|
||||
User user = getAuthenticatedUser(request, response);
|
||||
|
||||
// Fallback to basic authentication scheme
|
||||
if (user == null)
|
||||
if (AutoLoginModules.isComplete(request))
|
||||
{
|
||||
String authentication = request.getHeader(HEADER_AUTHORIZATION);
|
||||
|
||||
if (Util.startWithIgnoreCase(authentication, AUTHORIZATION_BASIC_PREFIX))
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(
|
||||
"found basic authorization header, start authentication");
|
||||
}
|
||||
|
||||
user = authenticate(request, response, subject, authentication);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
if (user != null)
|
||||
{
|
||||
logger.trace("user {} successfully authenticated", user.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.trace("authentcation failed, user object is null");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((configuration != null)
|
||||
&& configuration.isAnonymousAccessEnabled())
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("anonymous access granted");
|
||||
}
|
||||
|
||||
user = SCMContext.ANONYMOUS;
|
||||
}
|
||||
}
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("could not find user send unauthorized");
|
||||
}
|
||||
|
||||
handleUnauthorized(request, response, chain);
|
||||
logger.debug("request marked as complete from an auto login module");
|
||||
}
|
||||
else
|
||||
{
|
||||
chain.doFilter(new SecurityHttpServletRequestWrapper(request, user),
|
||||
response);
|
||||
// process with basic authentication
|
||||
processRequest(request, response, chain, subject, user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +275,79 @@ public class BasicAuthenticationFilter extends AutoLoginFilter
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param chain
|
||||
* @param subject
|
||||
* @param user
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws ServletException
|
||||
*/
|
||||
private void processRequest(HttpServletRequest request,
|
||||
HttpServletResponse response, FilterChain chain, Subject subject, User user)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
|
||||
// Fallback to basic authentication scheme
|
||||
if (user == null)
|
||||
{
|
||||
String authentication = request.getHeader(HEADER_AUTHORIZATION);
|
||||
|
||||
if (Util.startWithIgnoreCase(authentication, AUTHORIZATION_BASIC_PREFIX))
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(
|
||||
"found basic authorization header, start authentication");
|
||||
}
|
||||
|
||||
user = authenticate(request, response, subject, authentication);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
if (user != null)
|
||||
{
|
||||
logger.trace("user {} successfully authenticated", user.getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.trace("authentcation failed, user object is null");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((configuration != null)
|
||||
&& configuration.isAnonymousAccessEnabled())
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("anonymous access granted");
|
||||
}
|
||||
|
||||
user = SCMContext.ANONYMOUS;
|
||||
}
|
||||
}
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("could not find user send unauthorized");
|
||||
}
|
||||
|
||||
handleUnauthorized(request, response, chain);
|
||||
}
|
||||
else
|
||||
{
|
||||
chain.doFilter(new SecurityHttpServletRequestWrapper(request, user),
|
||||
response);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** scm main configuration */
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<body>
|
||||
<div style="text-align: justify;">
|
||||
<p>
|
||||
<img src="http://download.scm-manager.org/images/logo/scm-manager_logo.jpg" />
|
||||
<img src="//download.scm-manager.org/images/logo/scm-manager_logo.jpg" alt="SCM-Manager" />
|
||||
</p>
|
||||
</div>
|
||||
<p></p>
|
||||
@@ -62,7 +62,7 @@
|
||||
<h3>Architecture</h3>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="http://download.scm-manager.org/images/architecture/architecture.001.png" alt="Architecture Overview">
|
||||
<img src="//download.scm-manager.org/images/architecture/architecture.001.png" alt="Architecture Overview">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -33,4 +33,7 @@ http://bitbucket.org/sdorra/scm-manager
|
||||
<extension-point>
|
||||
<class>javax.servlet.ServletContextListener</class>
|
||||
</extension-point>
|
||||
<extension-point>
|
||||
<class>javax.servlet.http.HttpSessionListener</class>
|
||||
</extension-point>
|
||||
</module>
|
||||
|
||||
@@ -37,8 +37,8 @@ package sonia.scm.repository;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevSort;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
@@ -52,8 +52,9 @@ import sonia.scm.util.IOUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -109,76 +110,27 @@ public class GitHookChangesetCollector
|
||||
|
||||
for (ReceiveCommand rc : receiveCommands)
|
||||
{
|
||||
//J-
|
||||
logger.trace("handle receive command, type={}, ref={}, result={}",
|
||||
new Object[] {
|
||||
rc.getType(),
|
||||
rc.getRefName(),
|
||||
rc.getResult()
|
||||
}
|
||||
);
|
||||
//J+
|
||||
|
||||
ObjectId newId = rc.getNewId();
|
||||
|
||||
String branch = GitUtil.getBranch(rc.getRefName());
|
||||
|
||||
walk.reset();
|
||||
walk.sort(RevSort.TOPO);
|
||||
walk.sort(RevSort.REVERSE, true);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
if (rc.getType() != ReceiveCommand.Type.DELETE)
|
||||
{
|
||||
logger.trace("mark {} as start for rev walk", newId.getName());
|
||||
}
|
||||
|
||||
walk.markStart(walk.parseCommit(newId));
|
||||
|
||||
ObjectId oldId = rc.getOldId();
|
||||
|
||||
if ((oldId != null) &&!oldId.equals(ObjectId.zeroId()))
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
try
|
||||
{
|
||||
logger.trace("mark {} as uninteresting for rev walk",
|
||||
oldId.getName());
|
||||
collectChangesets(changesets, converter, walk, rc);
|
||||
}
|
||||
|
||||
walk.markUninteresting(walk.parseCommit(oldId));
|
||||
}
|
||||
|
||||
for (ObjectId id : getExistingObjects(rpack))
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.trace("mark {} as uninteresting for rev walk", id.getName());
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("could not handle receive command, type=");
|
||||
builder.append(rc.getType()).append(", ref=");
|
||||
builder.append(rc.getRefName()).append(", result=");
|
||||
builder.append(rc.getResult());
|
||||
logger.error(builder.toString(), ex);
|
||||
}
|
||||
|
||||
walk.markUninteresting(walk.parseCommit(id));
|
||||
}
|
||||
|
||||
RevCommit commit = walk.next();
|
||||
|
||||
List<String> branches = Lists.newArrayList(branch);
|
||||
|
||||
while (commit != null)
|
||||
else
|
||||
{
|
||||
|
||||
// parse commit body to avoid npe
|
||||
walk.parseBody(commit);
|
||||
|
||||
Changeset changeset = converter.createChangeset(commit, branches);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("retrive commit {} for hook", changeset.getId());
|
||||
}
|
||||
|
||||
changesets.add(changeset);
|
||||
|
||||
commit = walk.next();
|
||||
logger.debug("skip delete of branch {}", rc.getRefName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -195,38 +147,78 @@ public class GitHookChangesetCollector
|
||||
return changesets;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param rpack
|
||||
* @param changesets
|
||||
* @param converter
|
||||
* @param walk
|
||||
* @param rc
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws IncorrectObjectTypeException
|
||||
*/
|
||||
private List<ObjectId> getExistingObjects(ReceivePack rpack)
|
||||
private void collectChangesets(List<Changeset> changesets,
|
||||
GitChangesetConverter converter, RevWalk walk, ReceiveCommand rc)
|
||||
throws IncorrectObjectTypeException, IOException
|
||||
{
|
||||
List<ObjectId> existingObjects = Lists.newArrayList();
|
||||
|
||||
if (existingObjects == null)
|
||||
{
|
||||
Map<String, Ref> refs = rpack.getRepository().getAllRefs();
|
||||
|
||||
for (Ref r : refs.values())
|
||||
{
|
||||
existingObjects.add(r.getObjectId());
|
||||
//J-
|
||||
logger.trace("handle receive command, type={}, ref={}, result={}",
|
||||
new Object[] {
|
||||
rc.getType(),
|
||||
rc.getRefName(),
|
||||
rc.getResult()
|
||||
}
|
||||
);
|
||||
//J+
|
||||
|
||||
ObjectId newId = rc.getNewId();
|
||||
|
||||
String branch = GitUtil.getBranch(rc.getRefName());
|
||||
|
||||
walk.reset();
|
||||
walk.sort(RevSort.TOPO);
|
||||
walk.sort(RevSort.REVERSE, true);
|
||||
|
||||
logger.trace("mark {} as start for rev walk", newId.getName());
|
||||
|
||||
walk.markStart(walk.parseCommit(newId));
|
||||
|
||||
ObjectId oldId = rc.getOldId();
|
||||
|
||||
if ((oldId != null) &&!oldId.equals(ObjectId.zeroId()))
|
||||
{
|
||||
logger.trace("mark {} as uninteresting for rev walk", oldId.getName());
|
||||
|
||||
walk.markUninteresting(walk.parseCommit(oldId));
|
||||
}
|
||||
|
||||
return existingObjects;
|
||||
RevCommit commit = walk.next();
|
||||
|
||||
List<String> branches = Lists.newArrayList(branch);
|
||||
|
||||
while (commit != null)
|
||||
{
|
||||
|
||||
// parse commit body to avoid npe
|
||||
walk.parseBody(commit);
|
||||
|
||||
Changeset changeset = converter.createChangeset(commit, branches);
|
||||
|
||||
logger.trace("retrive commit {} for hook", changeset.getId());
|
||||
|
||||
changesets.add(changeset);
|
||||
|
||||
commit = walk.next();
|
||||
}
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private List<ReceiveCommand> receiveCommands;
|
||||
private final List<ReceiveCommand> receiveCommands;
|
||||
|
||||
/** Field description */
|
||||
private ReceivePack rpack;
|
||||
private final ReceivePack rpack;
|
||||
}
|
||||
|
||||
@@ -695,6 +695,32 @@ public final class GitUtil
|
||||
//J+
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param ref
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isHead(String ref)
|
||||
{
|
||||
return ref.startsWith(REF_HEAD_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param id
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isValidObjectId(ObjectId id)
|
||||
{
|
||||
return (id != null) &&!id.equals(ObjectId.zeroId());
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,6 +48,8 @@ import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
@@ -61,17 +63,17 @@ import java.util.Collection;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public abstract class AbstractPushOrPullCommand extends AbstractGitCommand
|
||||
public abstract class AbstractGitPushOrPullCommand extends AbstractGitCommand
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String SCHEME = "scm://";
|
||||
|
||||
/**
|
||||
* the logger for AbstractPushOrPullCommand
|
||||
* the logger for AbstractGitPushOrPullCommand
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(AbstractPushOrPullCommand.class);
|
||||
LoggerFactory.getLogger(AbstractGitPushOrPullCommand.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
@@ -79,36 +81,38 @@ public abstract class AbstractPushOrPullCommand extends AbstractGitCommand
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param handler
|
||||
* @param context
|
||||
* @param repository
|
||||
*/
|
||||
public AbstractPushOrPullCommand(GitContext context,
|
||||
sonia.scm.repository.Repository repository)
|
||||
protected AbstractGitPushOrPullCommand(GitRepositoryHandler handler,
|
||||
GitContext context, sonia.scm.repository.Repository repository)
|
||||
{
|
||||
super(context, repository);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param source
|
||||
* @param target
|
||||
* @param remoteUrl
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
protected long push(Repository source, File target)
|
||||
protected long push(Repository source, String remoteUrl)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
Git git = Git.wrap(source);
|
||||
org.eclipse.jgit.api.PushCommand push = git.push();
|
||||
|
||||
push.setPushAll().setPushTags();
|
||||
push.setRemote(SCHEME.concat(target.getAbsolutePath()));
|
||||
push.setRemote(remoteUrl);
|
||||
|
||||
long counter = -1;
|
||||
|
||||
@@ -158,6 +162,61 @@ public abstract class AbstractPushOrPullCommand extends AbstractGitCommand
|
||||
return remoteRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getRemoteUrl(RemoteCommandRequest request)
|
||||
{
|
||||
String url;
|
||||
sonia.scm.repository.Repository remRepo = request.getRemoteRepository();
|
||||
|
||||
if (remRepo != null)
|
||||
{
|
||||
url = getRemoteUrl(remRepo);
|
||||
}
|
||||
else if (request.getRemoteUrl() != null)
|
||||
{
|
||||
url = request.getRemoteUrl().toExternalForm();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("repository or url is requiered");
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param directory
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getRemoteUrl(File directory)
|
||||
{
|
||||
return SCHEME.concat(directory.getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param repository
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getRemoteUrl(sonia.scm.repository.Repository repository)
|
||||
{
|
||||
return getRemoteUrl(handler.getDirectory(repository));
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -195,21 +254,24 @@ public abstract class AbstractPushOrPullCommand extends AbstractGitCommand
|
||||
{
|
||||
long counter = 0;
|
||||
|
||||
try
|
||||
if (GitUtil.isHead(update.getRemoteName()))
|
||||
{
|
||||
org.eclipse.jgit.api.LogCommand log = git.log();
|
||||
ObjectId oldId = update.getExpectedOldObjectId();
|
||||
|
||||
if (oldId != null)
|
||||
try
|
||||
{
|
||||
log.not(oldId);
|
||||
}
|
||||
org.eclipse.jgit.api.LogCommand log = git.log();
|
||||
ObjectId oldId = update.getExpectedOldObjectId();
|
||||
|
||||
ObjectId newId = update.getNewObjectId();
|
||||
if (GitUtil.isValidObjectId(oldId))
|
||||
{
|
||||
log.not(oldId);
|
||||
}
|
||||
|
||||
if (newId != null)
|
||||
{
|
||||
log.add(newId);
|
||||
ObjectId newId = update.getNewObjectId();
|
||||
|
||||
if (GitUtil.isValidObjectId(newId))
|
||||
{
|
||||
log.add(newId);
|
||||
}
|
||||
|
||||
Iterable<RevCommit> commits = log.call();
|
||||
|
||||
@@ -217,18 +279,24 @@ public abstract class AbstractPushOrPullCommand extends AbstractGitCommand
|
||||
{
|
||||
counter += Iterables.size(commits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("update without new object id");
|
||||
}
|
||||
|
||||
logger.trace("counting {} commits for ref update {}", counter, update);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error("could not count pushed/pulled changesets", ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
logger.error("could not count pushed/pulled changesets", ex);
|
||||
logger.debug("do not count non branch ref update {}", update);
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
protected GitRepositoryHandler handler;
|
||||
}
|
||||
@@ -36,8 +36,19 @@ package sonia.scm.repository.spi;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.transport.FetchResult;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.transport.TagOpt;
|
||||
import org.eclipse.jgit.transport.TrackingRefUpdate;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
@@ -50,27 +61,37 @@ import sonia.scm.repository.api.PullResponse;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class GitPullCommand extends AbstractPushOrPullCommand
|
||||
public class GitPullCommand extends AbstractGitPushOrPullCommand
|
||||
implements PullCommand
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final String REF_SPEC = "refs/heads/*:refs/heads/*";
|
||||
|
||||
/** Field description */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(GitPullCommand.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param repositoryHandler
|
||||
* @param handler
|
||||
* @param context
|
||||
* @param repository
|
||||
*/
|
||||
public GitPullCommand(GitRepositoryHandler repositoryHandler,
|
||||
GitContext context, Repository repository)
|
||||
public GitPullCommand(GitRepositoryHandler handler, GitContext context,
|
||||
Repository repository)
|
||||
{
|
||||
super(context, repository);
|
||||
this.repositoryHandler = repositoryHandler;
|
||||
super(handler, context, repository);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -90,18 +111,134 @@ public class GitPullCommand extends AbstractPushOrPullCommand
|
||||
public PullResponse pull(PullCommandRequest request)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
Repository sourceRepository = getRemoteRepository(request);
|
||||
PullResponse response;
|
||||
Repository sourceRepository = request.getRemoteRepository();
|
||||
|
||||
File sourceDirectory = repositoryHandler.getDirectory(sourceRepository);
|
||||
if (sourceRepository != null)
|
||||
{
|
||||
response = pullFromScmRepository(sourceRepository);
|
||||
}
|
||||
else if (request.getRemoteUrl() != null)
|
||||
{
|
||||
response = pullFromUrl(request.getRemoteUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("repository or url is required");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param git
|
||||
* @param result
|
||||
* @param fetch
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private PullResponse convert(Git git, FetchResult fetch)
|
||||
throws RepositoryException
|
||||
{
|
||||
long counter = 0l;
|
||||
|
||||
for (TrackingRefUpdate tru : fetch.getTrackingRefUpdates())
|
||||
{
|
||||
counter += count(git, tru);
|
||||
}
|
||||
|
||||
logger.debug("received {} changesets by pull", counter);
|
||||
|
||||
return new PullResponse(counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param git
|
||||
* @param tru
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private long count(Git git, TrackingRefUpdate tru)
|
||||
{
|
||||
long counter = 0;
|
||||
|
||||
if (GitUtil.isHead(tru.getLocalName()))
|
||||
{
|
||||
try
|
||||
{
|
||||
org.eclipse.jgit.api.LogCommand log = git.log();
|
||||
|
||||
ObjectId oldId = tru.getOldObjectId();
|
||||
|
||||
if (GitUtil.isValidObjectId(oldId))
|
||||
{
|
||||
log.not(oldId);
|
||||
}
|
||||
|
||||
ObjectId newId = tru.getNewObjectId();
|
||||
|
||||
if (GitUtil.isValidObjectId(newId))
|
||||
{
|
||||
log.add(newId);
|
||||
}
|
||||
|
||||
Iterable<RevCommit> commits = log.call();
|
||||
|
||||
if (commits != null)
|
||||
{
|
||||
counter += Iterables.size(commits);
|
||||
}
|
||||
|
||||
logger.trace("counting {} commits for ref update {}", counter, tru);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error("could not count pushed/pulled changesets", ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("do not count non branch ref update {}", tru);
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param sourceRepository
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private PullResponse pullFromScmRepository(Repository sourceRepository)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
File sourceDirectory = handler.getDirectory(sourceRepository);
|
||||
|
||||
Preconditions.checkArgument(sourceDirectory.exists(),
|
||||
"source repository directory does not exists");
|
||||
|
||||
File targetDirectory = repositoryHandler.getDirectory(repository);
|
||||
File targetDirectory = handler.getDirectory(repository);
|
||||
|
||||
Preconditions.checkArgument(sourceDirectory.exists(),
|
||||
"target repository directory does not exists");
|
||||
|
||||
logger.debug("pull changes from {} to {}",
|
||||
sourceDirectory.getAbsolutePath(), repository.getId());
|
||||
|
||||
PullResponse response = null;
|
||||
|
||||
org.eclipse.jgit.lib.Repository source = null;
|
||||
@@ -109,7 +246,7 @@ public class GitPullCommand extends AbstractPushOrPullCommand
|
||||
try
|
||||
{
|
||||
source = Git.open(sourceDirectory).getRepository();
|
||||
response = new PullResponse(push(source, targetDirectory));
|
||||
response = new PullResponse(push(source, getRemoteUrl(targetDirectory)));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -119,8 +256,42 @@ public class GitPullCommand extends AbstractPushOrPullCommand
|
||||
return response;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private PullResponse pullFromUrl(URL url)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
logger.debug("pull changes from {} to {}", url, repository.getId());
|
||||
|
||||
/** Field description */
|
||||
private GitRepositoryHandler repositoryHandler;
|
||||
PullResponse response;
|
||||
Git git = Git.wrap(open());
|
||||
|
||||
try
|
||||
{
|
||||
//J-
|
||||
FetchResult result = git.fetch()
|
||||
.setRefSpecs(new RefSpec(REF_SPEC))
|
||||
.setRemote(url.toExternalForm())
|
||||
.setTagOpt(TagOpt.FETCH_TAGS)
|
||||
.call();
|
||||
//J+
|
||||
|
||||
response = convert(git, result);
|
||||
}
|
||||
catch (GitAPIException ex)
|
||||
{
|
||||
throw new RepositoryException("error durring pull", ex);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,13 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
@@ -43,17 +45,22 @@ import sonia.scm.repository.api.PushResponse;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class GitPushCommand extends AbstractPushOrPullCommand
|
||||
public class GitPushCommand extends AbstractGitPushOrPullCommand
|
||||
implements PushCommand
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(GitPushCommand.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
@@ -65,7 +72,7 @@ public class GitPushCommand extends AbstractPushOrPullCommand
|
||||
public GitPushCommand(GitRepositoryHandler handler, GitContext context,
|
||||
Repository repository)
|
||||
{
|
||||
super(context, repository);
|
||||
super(handler, context, repository);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@@ -86,17 +93,10 @@ public class GitPushCommand extends AbstractPushOrPullCommand
|
||||
public PushResponse push(PushCommandRequest request)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
Repository target = getRemoteRepository(request);
|
||||
File targetDirectory = handler.getDirectory(target);
|
||||
String remoteUrl = getRemoteUrl(request);
|
||||
|
||||
Preconditions.checkArgument(targetDirectory.exists(),
|
||||
"target repository directory does not exists");
|
||||
logger.debug("push changes from {} to {}", repository.getId(), remoteUrl);
|
||||
|
||||
return new PushResponse(push(open(), targetDirectory));
|
||||
return new PushResponse(push(open(), remoteUrl));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private GitRepositoryHandler handler;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import com.google.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.repository.Branch;
|
||||
import sonia.scm.repository.Branches;
|
||||
import sonia.scm.repository.Changeset;
|
||||
@@ -60,6 +59,7 @@ import sonia.scm.template.TemplateEngineFactory;
|
||||
import sonia.scm.url.RepositoryUrlProvider;
|
||||
import sonia.scm.url.UrlProvider;
|
||||
import sonia.scm.url.UrlProviderFactory;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
@@ -85,7 +85,8 @@ public class GitRepositoryViewer
|
||||
public static final String MIMETYPE_HTML = "text/html";
|
||||
|
||||
/** Field description */
|
||||
public static final String RESOURCE_GITINDEX = "/sonia/scm/git.index.mustache";
|
||||
public static final String RESOURCE_GITINDEX =
|
||||
"/sonia/scm/git.index.mustache";
|
||||
|
||||
/** Field description */
|
||||
private static final int CHANGESET_PER_BRANCH = 10;
|
||||
@@ -104,16 +105,13 @@ public class GitRepositoryViewer
|
||||
*
|
||||
* @param templateEngineFactory
|
||||
* @param repositoryServiceFactory
|
||||
* @param configuration
|
||||
*/
|
||||
@Inject
|
||||
public GitRepositoryViewer(TemplateEngineFactory templateEngineFactory,
|
||||
RepositoryServiceFactory repositoryServiceFactory,
|
||||
ScmConfiguration configuration)
|
||||
RepositoryServiceFactory repositoryServiceFactory)
|
||||
{
|
||||
this.templateEngineFactory = templateEngineFactory;
|
||||
this.repositoryServiceFactory = repositoryServiceFactory;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -135,7 +133,9 @@ public class GitRepositoryViewer
|
||||
throws RepositoryException, IOException
|
||||
{
|
||||
|
||||
String baseUrl = configuration.getBaseUrl();
|
||||
String baseUrl = HttpUtil.getCompleteUrl(request);
|
||||
|
||||
logger.trace("render git repository quick view with base url {}", baseUrl);
|
||||
|
||||
UrlProvider urlProvider = UrlProviderFactory.createUrlProvider(baseUrl,
|
||||
UrlProviderFactory.TYPE_WUI);
|
||||
@@ -159,6 +159,7 @@ public class GitRepositoryViewer
|
||||
|
||||
try
|
||||
{
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
writer = response.getWriter();
|
||||
template.execute(writer, env);
|
||||
}
|
||||
@@ -453,9 +454,6 @@ public class GitRepositoryViewer
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
/** Field description */
|
||||
private final RepositoryServiceFactory repositoryServiceFactory;
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ package sonia.scm.repository.spi;
|
||||
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.transport.ScmTransportProtocol;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -106,9 +105,4 @@ public class GitPushCommandTest extends AbstractRemoteCommandTestBase
|
||||
return new GitPushCommand(handler, new GitContext(outgoingDirectory),
|
||||
outgoingRepository);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private ScmTransportProtocol proto;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
<groupId>com.aragost.javahg</groupId>
|
||||
<artifactId>javahg</artifactId>
|
||||
<version>0.8-SNAPSHOT</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- test scope -->
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
*/
|
||||
public class AbstractHgPushOrPullCommand extends AbstractCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param handler
|
||||
* @param context
|
||||
* @param repository
|
||||
*/
|
||||
protected AbstractHgPushOrPullCommand(HgRepositoryHandler handler,
|
||||
HgCommandContext context, Repository repository)
|
||||
{
|
||||
super(context, repository);
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getRemoteUrl(RemoteCommandRequest request)
|
||||
{
|
||||
String url;
|
||||
Repository repo = request.getRemoteRepository();
|
||||
|
||||
if (repo != null)
|
||||
{
|
||||
url =
|
||||
handler.getDirectory(request.getRemoteRepository()).getAbsolutePath();
|
||||
}
|
||||
else if (request.getRemoteUrl() != null)
|
||||
{
|
||||
url = request.getRemoteUrl().toExternalForm();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("url or repository is required");
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
protected final HgRepositoryHandler handler;
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -37,6 +38,9 @@ package sonia.scm.repository.spi;
|
||||
import com.aragost.javahg.Changeset;
|
||||
import com.aragost.javahg.commands.ExecutionException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
@@ -44,7 +48,6 @@ import sonia.scm.repository.api.PullResponse;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -54,9 +57,16 @@ import java.util.List;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgPullCommand extends AbstractCommand implements PullCommand
|
||||
public class HgPullCommand extends AbstractHgPushOrPullCommand
|
||||
implements PullCommand
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(HgPullCommand.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
@@ -68,8 +78,7 @@ public class HgPullCommand extends AbstractCommand implements PullCommand
|
||||
public HgPullCommand(HgRepositoryHandler handler, HgCommandContext context,
|
||||
Repository repository)
|
||||
{
|
||||
super(context, repository);
|
||||
this.handler = handler;
|
||||
super(handler, context, repository);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -90,14 +99,15 @@ public class HgPullCommand extends AbstractCommand implements PullCommand
|
||||
public PullResponse pull(PullCommandRequest request)
|
||||
throws RepositoryException, IOException
|
||||
{
|
||||
File remoteRepository = handler.getDirectory(request.getRemoteRepository());
|
||||
String url = getRemoteUrl(request);
|
||||
|
||||
logger.debug("pull changes from {} to {}", url, getRepository().getId());
|
||||
|
||||
List<Changeset> result = Collections.EMPTY_LIST;
|
||||
|
||||
try
|
||||
{
|
||||
result = com.aragost.javahg.commands.PullCommand.on(open()).execute(
|
||||
remoteRepository.getAbsolutePath());
|
||||
result = com.aragost.javahg.commands.PullCommand.on(open()).execute(url);
|
||||
}
|
||||
catch (ExecutionException ex)
|
||||
{
|
||||
@@ -106,9 +116,4 @@ public class HgPullCommand extends AbstractCommand implements PullCommand
|
||||
|
||||
return new PullResponse(result.size());
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final HgRepositoryHandler handler;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
@@ -37,6 +38,9 @@ package sonia.scm.repository.spi;
|
||||
import com.aragost.javahg.Changeset;
|
||||
import com.aragost.javahg.commands.ExecutionException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.repository.HgRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
@@ -44,7 +48,6 @@ import sonia.scm.repository.api.PushResponse;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -54,9 +57,16 @@ import java.util.List;
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class HgPushCommand extends AbstractCommand implements PushCommand
|
||||
public class HgPushCommand extends AbstractHgPushOrPullCommand
|
||||
implements PushCommand
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(HgPushCommand.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
@@ -68,8 +78,7 @@ public class HgPushCommand extends AbstractCommand implements PushCommand
|
||||
public HgPushCommand(HgRepositoryHandler handler, HgCommandContext context,
|
||||
Repository repository)
|
||||
{
|
||||
super(context, repository);
|
||||
this.handler = handler;
|
||||
super(handler, context, repository);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -90,14 +99,15 @@ public class HgPushCommand extends AbstractCommand implements PushCommand
|
||||
public PushResponse push(PushCommandRequest request)
|
||||
throws RepositoryException, IOException
|
||||
{
|
||||
File remoteRepository = handler.getDirectory(request.getRemoteRepository());
|
||||
String url = getRemoteUrl(request);
|
||||
|
||||
logger.debug("push changes from {} to {}", getRepository().getId(), url);
|
||||
|
||||
List<Changeset> result = Collections.EMPTY_LIST;
|
||||
|
||||
try
|
||||
{
|
||||
result = com.aragost.javahg.commands.PushCommand.on(open()).execute(
|
||||
remoteRepository.getAbsolutePath());
|
||||
result = com.aragost.javahg.commands.PushCommand.on(open()).execute(url);
|
||||
}
|
||||
catch (ExecutionException ex)
|
||||
{
|
||||
@@ -106,9 +116,4 @@ public class HgPushCommand extends AbstractCommand implements PushCommand
|
||||
|
||||
return new PushResponse(result.size());
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final HgRepositoryHandler handler;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.ByteSink;
|
||||
import com.google.common.io.Closeables;
|
||||
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.wc.SVNClientManager;
|
||||
import org.tmatesoft.svn.core.wc.SVNRevision;
|
||||
import org.tmatesoft.svn.core.wc.admin.SVNAdminClient;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.SvnUtil;
|
||||
import sonia.scm.repository.api.BundleResponse;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
*/
|
||||
public class SvnBundleCommand extends AbstractSvnCommand
|
||||
implements BundleCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param repository
|
||||
*/
|
||||
public SvnBundleCommand(SvnContext context, Repository repository)
|
||||
{
|
||||
super(context, repository);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param adminClient
|
||||
* @param repository
|
||||
* @param target
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws SVNException
|
||||
*/
|
||||
private static void dump(SVNAdminClient adminClient, File repository,
|
||||
ByteSink target)
|
||||
throws SVNException, IOException
|
||||
{
|
||||
OutputStream outputStream = null;
|
||||
|
||||
try
|
||||
{
|
||||
outputStream = target.openBufferedStream();
|
||||
adminClient.doDump(repository, outputStream, SVNRevision.create(-1l),
|
||||
SVNRevision.HEAD, false, false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Closeables.close(outputStream, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Override
|
||||
public BundleResponse bundle(BundleCommandRequest request)
|
||||
throws IOException, RepositoryException
|
||||
{
|
||||
ByteSink archive = checkNotNull(request.getArchive(),
|
||||
"archive is required");
|
||||
|
||||
BundleResponse response;
|
||||
|
||||
SVNClientManager clientManager = null;
|
||||
|
||||
try
|
||||
{
|
||||
clientManager = SVNClientManager.newInstance();
|
||||
|
||||
SVNAdminClient adminClient = clientManager.getAdminClient();
|
||||
|
||||
dump(adminClient, context.getDirectory(), archive);
|
||||
response = new BundleResponse(context.open().getLatestRevision());
|
||||
}
|
||||
catch (SVNException ex)
|
||||
{
|
||||
throw new IOException("could not create dump", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SvnUtil.dispose(clientManager);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -56,9 +56,12 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final Set<Command> COMMANDS = ImmutableSet.of(Command.BLAME,
|
||||
Command.BROWSE, Command.CAT,
|
||||
Command.DIFF, Command.LOG);
|
||||
//J-
|
||||
public static final Set<Command> COMMANDS = ImmutableSet.of(
|
||||
Command.BLAME, Command.BROWSE, Command.CAT, Command.DIFF,
|
||||
Command.LOG, Command.BUNDLE, Command.UNBUNDLE
|
||||
);
|
||||
//J+
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
@@ -116,6 +119,18 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
return new SvnBrowseCommand(context, repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public BundleCommand getBundleCommand()
|
||||
{
|
||||
return new SvnBundleCommand(context, repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
@@ -164,6 +179,18 @@ public class SvnRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
return COMMANDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UnbundleCommand getUnbundleCommand()
|
||||
{
|
||||
return new SvnUnbundleCommand(context, repository);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.common.io.Closeables;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.wc.SVNClientManager;
|
||||
import org.tmatesoft.svn.core.wc.admin.SVNAdminClient;
|
||||
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.SvnUtil;
|
||||
import sonia.scm.repository.api.UnbundleResponse;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra <s.sdorra@gmail.com>
|
||||
*/
|
||||
public class SvnUnbundleCommand extends AbstractSvnCommand
|
||||
implements UnbundleCommand
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(SvnUnbundleCommand.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param repository
|
||||
*/
|
||||
public SvnUnbundleCommand(SvnContext context, Repository repository)
|
||||
{
|
||||
super(context, repository);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public UnbundleResponse unbundle(UnbundleCommandRequest request)
|
||||
throws IOException
|
||||
{
|
||||
ByteSource archive = checkNotNull(request.getArchive(),
|
||||
"archive is required");
|
||||
|
||||
logger.debug("archive repository {} to {}", context.getDirectory(),
|
||||
archive);
|
||||
|
||||
UnbundleResponse response;
|
||||
|
||||
SVNClientManager clientManager = null;
|
||||
|
||||
try
|
||||
{
|
||||
clientManager = SVNClientManager.newInstance();
|
||||
|
||||
SVNAdminClient adminClient = clientManager.getAdminClient();
|
||||
|
||||
restore(adminClient, archive, context.getDirectory());
|
||||
|
||||
response = new UnbundleResponse(context.open().getLatestRevision());
|
||||
}
|
||||
catch (SVNException ex)
|
||||
{
|
||||
throw new IOException("could not restore dump", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SvnUtil.dispose(clientManager);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param adminClient
|
||||
* @param dump
|
||||
* @param repository
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws SVNException
|
||||
*/
|
||||
private void restore(SVNAdminClient adminClient, ByteSource dump,
|
||||
File repository)
|
||||
throws SVNException, IOException
|
||||
{
|
||||
InputStream inputStream = null;
|
||||
|
||||
try
|
||||
{
|
||||
inputStream = dump.openBufferedStream();
|
||||
adminClient.doLoad(repository, inputStream);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Closeables.close(inputStream, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.ByteSink;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.api.BundleResponse;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class SvnBundleCommandTest extends AbstractSvnCommandTestBase
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testBundle() throws IOException, RepositoryException
|
||||
{
|
||||
File file = temp.newFile();
|
||||
ByteSink sink = Files.asByteSink(file);
|
||||
BundleCommandRequest req = new BundleCommandRequest(sink);
|
||||
BundleResponse res = new SvnBundleCommand(createContext(),
|
||||
repository).bundle(req);
|
||||
|
||||
assertThat(res, notNullValue());
|
||||
assertThat(res.getChangesetCount(), is(5l));
|
||||
assertTrue("file does not exists", file.exists());
|
||||
assertThat(file.length(), greaterThan(0l));
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@Rule
|
||||
public TemporaryFolder temp = new TemporaryFolder();
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) 2014, 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.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.io.SVNRepository;
|
||||
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
|
||||
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.SvnUtil;
|
||||
import sonia.scm.repository.api.UnbundleResponse;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class SvnUnbundleCommandTest extends AbstractSvnCommandTestBase
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
* @throws SVNException
|
||||
*/
|
||||
@Test
|
||||
public void testUnbundle()
|
||||
throws IOException, RepositoryException, SVNException
|
||||
{
|
||||
File bundle = bundle();
|
||||
SvnContext ctx = createEmptyContext();
|
||||
//J-
|
||||
UnbundleResponse res = new SvnUnbundleCommand(
|
||||
ctx,
|
||||
repository
|
||||
).unbundle(
|
||||
new UnbundleCommandRequest(
|
||||
Files.asByteSource(bundle)
|
||||
)
|
||||
);
|
||||
//J+
|
||||
|
||||
assertThat(res, notNullValue());
|
||||
assertThat(res.getChangesetCount(), is(5l));
|
||||
|
||||
SVNRepository repo = ctx.open();
|
||||
|
||||
assertThat(repo.getLatestRevision(), is(5l));
|
||||
SvnUtil.closeSession(repo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
private File bundle() throws IOException, RepositoryException
|
||||
{
|
||||
File file = tempFolder.newFile();
|
||||
|
||||
//J-
|
||||
new SvnBundleCommand(
|
||||
createContext(),
|
||||
repository
|
||||
).bundle(
|
||||
new BundleCommandRequest(
|
||||
Files.asByteSink(file)
|
||||
)
|
||||
);
|
||||
//J+
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws SVNException
|
||||
*/
|
||||
private SvnContext createEmptyContext() throws IOException, SVNException
|
||||
{
|
||||
File folder = tempFolder.newFolder();
|
||||
|
||||
SVNRepositoryFactory.createLocalRepository(folder, true, true);
|
||||
|
||||
return new SvnContext(folder);
|
||||
}
|
||||
}
|
||||
@@ -127,5 +127,90 @@
|
||||
</Set>
|
||||
</New>
|
||||
</Set>
|
||||
<<<<<<< mine
|
||||
=======
|
||||
|
||||
<!-- request logging -->
|
||||
<!--
|
||||
<Ref id="RequestLog">
|
||||
<Set name="requestLog">
|
||||
<New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
|
||||
<Arg><SystemProperty name="basedir" default="."/>/var/log/yyyy_mm_dd.request.log</Arg>
|
||||
<Set name="retainDays">90</Set>
|
||||
<Set name="append">true</Set>
|
||||
<Set name="extended">false</Set>
|
||||
<Set name="LogTimeZone">GMT</Set>
|
||||
</New>
|
||||
</Set>
|
||||
</Ref>
|
||||
-->
|
||||
|
||||
<!-- mod_proxy_ajp or mod_jk -->
|
||||
<!--
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.ajp.Ajp13SocketConnector">
|
||||
<Set name="port">8009</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
-->
|
||||
|
||||
<!-- SSL-Connector -->
|
||||
<!--
|
||||
Documentation for the SSL-Connector:
|
||||
http://wiki.eclipse.org/Jetty/Reference/SSL_Connectors
|
||||
-->
|
||||
<!--
|
||||
Besure SSLv3 protocol is excluded to avoid POODLE vulnerability.
|
||||
See https://groups.google.com/d/msg/scmmanager/sX_Ydy-wAPA/-Dvs5i7RHtQJ
|
||||
-->
|
||||
<!--
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.ssl.SslSelectChannelConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.http.ssl.SslContextFactory">
|
||||
<Set name="excludeProtocols">
|
||||
<Array type="java.lang.String">
|
||||
<Item>SSLv2Hello</Item>
|
||||
<Item>SSLv3</Item>
|
||||
</Array>
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
<Set name="Port">8181</Set>
|
||||
<Set name="maxIdleTime">30000</Set>
|
||||
<Set name="requestHeaderSize">16384</Set>
|
||||
<Set name="keystore"><SystemProperty name="basedir" default="." />/conf/keystore.jks</Set>
|
||||
<Set name="password">OBF:xxx</Set>
|
||||
<Set name="keyPassword">OBF:xxx</Set>
|
||||
<Set name="truststore"><SystemProperty name="basedir" default="." />/conf/keystore.jks</Set>
|
||||
<Set name="trustPassword">OBF:xxx</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
-->
|
||||
|
||||
<!-- JMX support -->
|
||||
<!--
|
||||
<Call id="MBeanServer" class="java.lang.management.ManagementFactory"
|
||||
name="getPlatformMBeanServer" />
|
||||
|
||||
<New id="MBeanContainer" class="org.eclipse.jetty.jmx.MBeanContainer">
|
||||
<Arg>
|
||||
<Ref id="MBeanServer" />
|
||||
</Arg>
|
||||
</New>
|
||||
|
||||
<Get id="Container" name="container">
|
||||
<Call name="addEventListener">
|
||||
<Arg>
|
||||
<Ref id="MBeanContainer" />
|
||||
</Arg>
|
||||
</Call>
|
||||
</Get>
|
||||
-->
|
||||
>>>>>>> theirs
|
||||
|
||||
</Configure>
|
||||
|
||||
@@ -52,6 +52,9 @@ public class ScmServer extends Thread
|
||||
/** Field description */
|
||||
public static final String CONFIGURATION = "/server-config.xml";
|
||||
|
||||
/** Field description */
|
||||
static final int GRACEFUL_TIMEOUT = 2000;
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -106,14 +109,17 @@ public class ScmServer extends Thread
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Stop embedded webserver. Use {@link Server#stop()} to fix windows service.
|
||||
*
|
||||
* @see <a href="http://goo.gl/Zfy0Ev">http://goo.gl/Zfy0Ev</a>
|
||||
*/
|
||||
public void stopServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
server.setStopTimeout(GRACEFUL_TIMEOUT);
|
||||
server.setStopAtShutdown(true);
|
||||
server.stop();
|
||||
initialized = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -86,7 +86,7 @@ public class ScmServerDaemon implements Daemon
|
||||
public static void stop(String[] args) throws Exception
|
||||
{
|
||||
webserver.stopServer();
|
||||
webserver.join(2000l);
|
||||
webserver.join((long) ScmServer.GRACEFUL_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +113,8 @@ public class ScmServerDaemon implements Daemon
|
||||
public void init(DaemonContext context) throws DaemonInitException, Exception
|
||||
{
|
||||
daemonArgs = context.getArguments();
|
||||
// initialize web server and open port. We have to do this in the init
|
||||
|
||||
// initialize web server and open port. We have to do this in the init
|
||||
// method, because this method is started by jsvc with super user privileges.
|
||||
webserver.init();
|
||||
}
|
||||
|
||||
@@ -490,10 +490,8 @@
|
||||
<environment.profile>default</environment.profile>
|
||||
<selenium.version>2.28.0</selenium.version>
|
||||
<enunciate.version>1.28</enunciate.version>
|
||||
<aether.version>1.13.1</aether.version>
|
||||
<wagon.version>1.0</wagon.version>
|
||||
<maven.version>3.0.5</maven.version>
|
||||
<mustache.version>0.8.15</mustache.version>
|
||||
<mustache.version>0.8.17</mustache.version>
|
||||
<netbeans.hint.deploy.server>Tomcat</netbeans.hint.deploy.server>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
|
||||
package sonia.scm;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.plugin.Extension;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
/**
|
||||
* Dispatcher for {@link HttpSessionEvent}. The {@link HttpSessionListenerHolder}
|
||||
* loads all registered {@link HttpSessionListener}s from the {@link Injector}
|
||||
* and delegates the events to the them. {@link HttpSessionListener} can be
|
||||
* registered with the {@link Extension} annotation.
|
||||
*
|
||||
* @author Sebastian Sdorra <sebastian.sdorra@gmail.com>
|
||||
* @since 1.42
|
||||
*/
|
||||
public class HttpSessionListenerHolder implements HttpSessionListener
|
||||
{
|
||||
|
||||
/** key type of the session listeners */
|
||||
private static final Key<Set<HttpSessionListener>> KEY =
|
||||
Key.get(new TypeLiteral<Set<HttpSessionListener>>() {}
|
||||
);
|
||||
|
||||
/** logger for HttpSessionListenerHolder */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(HttpSessionListenerHolder.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a new HttpSessionListenerHolder.
|
||||
*
|
||||
*/
|
||||
public HttpSessionListenerHolder()
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("create instance of {}",
|
||||
HttpSessionListenerHolder.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Delegates the create session event to all registered
|
||||
* {@ĺink HttpSessionListener}s.
|
||||
*
|
||||
*
|
||||
* @param event session event
|
||||
*/
|
||||
@Override
|
||||
public void sessionCreated(HttpSessionEvent event)
|
||||
{
|
||||
if (listenerSet == null)
|
||||
{
|
||||
listenerSet = loadListeners(event);
|
||||
}
|
||||
|
||||
dispatch(event, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates the destroy session event to all registered
|
||||
* {@ĺink HttpSessionListener}s.
|
||||
*
|
||||
*
|
||||
* @param event session event
|
||||
*/
|
||||
@Override
|
||||
public void sessionDestroyed(HttpSessionEvent event)
|
||||
{
|
||||
dispatch(event, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch session events.
|
||||
*
|
||||
*
|
||||
* @param event session event
|
||||
* @param create {@code true} if the event is a create event
|
||||
*/
|
||||
private void dispatch(HttpSessionEvent event, boolean create)
|
||||
{
|
||||
if (listenerSet != null)
|
||||
{
|
||||
for (HttpSessionListener listener : listenerSet)
|
||||
{
|
||||
if (create)
|
||||
{
|
||||
listener.sessionCreated(event);
|
||||
}
|
||||
else
|
||||
{
|
||||
listener.sessionDestroyed(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn(
|
||||
"could not dispatch session event, because holder is not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load listeners from {@link Injector} which is stored in the
|
||||
* {@link ServletContext}.
|
||||
*
|
||||
*
|
||||
* @param event session event
|
||||
*
|
||||
* @return set of session listeners
|
||||
*/
|
||||
private synchronized Set<HttpSessionListener> loadListeners(
|
||||
HttpSessionEvent event)
|
||||
{
|
||||
Set<HttpSessionListener> listeners = null;
|
||||
HttpSession session = event.getSession();
|
||||
|
||||
if (session != null)
|
||||
{
|
||||
Injector injector = (Injector) session.getServletContext().getAttribute(
|
||||
Injector.class.getName());
|
||||
|
||||
if (injector != null)
|
||||
{
|
||||
logger.debug("load HttpSessionListeners from injector");
|
||||
listeners = injector.getInstance(KEY);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.error("could not find injector in servletContext");
|
||||
}
|
||||
|
||||
if (listeners == null)
|
||||
{
|
||||
listeners = Sets.newHashSet();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warn("received session event without session");
|
||||
}
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** listener set */
|
||||
private Set<HttpSessionListener> listenerSet;
|
||||
}
|
||||
@@ -37,6 +37,8 @@ import com.google.inject.Inject;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
|
||||
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
|
||||
|
||||
import sonia.scm.security.KeyGenerator;
|
||||
import sonia.scm.security.Role;
|
||||
|
||||
@@ -54,6 +56,7 @@ import javax.ws.rs.core.MediaType;
|
||||
* @since 1.41
|
||||
*/
|
||||
@Path("security/key")
|
||||
@ExternallyManagedLifecycle
|
||||
public class KeyResource
|
||||
{
|
||||
|
||||
@@ -72,7 +75,7 @@ public class KeyResource
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generates a unique key. This method can only executed with administration
|
||||
* Generates a unique key. This method can only executed with administration
|
||||
* privileges.<br />
|
||||
* <br />
|
||||
* <ul>
|
||||
@@ -93,6 +96,6 @@ public class KeyResource
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
/** key generator */
|
||||
private final KeyGenerator keyGenerator;
|
||||
}
|
||||
|
||||
@@ -35,8 +35,13 @@ package sonia.scm.api.rest.resources;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
|
||||
import org.codehaus.enunciate.jaxrs.TypeHint;
|
||||
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
|
||||
@@ -46,31 +51,65 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.NotSupportedFeatuerException;
|
||||
import sonia.scm.Type;
|
||||
import sonia.scm.api.rest.RestActionUploadResult;
|
||||
import sonia.scm.repository.AdvancedImportHandler;
|
||||
import sonia.scm.repository.ImportHandler;
|
||||
import sonia.scm.repository.ImportResult;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryAllreadyExistExeption;
|
||||
import sonia.scm.repository.RepositoryException;
|
||||
import sonia.scm.repository.RepositoryHandler;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.util.SecurityUtil;
|
||||
import sonia.scm.repository.RepositoryType;
|
||||
import sonia.scm.repository.api.Command;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.repository.api.UnbundleCommandBuilder;
|
||||
import sonia.scm.security.Role;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||
import com.sun.jersey.multipart.FormDataParam;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
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.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.GenericEntity;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* Rest resource for importing repositories.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Singleton
|
||||
@Path("import/repositories")
|
||||
@ExternallyManagedLifecycle
|
||||
public class RepositoryImportResource
|
||||
@@ -85,94 +124,525 @@ public class RepositoryImportResource
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
* Constructs a new repository import resource.
|
||||
*
|
||||
*
|
||||
* @param manager
|
||||
* @param securityContextProvider
|
||||
* @param manager repository manager
|
||||
* @param serviceFactory
|
||||
*/
|
||||
@Inject
|
||||
public RepositoryImportResource(RepositoryManager manager)
|
||||
public RepositoryImportResource(RepositoryManager manager,
|
||||
RepositoryServiceFactory serviceFactory)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.serviceFactory = serviceFactory;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Imports a repository type specific bundle. The bundle file is uploaded to
|
||||
* the server which is running scm-manager. After the upload has finished, the
|
||||
* bundle file is passed to the {@link UnbundleCommandBuilder}. This method
|
||||
* requires admin privileges.<br />
|
||||
*
|
||||
* Status codes:
|
||||
* <ul>
|
||||
* <li>201 created</li>
|
||||
* <li>400 bad request, the import bundle feature is not supported by this
|
||||
* type of repositories or the parameters are not valid.</li>
|
||||
* <li>500 internal server error</li>
|
||||
* <li>409 conflict, a repository with the name already exists.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param uriInfo uri info
|
||||
* @param type repository type
|
||||
* @param name name of the repository
|
||||
* @param inputStream input bundle
|
||||
* @param compressed true if the bundle is gzip compressed
|
||||
*
|
||||
* @return empty response with location header which points to the imported
|
||||
* repository
|
||||
* @since 1.43
|
||||
*/
|
||||
@POST
|
||||
@Path("{type}/bundle")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
public Response importFromBundle(@Context UriInfo uriInfo,
|
||||
@PathParam("type") String type, @FormDataParam("name") String name,
|
||||
@FormDataParam("bundle") InputStream inputStream, @QueryParam("compressed")
|
||||
@DefaultValue("false") boolean compressed)
|
||||
{
|
||||
Repository repository = doImportFromBundle(type, name, inputStream,
|
||||
compressed);
|
||||
|
||||
return buildResponse(uriInfo, repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method works exactly like
|
||||
* {@link #importFromBundle(UriInfo, String, String, InputStream)}, but this
|
||||
* method returns an html content-type. The method exists only for a
|
||||
* workaround of the javascript ui extjs. This method requires admin
|
||||
* privileges.<br />
|
||||
*
|
||||
* Status codes:
|
||||
* <ul>
|
||||
* <li>201 created</li>
|
||||
* <li>400 bad request, the import bundle feature is not supported by this
|
||||
* type of repositories or the parameters are not valid.</li>
|
||||
* <li>500 internal server error</li>
|
||||
* <li>409 conflict, a repository with the name already exists.</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
* @param type repository type
|
||||
* @param name name of the repository
|
||||
* @param inputStream input bundle
|
||||
* @param compressed true if the bundle is gzip compressed
|
||||
*
|
||||
* @return
|
||||
* @return empty response with location header which points to the imported
|
||||
* repository
|
||||
* @since 1.43
|
||||
*/
|
||||
@POST
|
||||
@Path("{type}/bundle.html")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
public Response importFromBundleUI(@PathParam("type") String type,
|
||||
@FormDataParam("name") String name,
|
||||
@FormDataParam("bundle") InputStream inputStream, @QueryParam("compressed")
|
||||
@DefaultValue("false") boolean compressed)
|
||||
{
|
||||
Response response;
|
||||
|
||||
try
|
||||
{
|
||||
doImportFromBundle(type, name, inputStream, compressed);
|
||||
response = Response.ok(new RestActionUploadResult(true)).build();
|
||||
}
|
||||
catch (WebApplicationException ex)
|
||||
{
|
||||
logger.warn("error durring bundle import", ex);
|
||||
response = Response.fromResponse(ex.getResponse()).entity(
|
||||
new RestActionUploadResult(false)).build();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a external repository which is accessible via url. The method can
|
||||
* only be used, if the repository type supports the {@link Command#PULL}. The
|
||||
* method will return a location header with the url to the imported
|
||||
* repository. This method requires admin privileges.<br />
|
||||
*
|
||||
* Status codes:
|
||||
* <ul>
|
||||
* <li>201 created</li>
|
||||
* <li>400 bad request, the import by url feature is not supported by this
|
||||
* type of repositories or the parameters are not valid.</li>
|
||||
* <li>409 conflict, a repository with the name already exists.</li>
|
||||
* <li>500 internal server error</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param uriInfo uri info
|
||||
* @param type repository type
|
||||
* @param request request object
|
||||
*
|
||||
* @return empty response with location header which points to the imported
|
||||
* repository
|
||||
* @since 1.43
|
||||
*/
|
||||
@POST
|
||||
@Path("{type}/url")
|
||||
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||
public Response importFromUrl(@Context UriInfo uriInfo,
|
||||
@PathParam("type") String type, UrlImportRequest request)
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
checkNotNull(request, "request is required");
|
||||
checkArgument(!Strings.isNullOrEmpty(request.getName()),
|
||||
"request does not contain name of the repository");
|
||||
checkArgument(!Strings.isNullOrEmpty(request.getUrl()),
|
||||
"request does not contain url of the remote repository");
|
||||
|
||||
Type t = type(type);
|
||||
|
||||
checkSupport(t, Command.PULL, request);
|
||||
|
||||
logger.info("start {} import for external url {}", type, request.getUrl());
|
||||
|
||||
Repository repository = create(type, request.getName());
|
||||
RepositoryService service = null;
|
||||
|
||||
try
|
||||
{
|
||||
service = serviceFactory.create(repository);
|
||||
service.getPullCommand().pull(request.getUrl());
|
||||
}
|
||||
catch (RepositoryException ex)
|
||||
{
|
||||
handleImportFailure(ex, repository);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
handleImportFailure(ex, repository);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(service);
|
||||
}
|
||||
|
||||
return buildResponse(uriInfo, repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports repositories of the given type from the configured repository
|
||||
* directory. This method requires admin privileges.<br />
|
||||
* <br />
|
||||
* Status codes:
|
||||
* <ul>
|
||||
* <li>200 ok, successful</li>
|
||||
* <li>400 bad request, the import feature is not
|
||||
* supported by this type of repositories.</li>
|
||||
* <li>500 internal server error</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param type repository type
|
||||
*
|
||||
* @return imported repositories
|
||||
*/
|
||||
@POST
|
||||
@Path("{type}")
|
||||
@TypeHint(Repository[].class)
|
||||
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||
public GenericEntity<List<Repository>> importRepositories(
|
||||
@PathParam("type") String type)
|
||||
public Response importRepositories(@PathParam("type") String type)
|
||||
{
|
||||
SecurityUtil.assertIsAdmin();
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
|
||||
List<Repository> repositories = new ArrayList<Repository>();
|
||||
|
||||
importFromDirectory(repositories, type);
|
||||
|
||||
//J-
|
||||
return Response.ok(
|
||||
new GenericEntity<List<Repository>>(repositories) {}
|
||||
).build();
|
||||
//J+
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports repositories of all supported types from the configured repository
|
||||
* directories. This method requires admin privileges.<br />
|
||||
* <br />
|
||||
* Status codes:
|
||||
* <ul>
|
||||
* <li>200 ok, successful</li>
|
||||
* <li>400 bad request, the import feature is not
|
||||
* supported by this type of repositories.</li>
|
||||
* <li>500 internal server error</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return imported repositories
|
||||
*/
|
||||
@POST
|
||||
@TypeHint(Repository[].class)
|
||||
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||
public Response importRepositories()
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
|
||||
logger.info("start directory import for all supported repository types");
|
||||
|
||||
List<Repository> repositories = new ArrayList<Repository>();
|
||||
|
||||
for (Type t : findImportableTypes())
|
||||
{
|
||||
importFromDirectory(repositories, t.getName());
|
||||
}
|
||||
|
||||
//J-
|
||||
return Response.ok(
|
||||
new GenericEntity<List<Repository>>(repositories) {}
|
||||
).build();
|
||||
//J+
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports repositories of the given type from the configured repository
|
||||
* directory. Returns a list of successfully imported directories and a list
|
||||
* of failed directories. This method requires admin privileges.<br />
|
||||
* <br />
|
||||
* Status codes:
|
||||
* <ul>
|
||||
* <li>200 ok, successful</li>
|
||||
* <li>400 bad request, the import feature is not
|
||||
* supported by this type of repositories.</li>
|
||||
* <li>500 internal server error</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param type repository type
|
||||
*
|
||||
* @return imported repositories
|
||||
* @since 1.43
|
||||
*/
|
||||
@POST
|
||||
@Path("{type}/directory")
|
||||
@TypeHint(ImportResult.class)
|
||||
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||
public Response importRepositoriesFromDirectory(
|
||||
@PathParam("type") String type)
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
|
||||
Response response;
|
||||
|
||||
RepositoryHandler handler = manager.getHandler(type);
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
logger.info("start directory import for repository type {}", type);
|
||||
|
||||
try
|
||||
{
|
||||
List<String> repositoryNames =
|
||||
handler.getImportHandler().importRepositories(manager);
|
||||
ImportResult result;
|
||||
ImportHandler importHandler = handler.getImportHandler();
|
||||
|
||||
if (repositoryNames != null)
|
||||
if (importHandler instanceof AdvancedImportHandler)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
logger.debug("start directory import, using advanced import handler");
|
||||
result =
|
||||
((AdvancedImportHandler) importHandler)
|
||||
.importRepositoriesFromDirectory(manager);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("start directory import, using normal import handler");
|
||||
result = new ImportResult(importHandler.importRepositories(manager),
|
||||
ImmutableList.<String>of());
|
||||
}
|
||||
|
||||
response = Response.ok(result).build();
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (NotSupportedFeatuerException ex)
|
||||
{
|
||||
throw new WebApplicationException(ex);
|
||||
logger
|
||||
.warn(
|
||||
"import feature is not supported by repository handler for type "
|
||||
.concat(type), ex);
|
||||
response = Response.status(Status.BAD_REQUEST).build();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.warn("exception occured durring directory import", ex);
|
||||
response = Response.serverError().build();
|
||||
}
|
||||
catch (RepositoryException ex)
|
||||
{
|
||||
logger.warn("exception occured durring directory import", ex);
|
||||
response = Response.serverError().build();
|
||||
}
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
else
|
||||
{
|
||||
logger.warn("could not find handler for type {}", type);
|
||||
logger.warn("could not find reposiotry handler for type {}", type);
|
||||
response = Response.status(Status.BAD_REQUEST).build();
|
||||
}
|
||||
|
||||
return new GenericEntity<List<Repository>>(repositories) {}
|
||||
;
|
||||
return response;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a list of repository types, which support the directory import
|
||||
* feature.
|
||||
*
|
||||
* This method requires admin privileges.<br />
|
||||
* <br />
|
||||
* Status codes:
|
||||
* <ul>
|
||||
* <li>200 ok, successful</li>
|
||||
* <li>400 bad request, the import feature is not
|
||||
* supported by this type of repositories.</li>
|
||||
* <li>500 internal server error</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return list of repository types
|
||||
*/
|
||||
@GET
|
||||
@TypeHint(Type[].class)
|
||||
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||
public Response getImportableTypes()
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
|
||||
List<Type> types = findImportableTypes();
|
||||
|
||||
//J-
|
||||
return Response.ok(
|
||||
new GenericEntity<List<Type>>(types) {}
|
||||
).build();
|
||||
//J+
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Build rest response for repository.
|
||||
*
|
||||
*
|
||||
* @param uriInfo uri info
|
||||
* @param repository imported repository
|
||||
*
|
||||
* @return rest response
|
||||
*/
|
||||
private Response buildResponse(UriInfo uriInfo, Repository repository)
|
||||
{
|
||||
URI location = uriInfo.getBaseUriBuilder().path(
|
||||
RepositoryResource.class).path(repository.getId()).build();
|
||||
|
||||
return Response.created(location).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check repository type for support for the given command.
|
||||
*
|
||||
*
|
||||
* @param type repository type
|
||||
* @param cmd command
|
||||
* @param request request object
|
||||
*/
|
||||
private void checkSupport(Type type, Command cmd, Object request)
|
||||
{
|
||||
if (!(type instanceof RepositoryType))
|
||||
{
|
||||
logger.warn("type {} is not a repository type", type.getName());
|
||||
|
||||
throw new WebApplicationException(Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
Set<Command> cmds = ((RepositoryType) type).getSupportedCommands();
|
||||
|
||||
if (!cmds.contains(cmd))
|
||||
{
|
||||
logger.warn("type {} does not support this type of import: {}",
|
||||
type.getName(), request);
|
||||
|
||||
throw new WebApplicationException(Response.Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new repository with the given name and type.
|
||||
*
|
||||
*
|
||||
* @param type repository type
|
||||
* @param name repository name
|
||||
*
|
||||
* @return newly created repository
|
||||
*/
|
||||
private Repository create(String type, String name)
|
||||
{
|
||||
Repository repository = null;
|
||||
|
||||
try
|
||||
{
|
||||
repository = new Repository(null, type, name);
|
||||
manager.create(repository);
|
||||
}
|
||||
catch (RepositoryAllreadyExistExeption ex)
|
||||
{
|
||||
logger.warn("a {} repository with the name {} already exists", ex);
|
||||
|
||||
throw new WebApplicationException(Response.Status.CONFLICT);
|
||||
}
|
||||
catch (RepositoryException ex)
|
||||
{
|
||||
handleGenericCreationFailure(ex, type, name);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
handleGenericCreationFailure(ex, type, name);
|
||||
}
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start bundle import.
|
||||
*
|
||||
*
|
||||
* @param type repository type
|
||||
* @param name name of the repository
|
||||
* @param inputStream bundle stream
|
||||
* @param compressed true if the bundle is gzip compressed
|
||||
*
|
||||
* @return imported repository
|
||||
*/
|
||||
private Repository doImportFromBundle(String type, String name,
|
||||
InputStream inputStream, boolean compressed)
|
||||
{
|
||||
SecurityUtils.getSubject().checkRole(Role.ADMIN);
|
||||
|
||||
checkArgument(!Strings.isNullOrEmpty(name),
|
||||
"request does not contain name of the repository");
|
||||
checkNotNull(inputStream, "bundle inputStream is required");
|
||||
|
||||
Repository repository;
|
||||
|
||||
try
|
||||
{
|
||||
Type t = type(type);
|
||||
|
||||
checkSupport(t, Command.UNBUNDLE, "bundle");
|
||||
|
||||
repository = create(type, name);
|
||||
|
||||
RepositoryService service = null;
|
||||
|
||||
File file = File.createTempFile("scm-import-", ".bundle");
|
||||
|
||||
try
|
||||
{
|
||||
long length = Files.asByteSink(file).writeFrom(inputStream);
|
||||
|
||||
logger.info("copied {} bytes to temp, start bundle import", length);
|
||||
service = serviceFactory.create(repository);
|
||||
service.getUnbundleCommand().setCompressed(compressed).unbundle(file);
|
||||
}
|
||||
catch (RepositoryException ex)
|
||||
{
|
||||
handleImportFailure(ex, repository);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
handleImportFailure(ex, repository);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtil.close(service);
|
||||
IOUtil.delete(file);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.warn("could not create temporary file", ex);
|
||||
|
||||
throw new WebApplicationException(ex);
|
||||
}
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@GET
|
||||
@TypeHint(Type[].class)
|
||||
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
|
||||
public GenericEntity<List<Type>> getImportableTypes()
|
||||
private List<Type> findImportableTypes()
|
||||
{
|
||||
SecurityUtil.assertIsAdmin();
|
||||
|
||||
List<Type> types = new ArrayList<Type>();
|
||||
Collection<Type> handlerTypes = manager.getTypes();
|
||||
|
||||
@@ -208,12 +678,217 @@ public class RepositoryImportResource
|
||||
}
|
||||
}
|
||||
|
||||
return new GenericEntity<List<Type>>(types) {}
|
||||
;
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle creation failures.
|
||||
*
|
||||
*
|
||||
* @param ex exception
|
||||
* @param type repository type
|
||||
* @param name name of the repository
|
||||
*/
|
||||
private void handleGenericCreationFailure(Exception ex, String type,
|
||||
String name)
|
||||
{
|
||||
logger.error(String.format("could not create repository {} with type {}",
|
||||
type, name), ex);
|
||||
|
||||
throw new WebApplicationException(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle import failures.
|
||||
*
|
||||
*
|
||||
* @param ex exception
|
||||
* @param repository repository
|
||||
*/
|
||||
private void handleImportFailure(Exception ex, Repository repository)
|
||||
{
|
||||
logger.error("import for repository failed, delete repository", ex);
|
||||
|
||||
try
|
||||
{
|
||||
manager.delete(repository);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.error("can not delete repository", e);
|
||||
}
|
||||
catch (RepositoryException e)
|
||||
{
|
||||
logger.error("can not delete repository", e);
|
||||
}
|
||||
|
||||
throw new WebApplicationException(ex,
|
||||
Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import repositories from a specific type.
|
||||
*
|
||||
*
|
||||
* @param repositories repository list
|
||||
* @param type type of repository
|
||||
*/
|
||||
private void importFromDirectory(List<Repository> repositories, String type)
|
||||
{
|
||||
RepositoryHandler handler = manager.getHandler(type);
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
logger.info("start directory import for repository type {}", type);
|
||||
|
||||
try
|
||||
{
|
||||
List<String> 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 (NotSupportedFeatuerException ex)
|
||||
{
|
||||
throw new WebApplicationException(ex, Response.Status.BAD_REQUEST);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new WebApplicationException(ex);
|
||||
}
|
||||
catch (RepositoryException ex)
|
||||
{
|
||||
throw new WebApplicationException(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WebApplicationException(Response.Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Type type(String type)
|
||||
{
|
||||
RepositoryHandler handler = manager.getHandler(type);
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
logger.warn("no handler for type {} found", type);
|
||||
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
return handler.getType();
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Request for importing external repositories which are accessible via url.
|
||||
*/
|
||||
@XmlRootElement(name = "import")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class UrlImportRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
public UrlImportRequest() {}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link UrlImportRequest}
|
||||
*
|
||||
*
|
||||
* @param name name of the repository
|
||||
* @param url external url of the repository
|
||||
*/
|
||||
public UrlImportRequest(String name, String url)
|
||||
{
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
//J-
|
||||
return Objects.toStringHelper(this)
|
||||
.add("name", name)
|
||||
.add("url", url)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
|
||||
//~--- get methods --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns name of the repository.
|
||||
*
|
||||
*
|
||||
* @return name of the repository
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns external url of the repository.
|
||||
*
|
||||
*
|
||||
* @return external url of the repository
|
||||
*/
|
||||
public String getUrl()
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** name of the repository */
|
||||
private String name;
|
||||
|
||||
/** external url of the repository */
|
||||
private String url;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private RepositoryManager manager;
|
||||
/** repository manager */
|
||||
private final RepositoryManager manager;
|
||||
|
||||
/** repository service factory */
|
||||
private final RepositoryServiceFactory serviceFactory;
|
||||
}
|
||||
|
||||
@@ -99,6 +99,10 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
<<<<<<< mine
|
||||
=======
|
||||
*
|
||||
>>>>>>> theirs
|
||||
* @param userDAO
|
||||
*/
|
||||
@Inject
|
||||
@@ -227,8 +231,8 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
public void init(SCMContextProvider context)
|
||||
{
|
||||
|
||||
// TODO improve
|
||||
if (!userDAO.contains("scmadmin") &&!userDAO.contains("anonymous"))
|
||||
// create default account only, if no other account is available
|
||||
if (userDAO.getAll().isEmpty())
|
||||
{
|
||||
createDefaultAccounts();
|
||||
}
|
||||
@@ -519,6 +523,8 @@ public class DefaultUserManager extends AbstractUserManager
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.info("create default accounts");
|
||||
|
||||
JAXBContext context = JAXBContext.newInstance(User.class);
|
||||
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
<listener>
|
||||
<listener-class>sonia.scm.boot.BootstrapContextListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<listener>
|
||||
<listener-class>sonia.scm.HttpSessionListenerHolder</listener-class>
|
||||
</listener>
|
||||
|
||||
<filter>
|
||||
<filter-name>guiceFilter</filter-name>
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
<!-- sonia.util -->
|
||||
<script type="text/javascript" src="resources/js/util/sonia.util.js"></script>
|
||||
<script type="text/javascript" src="resources/js/util/sonia.util.link.js"></script>
|
||||
<script type="text/javascript" src="resources/js/util/sonia.util.tip.js"></script>
|
||||
|
||||
<!-- sonia.override -->
|
||||
<script type="text/javascript" src="resources/js/override/ext.form.vtypes.js"></script>
|
||||
|
||||
@@ -105,6 +105,11 @@ a.scm-link:hover {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.scm-form-fileupload-help-button {
|
||||
position: absolute;
|
||||
right: -19px;
|
||||
}
|
||||
|
||||
.scm-nav-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -238,3 +243,12 @@ div.noscript-container h1 {
|
||||
.unhealthy {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/** import **/
|
||||
.import-fu {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
.import-fu input {
|
||||
width: 215px;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ Ext.override(Ext.form.Field, {
|
||||
cls = 'scm-form-combo-help-button';
|
||||
}
|
||||
break;
|
||||
case 'fileuploadfield':
|
||||
cls = 'scm-form-fileupload-help-button';
|
||||
break;
|
||||
case 'textarea':
|
||||
cls = 'scm-form-textarea-help-button';
|
||||
break;
|
||||
|
||||
@@ -31,75 +31,517 @@
|
||||
|
||||
Sonia.repository.ImportWindow = Ext.extend(Ext.Window,{
|
||||
|
||||
titleText: 'Import Repositories',
|
||||
okText: 'Ok',
|
||||
closeText: 'Close',
|
||||
|
||||
// cache
|
||||
importForm: null,
|
||||
|
||||
imported: [],
|
||||
importJobsFinished: 0,
|
||||
importJobs: 0,
|
||||
title: 'Repository Import Wizard',
|
||||
|
||||
initComponent: function(){
|
||||
this.imported = [];
|
||||
this.importJobsFinished = 0;
|
||||
this.importJobs = 0;
|
||||
|
||||
this.addEvents('finish');
|
||||
|
||||
var config = {
|
||||
layout:'fit',
|
||||
width:300,
|
||||
height:170,
|
||||
title: this.title,
|
||||
layout: 'fit',
|
||||
width: 420,
|
||||
height: 190,
|
||||
closable: true,
|
||||
resizable: false,
|
||||
resizable: true,
|
||||
plain: true,
|
||||
border: false,
|
||||
modal: true,
|
||||
title: this.titleText,
|
||||
bodyCssClass: 'x-panel-mc',
|
||||
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
|
||||
id: 'scmRepositoryImportWizard',
|
||||
xtype: 'scmRepositoryImportWizard',
|
||||
listeners: {
|
||||
finish: {
|
||||
fn: this.onFinish,
|
||||
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');
|
||||
onFinish: function(config){
|
||||
this.fireEvent('finish', config);
|
||||
this.close();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Sonia.repository.ImportPanel = Ext.extend(Ext.Panel, {
|
||||
|
||||
// text
|
||||
backText: 'Back',
|
||||
nextText: 'Next',
|
||||
closeBtnText: 'Close',
|
||||
|
||||
imported: [],
|
||||
failed: [],
|
||||
|
||||
// help text
|
||||
importTypeDirectoryHelpText: 'Imports all repositories that are located at the repository folder of SCM-Manager.',
|
||||
importTypeURLHelpText: 'Imports a repository from a remote url.',
|
||||
importTypeFileHelpText: 'Imports a repository from a file (e.g. SVN dump).',
|
||||
|
||||
importUrlNameHelpText: 'The name of the repository in SCM-Manager.',
|
||||
importUrlHelpText: 'The source url of the repository.',
|
||||
|
||||
importFileNameHelpText: 'The name of the repository in SCM-Manager.',
|
||||
importFileHelpText: 'Choose the dump file you want to import to SCM-Manager.',
|
||||
importFileGZipCompressedHelpText: 'The file is gzip compressed.',
|
||||
|
||||
// tips
|
||||
tipRepositoryType: 'Choose your repository type for the import.',
|
||||
tipImportType: 'Select the type of import. <b>Note:</b> Not all repository types support all options.',
|
||||
|
||||
// cache
|
||||
nextButton: null,
|
||||
prevButton: null,
|
||||
|
||||
// settings
|
||||
repositoryType: null,
|
||||
|
||||
// active card
|
||||
activeForm: null,
|
||||
|
||||
// result template
|
||||
tpl: new Ext.XTemplate([
|
||||
'<p style="padding: 0 10px">',
|
||||
' <tpl if="imported">',
|
||||
' <b>Imported repositories</b><br />',
|
||||
' <tpl for="imported">',
|
||||
' - {.}<br />',
|
||||
' </tpl>',
|
||||
' <br />',
|
||||
' </tpl>',
|
||||
' <tpl if="failed">',
|
||||
' <b>Failed to import the following directories</b><br />',
|
||||
' <tpl for="failed">',
|
||||
' - {.}<br />',
|
||||
' </tpl>',
|
||||
' </tpl>',
|
||||
' <tpl if="isEmpty(imported, failed)">',
|
||||
' <b>No repositories to import</b>',
|
||||
' </tpl>',
|
||||
'</p>',
|
||||
'<br />'
|
||||
]),
|
||||
|
||||
initComponent: function(){
|
||||
this.addEvents('finish');
|
||||
|
||||
// fix initialization bug
|
||||
this.imported = [];
|
||||
this.failed = [];
|
||||
this.activeForm = null;
|
||||
|
||||
var typeItems = [];
|
||||
|
||||
Ext.each(state.repositoryTypes, function(repositoryType){
|
||||
typeItems.push({
|
||||
boxLabel: repositoryType.displayName,
|
||||
name: 'repositoryType',
|
||||
inputValue: repositoryType.name,
|
||||
checked: false
|
||||
});
|
||||
});
|
||||
|
||||
typeItems = typeItems.sort(function(a, b){
|
||||
return a.boxLabel > b.boxLabel;
|
||||
});
|
||||
|
||||
typeItems.push({
|
||||
xtype: 'scmTip',
|
||||
content: this.tipRepositoryType,
|
||||
width: '100%'
|
||||
});
|
||||
|
||||
var config = {
|
||||
layout: 'card',
|
||||
activeItem: 0,
|
||||
bodyStyle: 'padding: 5px',
|
||||
defaults: {
|
||||
bodyCssClass: 'x-panel-mc',
|
||||
border: false,
|
||||
labelWidth: 120,
|
||||
width: 250
|
||||
},
|
||||
bbar: ['->',{
|
||||
id: 'move-prev',
|
||||
text: this.backText,
|
||||
handler: this.navHandler.createDelegate(this, [-1]),
|
||||
disabled: true,
|
||||
scope: this
|
||||
},{
|
||||
id: 'move-next',
|
||||
text: this.nextText,
|
||||
handler: this.navHandler.createDelegate(this, [1]),
|
||||
disabled: true,
|
||||
scope: this
|
||||
},{
|
||||
id: 'closeBtn',
|
||||
text: this.closeBtnText,
|
||||
handler: this.applyChanges,
|
||||
disabled: true,
|
||||
scope: this
|
||||
}],
|
||||
items: [{
|
||||
id: 'repositoryTypeLayout',
|
||||
items: [{
|
||||
id: 'chooseRepositoryType',
|
||||
xtype: 'radiogroup',
|
||||
name: 'chooseRepositoryType',
|
||||
columns: 1,
|
||||
items: [typeItems],
|
||||
listeners: {
|
||||
change: {
|
||||
fn: function(){
|
||||
this.getNextButton().setDisabled(false);
|
||||
},
|
||||
scope: this
|
||||
}
|
||||
}
|
||||
}]
|
||||
},{
|
||||
id: 'importTypeLayout',
|
||||
items: [{
|
||||
id: 'chooseImportType',
|
||||
xtype: 'radiogroup',
|
||||
name: 'chooseImportType',
|
||||
columns: 1,
|
||||
items: [{
|
||||
id: 'importTypeDirectory',
|
||||
boxLabel: 'Import from directory',
|
||||
name: 'importType',
|
||||
inputValue: 'directory',
|
||||
disabled: false,
|
||||
helpText: this.importTypeDirectoryHelpText
|
||||
},{
|
||||
id: 'importTypeURL',
|
||||
boxLabel: 'Import from url',
|
||||
name: 'importType',
|
||||
inputValue: 'url',
|
||||
checked: false,
|
||||
disabled: true,
|
||||
helpText: this.importTypeURLHelpText
|
||||
},{
|
||||
id: 'importTypeFile',
|
||||
boxLabel: 'Import from file',
|
||||
name: 'importType',
|
||||
inputValue: 'file',
|
||||
checked: false,
|
||||
disabled: true,
|
||||
helpText: this.importTypeFileHelpText
|
||||
},{
|
||||
xtype: 'scmTip',
|
||||
content: this.tipImportType,
|
||||
width: '100%'
|
||||
}],
|
||||
listeners: {
|
||||
change: {
|
||||
fn: function(){
|
||||
this.getNextButton().setDisabled(false);
|
||||
},
|
||||
scope: this
|
||||
}
|
||||
}
|
||||
}]
|
||||
},{
|
||||
id: 'importUrlLayout',
|
||||
xtype: 'form',
|
||||
monitorValid: true,
|
||||
defaults: {
|
||||
width: 250
|
||||
},
|
||||
listeners: {
|
||||
clientvalidation: {
|
||||
fn: this.urlFormValidityMonitor,
|
||||
scope: this
|
||||
}
|
||||
},
|
||||
items: [{
|
||||
id: 'importUrlName',
|
||||
xtype: 'textfield',
|
||||
fieldLabel: 'Repository name',
|
||||
name: 'name',
|
||||
allowBlank: false,
|
||||
vtype: 'repositoryName',
|
||||
helpText: this.importUrlNameHelpText
|
||||
},{
|
||||
id: 'importUrl',
|
||||
xtype: 'textfield',
|
||||
fieldLabel: 'Import URL',
|
||||
name: 'url',
|
||||
allowBlank: false,
|
||||
vtype: 'url',
|
||||
helpText: this.importUrlHelpText
|
||||
},{
|
||||
xtype: 'scmTip',
|
||||
content: 'Please insert name and remote url of the repository.',
|
||||
width: '100%'
|
||||
}]
|
||||
},{
|
||||
id: 'importFileLayout',
|
||||
xtype: 'form',
|
||||
fileUpload: true,
|
||||
monitorValid: true,
|
||||
listeners: {
|
||||
clientvalidation: {
|
||||
fn: this.fileFormValidityMonitor,
|
||||
scope: this
|
||||
}
|
||||
},
|
||||
items: [{
|
||||
id: 'importFileName',
|
||||
xtype: 'textfield',
|
||||
fieldLabel: 'Repository name',
|
||||
name: 'name',
|
||||
type: 'textfield',
|
||||
width: 250,
|
||||
allowBlank: false,
|
||||
vtype: 'repositoryName',
|
||||
helpText: this.importFileNameHelpText
|
||||
},{
|
||||
id: 'importFile',
|
||||
xtype: 'fileuploadfield',
|
||||
fieldLabel: 'Import File',
|
||||
ctCls: 'import-fu',
|
||||
name: 'bundle',
|
||||
buttonText: '',
|
||||
allowBlank: false,
|
||||
helpText: this.importFileHelpText,
|
||||
cls: 'import-fu',
|
||||
buttonCfg: {
|
||||
iconCls: 'upload-icon'
|
||||
}
|
||||
},{
|
||||
id: 'importFileGZipCompressed',
|
||||
xtype: 'checkbox',
|
||||
fieldLabel: 'GZip compressed',
|
||||
helpText: this.importFileGZipCompressedHelpText
|
||||
},{
|
||||
xtype: 'scmTip',
|
||||
content: 'Please insert name and upload the repository file.',
|
||||
width: '100%'
|
||||
}]
|
||||
},{
|
||||
id: 'importFinishedLayout',
|
||||
layout: 'form',
|
||||
defaults: {
|
||||
width: 250
|
||||
},
|
||||
items: [{
|
||||
id: 'resultPanel',
|
||||
xtype: 'panel',
|
||||
bodyCssClass: 'x-panel-mc',
|
||||
tpl: this.tpl
|
||||
},{
|
||||
id: 'finalTip',
|
||||
xtype: 'scmTip',
|
||||
content: 'Please see log for details.',
|
||||
width: '100%',
|
||||
hidden: true
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
Ext.apply(this, Ext.apply(this.initialConfig, config));
|
||||
Sonia.repository.ImportPanel.superclass.initComponent.apply(this, arguments);
|
||||
},
|
||||
|
||||
navHandler: function(direction){
|
||||
this.activeForm = null;
|
||||
|
||||
var layout = this.getLayout();
|
||||
var id = layout.activeItem.id;
|
||||
|
||||
var next = -1;
|
||||
|
||||
if ( id === 'repositoryTypeLayout' && direction === 1 ){
|
||||
this.repositoryType = Ext.getCmp('chooseRepositoryType').getValue().getRawValue();
|
||||
this.enableAvailableImportTypes();
|
||||
next = 1;
|
||||
}
|
||||
else if ( id === 'importTypeLayout' && direction === -1 ){
|
||||
next = 0;
|
||||
Ext.getCmp('move-prev').setDisabled(true);
|
||||
Ext.getCmp('move-next').setDisabled(false);
|
||||
}
|
||||
else if ( id === 'importTypeLayout' && direction === 1 ){
|
||||
Ext.getCmp('move-next').setDisabled(false);
|
||||
var v = Ext.getCmp('chooseImportType').getValue();
|
||||
if ( v ){
|
||||
switch (v.getRawValue()){
|
||||
case 'directory':
|
||||
this.importFromDirectory(layout);
|
||||
break;
|
||||
case 'url':
|
||||
next = 2;
|
||||
this.activeForm = 'url';
|
||||
break;
|
||||
case 'file':
|
||||
next = 3;
|
||||
this.activeForm = 'file';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( (id === 'importUrlLayout' || id === 'importFileLayout') && direction === -1 )
|
||||
{
|
||||
next = 1;
|
||||
}
|
||||
else if ( id === 'importUrlLayout' && direction === 1 )
|
||||
{
|
||||
this.importFromUrl(layout, Ext.getCmp('importUrlLayout').getForm().getValues());
|
||||
}
|
||||
else if ( id === 'importFileLayout' && direction === 1 )
|
||||
{
|
||||
this.importFromFile(layout, Ext.getCmp('importFileLayout').getForm());
|
||||
}
|
||||
|
||||
if ( next >= 0 ){
|
||||
layout.setActiveItem(next);
|
||||
}
|
||||
},
|
||||
|
||||
getNextButton: function(){
|
||||
if (!this.nextButton){
|
||||
this.nextButton = Ext.getCmp('move-next');
|
||||
}
|
||||
return this.nextButton;
|
||||
},
|
||||
|
||||
getPrevButton: function(){
|
||||
if (!this.prevButton){
|
||||
this.prevButton = Ext.getCmp('move-prev');
|
||||
}
|
||||
return this.prevButton;
|
||||
},
|
||||
|
||||
showLoadingBox: function(){
|
||||
return Ext.MessageBox.show({
|
||||
title: 'Loading',
|
||||
msg: 'Import repository',
|
||||
width: 300,
|
||||
wait: true,
|
||||
animate: true,
|
||||
progress: true,
|
||||
closable: false
|
||||
});
|
||||
},
|
||||
|
||||
urlFormValidityMonitor: function(form, valid){
|
||||
if (this.activeForm === 'url'){
|
||||
this.formValidityMonitor(valid);
|
||||
}
|
||||
},
|
||||
|
||||
fileFormValidityMonitor: function(form, valid){
|
||||
if (this.activeForm === 'file'){
|
||||
this.formValidityMonitor(valid);
|
||||
}
|
||||
},
|
||||
|
||||
formValidityMonitor: function(valid){
|
||||
var nbt = this.getNextButton();
|
||||
if (valid && nbt.disabled){
|
||||
nbt.setDisabled(false);
|
||||
} else if (!valid && !nbt.disabled){
|
||||
nbt.setDisabled(true);
|
||||
}
|
||||
},
|
||||
|
||||
appendImportResult: function(layout, result){
|
||||
for (var i=0; i<result.importedDirectories.length; i++){
|
||||
this.imported.push(result.importedDirectories[i]);
|
||||
}
|
||||
for (var i=0; i<result.failedDirectories.length; i++){
|
||||
this.failed.push(result.failedDirectories[i]);
|
||||
}
|
||||
this.displayResult(layout);
|
||||
},
|
||||
|
||||
appendImported: function(layout, name){
|
||||
this.imported.push(name);
|
||||
this.displayResult(layout);
|
||||
},
|
||||
|
||||
appendFailed: function(layout, name){
|
||||
this.failed.push(name);
|
||||
this.displayResult(layout);
|
||||
layout.setActiveItem(4);
|
||||
},
|
||||
|
||||
displayResult: function(layout){
|
||||
this.getNextButton().setDisabled(true);
|
||||
this.getPrevButton().setDisabled(true);
|
||||
Ext.getCmp('closeBtn').setDisabled(false);
|
||||
|
||||
var model = {
|
||||
imported: this.imported.length > 0 ? this.imported : null,
|
||||
failed: this.failed.length > 0 ? this.failed : null,
|
||||
isEmpty: function(imported, failed){
|
||||
return !imported && !failed;
|
||||
}
|
||||
};
|
||||
var resultPanel = Ext.getCmp('resultPanel');
|
||||
Ext.getCmp('finalTip').setVisible(this.failed.length > 0);
|
||||
resultPanel.tpl.overwrite(resultPanel.body, model);
|
||||
layout.setActiveItem(4);
|
||||
},
|
||||
|
||||
importFromFile: function(layout, form){
|
||||
var compressed = Ext.getCmp('importFileGZipCompressed').getValue();
|
||||
var lbox = this.showLoadingBox();
|
||||
form.submit({
|
||||
url: restUrl + 'import/repositories/' + this.repositoryType + '/bundle.html?compressed=' + compressed,
|
||||
timeout: 300000, // 5min
|
||||
scope: this,
|
||||
success: function(form){
|
||||
lbox.hide();
|
||||
this.appendImported(layout, form.getValues().name);
|
||||
},
|
||||
failure: function(form){
|
||||
lbox.hide();
|
||||
this.appendFailed(layout, form.getValues().name);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
importFromUrl: function(layout, repository){
|
||||
var lbox = this.showLoadingBox();
|
||||
Ext.Ajax.request({
|
||||
url: restUrl + 'import/repositories.json',
|
||||
method: 'GET',
|
||||
url: restUrl + 'import/repositories/' + this.repositoryType + '/url.json',
|
||||
method: 'POST',
|
||||
scope: this,
|
||||
timeout: 300000, // 5min
|
||||
jsonData: repository,
|
||||
success: function(){
|
||||
lbox.hide();
|
||||
this.appendImported(layout, repository.name);
|
||||
},
|
||||
failure: function(){
|
||||
lbox.hide();
|
||||
this.appendFailed(layout, repository.name);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
importFromDirectory: function(layout){
|
||||
var lbox = this.showLoadingBox();
|
||||
Ext.Ajax.request({
|
||||
url: restUrl + 'import/repositories/' + this.repositoryType + '/directory.json',
|
||||
method: 'POST',
|
||||
scope: this,
|
||||
success: function(response){
|
||||
var obj = Ext.decode(response.responseText);
|
||||
this.renderTypeCheckboxes(obj);
|
||||
this.doLayout();
|
||||
lbox.hide();
|
||||
this.appendImportResult(layout, Ext.decode(response.responseText));
|
||||
},
|
||||
failure: function(result){
|
||||
lbox.hide();
|
||||
main.handleRestFailure(
|
||||
result,
|
||||
this.errorTitleText,
|
||||
@@ -107,123 +549,34 @@ Sonia.repository.ImportWindow = Ext.extend(Ext.Window,{
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
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;
|
||||
enableAvailableImportTypes: function(){
|
||||
var type = null;
|
||||
Ext.each(state.repositoryTypes, function(repositoryType){
|
||||
if (repositoryType.name === this.repositoryType){
|
||||
type = repositoryType;
|
||||
}
|
||||
} else {
|
||||
this.importJobs = 0;
|
||||
}
|
||||
Ext.each(values, function(value){
|
||||
this.importRepositoriesOfType(value);
|
||||
}, this);
|
||||
},
|
||||
|
||||
appendImported: function(repositories){
|
||||
for (var i=0; i<repositories.length; i++){
|
||||
this.imported.push(repositories[i]);
|
||||
}
|
||||
this.importJobsFinished++;
|
||||
if ( this.importJobsFinished >= this.importJobs ){
|
||||
if (debug){
|
||||
console.debug( 'import of ' + this.importJobsFinished + ' jobs finished' );
|
||||
}
|
||||
this.printImported();
|
||||
|
||||
if ( type !== null ){
|
||||
Ext.getCmp('chooseImportType').setValue(null);
|
||||
this.getNextButton().setDisabled(true);
|
||||
this.getPrevButton().setDisabled(false);
|
||||
Ext.getCmp('importTypeURL').setDisabled(type.supportedCommands.indexOf('PULL') < 0);
|
||||
Ext.getCmp('importTypeFile').setDisabled(type.supportedCommands.indexOf('UNBUNDLE') < 0);
|
||||
}
|
||||
},
|
||||
|
||||
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
|
||||
applyChanges: function(){
|
||||
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.handleRestFailure(
|
||||
result,
|
||||
this.errorTitleText,
|
||||
this.errorMsgText
|
||||
);
|
||||
}
|
||||
});
|
||||
this.fireEvent('finish');
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// register xtype
|
||||
Ext.reg('scmRepositoryImportWizard', Sonia.repository.ImportPanel);
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.util.Tip = Ext.extend(Ext.BoxComponent, {
|
||||
|
||||
tpl: new Ext.XTemplate('\
|
||||
<div id="" class="x-tip" style="position: inherit; visibility: visible;">\
|
||||
<div class="x-tip-tl">\
|
||||
<div class="x-tip-tr">\
|
||||
<div class="x-tip-tc">\
|
||||
<div class="x-tip-header x-unselectable">\
|
||||
<span class="x-tip-header-text"></span>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="x-tip-bwrap">\
|
||||
<div class="x-tip-ml">\
|
||||
<div class="x-tip-mr">\
|
||||
<div class="x-tip-mc">\
|
||||
<div class="x-tip-body" style="height: auto; width: auto;">\
|
||||
{content}\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="x-tip-bl x-panel-nofooter">\
|
||||
<div class="x-tip-br">\
|
||||
<div class="x-tip-bc"></div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>'),
|
||||
|
||||
constructor: function(config) {
|
||||
config = config || {};
|
||||
var cl = 'scm-tip';
|
||||
if (config['class']){
|
||||
cl += ' ' + config['class'];
|
||||
}
|
||||
config.xtype = 'box';
|
||||
this.html = this.tpl.apply({content: config.content});
|
||||
Sonia.util.Tip.superclass.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// register xtype
|
||||
Ext.reg('scmTip', Sonia.util.Tip);
|
||||
@@ -35,13 +35,22 @@ package sonia.scm.user;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import sonia.scm.store.JAXBStoreFactory;
|
||||
import sonia.scm.store.StoreFactory;
|
||||
import sonia.scm.user.xml.XmlUserDAO;
|
||||
import sonia.scm.util.MockUtil;
|
||||
|
||||
import org.junit.Before;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -49,11 +58,6 @@ import org.junit.Before;
|
||||
*/
|
||||
public class DefaultUserManagerTest extends UserManagerTestBase
|
||||
{
|
||||
|
||||
@Before
|
||||
public void setAdminSubject(){
|
||||
setSubject(MockUtil.createAdminSubject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
@@ -63,13 +67,72 @@ public class DefaultUserManagerTest extends UserManagerTestBase
|
||||
*/
|
||||
@Override
|
||||
public UserManager createManager()
|
||||
{
|
||||
return new DefaultUserManager(createXmlUserDAO());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testDefaultAccountAfterFristStart()
|
||||
{
|
||||
UserDAO userDAO = mock(UserDAO.class);
|
||||
List<User> users = Lists.newArrayList(new User("tuser"));
|
||||
|
||||
when(userDAO.getAll()).thenReturn(users);
|
||||
|
||||
UserManager userManager = new DefaultUserManager(userDAO);
|
||||
|
||||
userManager.init(contextProvider);
|
||||
verify(userDAO, never()).add(any(User.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDefaultAccountCreation()
|
||||
{
|
||||
UserDAO userDAO = mock(UserDAO.class);
|
||||
|
||||
when(userDAO.getAll()).thenReturn(Collections.EMPTY_LIST);
|
||||
|
||||
UserManager userManager = new DefaultUserManager(userDAO);
|
||||
|
||||
userManager.init(contextProvider);
|
||||
verify(userDAO, times(2)).add(any(User.class));
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
@Before
|
||||
public void setAdminSubject()
|
||||
{
|
||||
setSubject(MockUtil.createAdminSubject());
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private XmlUserDAO createXmlUserDAO()
|
||||
{
|
||||
StoreFactory factory = new JAXBStoreFactory();
|
||||
|
||||
factory.init(contextProvider);
|
||||
|
||||
XmlUserDAO userDAO = new XmlUserDAO(factory);
|
||||
|
||||
return new DefaultUserManager(userDAO);
|
||||
return new XmlUserDAO(factory);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user